Must CLANG be in System Path when using cmake and Visual Studio? - cmake

I want to use
Visual Studio 2022
CMakeLists.txt based project
LLVM and C++
To be able to have for future multiple Clang Versions in parallel on my HardDrive (e.g. Support of older projects), I do not want to add Clang to my system path.
If I run that setup in the command line it works without having to add CLANG to my system path. But it does not in Visual Studio
The current setup for Visual Studio works with a batch file adding the Clang Path to the current System Path and starting Visual Studio afterward.
Here is my current CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
set(PROJECT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../..")
set(CMAKE_PREFIX_PATH "${PROJECT_ROOT}/tools/compiler/LLVM/bin")
# Check if the compiler is available in the expected directory
find_path(COMPILER_DIR
NAMES clang.exe
)
# Check if the compiler was found
IF( IS_DIRECTORY "${COMPILER_DIR}")
# We want to choose the compiler used for this project
# This works only if the set is done before the project() call
set(CMAKE_C_COMPILER ${COMPILER_DIR}/clang.exe CACHE PATH "" FORCE)
set(CMAKE_CXX_COMPILER ${COMPILER_DIR}/clang++.exe CACHE PATH "" FORCE)
ELSE()
MESSAGE(STATUS "Compiler directory not found")
ENDIF()
project(runUnitTests)
# removed some lines ...
set(GTEST "${PROJECT_ROOT}/tools/googletest-main")
set(COMMON "${PROJECT_ROOT}/common")
# ----======================================================================----
add_definitions(-DINLINE=inline)
add_definitions(-DUNIT_TEST)
add_definitions(-DGTEST_USE_OWN_TR1_TUPLE)
# Prevent overriding the parent project's compiler/linker
# settings on Windows
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# ----======================================================================----
# GTest.
# ----======================================================================----
# enable CMake’s test runner to discover the tests included in the binary,
# using the GoogleTest CMake module.
include(GoogleTest)
# add the google test directory as a subdirectory
# by this it gets automatically build and the libraries are put into the "out" directory
add_subdirectory(${GTEST} out)
# Add include directories
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${GTEST}/googletest/include
${GTEST}/googlemock/include
# removed some lines ...
)
# ----======================================================================----
# Headers.
# ----======================================================================----
file(GLOB_RECURSE ${PROJECT_NAME}_HEADERS "*.h" "*.hpp")
# ----======================================================================----
# Sources.
# ----======================================================================----
# Main Sources
set(SOURCES
# removed some lines ...
./main_pc.cpp
)
# additional source files
set(ADDITIONAL_SOURCES
# Please add here the source files needed for your test
./sampleClass.cpp
)
# Test case source files
set(TEST_SOURCES
# Please add here your own test case files
./test_sampleClass.cpp
)
# ----======================================================================----
# Executable setup
# ----======================================================================----
# Test without matlab libraries
add_executable(${PROJECT_NAME} ${SOURCES} ${ADDITIONAL_SOURCES} ${TEST_SOURCES} ${${PROJECT_NAME}_HEADERS})
# add the gtest/gmock library to the linker
target_link_libraries(${PROJECT_NAME} gtest gmock)
# This is so you can do 'make test' to see all your tests run, instead of
# manually running the executable runUnitTests to see those specific tests.
add_test(NAME allTests COMMAND runUnitTests)
# Check if the teamcity envoirnment variable is set
# Because when running on teamcity the gtest_discover_tests call fails
# But this is needed that the Visual Studio Test Explorer can detect the test cases.
if(NOT DEFINED ENV{TEAMCITY_VERSION})
enable_testing()
# since CMake 3.10 it is possible to scan the test executable for test
# This is needed that Visual Studio Test Explorer is able to display the test cases
gtest_discover_tests(${PROJECT_NAME})
endif(NOT DEFINED ENV{TEAMCITY_VERSION})
and here is my CMakeSettings.json
{
"configurations": [
{
"name": "x86-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "clang_cl_x86" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
}
]
}
Error message in Visual Studio if Clang is not in Path:
1> CMakeSettings Error at CMakeSettings.json: Failed to load configuration 'x86-Debug'. Enable Cross Platform Logging for more information.
1> Failed to load configuration 'x86-Debug'. Enable Cross Platform Logging for more information.
1> Failed to load configuration 'x86-Debug'. Enable Cross Platform Logging for more information.
I enabled "Cross Platform Logging" but it is empty.
Tried also to set absolute paths in CMakeLists to CLANG. Did not change the situation.
When I run Visual Studio once with CLang in the Path and compile the project -> it works on the next start for Visual Studio that Clang is NOT in the PATH. When I delete the "out" folder, back to failing.

Related

How to use find_package after add_subdirectory

I am trying to import a third party package into my project. So I've been following:
https://cliutils.gitlab.io/modern-cmake/chapters/install/installing.html
But this fails with:
/tmp/top-level/bin/extern/MyLib
CMake Error at bin/extern/MyLib/MyLibConfig.cmake:12 (include):
include could not find load file:
/tmp/top-level/bin/extern/MyLib/MyLibTargets.cmake
Call Stack (most recent call first):
CMakeLists.txt:6 (find_package)
What am I missing from the documentation ? For reference, my top level cmakelists.txt is:
cmake_minimum_required(VERSION 3.18)
project(top-level)
add_subdirectory(extern)
find_package(MyLib CONFIG REQUIRED HINTS
${CMAKE_CURRENT_BINARY_DIR}/extern/MyLib)
And the cmakelists.txt file for 'MyLib' is:
cmake_minimum_required(VERSION 3.18)
project(MyLib VERSION 1.0 LANGUAGES C)
add_library(MyLib mylib.c)
add_library(MyLib::MyLib ALIAS MyLib)
install(
TARGETS MyLib
EXPORT MyLibTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES
DESTINATION include)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
MyLibConfigVersion.cmake
VERSION ${PACKAGE_VERSION}
COMPATIBILITY AnyNewerVersion)
install(
EXPORT MyLibTargets
FILE MyLibTargets.cmake
NAMESPACE MyLib::
DESTINATION lib/cmake/MyLib)
configure_file(MyLibConfig.cmake.in MyLibConfig.cmake #ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/MyLibConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/MyLibConfigVersion.cmake"
DESTINATION lib/cmake/MyLib)
The error message is self-explanatory:
You use script MyLibConfig.cmake from the build directory, and this script attempts to load the script MyLibTargets.cmake created by install(EXPORT MyLibTargets).
But the latter script is intended to work only after the project will be installed, it cannot work while the project is being built.
Actually, the whole call find_package(MyLib) is not needed in that situation:
since current project builds MyLib, the target MyLib::MyLib is already accessible for you.
If you want to make your top-level project to be flexible, so it would work both in cases MyLib is already installed or just being built, then you could use find_package conditionally:
cmake_minimum_required(VERSION 3.18)
project(top-level)
# This project could be built as standalone.
# In that case 'MyLib' is assumed to be already installed.
#
# Also, this project could work as a subproject of some other project,
# which also builds `MyLib` via 'add_subdirectory(MyLib)'.
if(NOT TARGET MyLib::MyLib)
find_package(MyLib CONFIG REQUIRED)
endif()
# ... use MyLib via 'MyLib::MyLib' target.
Alternatively, you may write MyLibConfig.cmake script in a manner, which allows it to be used even if MyLib is currently being built.
if(TARGET MyLib::MyLib)
return()
endif()
# ... usual content of the config file.
In that case, CMakeLists.txt for the root project could be simplified:
cmake_minimum_required(VERSION 3.18)
project(top-level)
# Normal use case is that 'MyLib' is already installed.
# But the project could work as a subproject in other scenarios.
#
# In those scenarios, a parent project should care about
# 'find_package' to work.
find_package(MyLib CONFIG REQUIRED)
# ... use MyLib via 'MyLib::MyLib' target.
The usage of the project in case of 'MyLib' being built could be as follows:
- CMakeLists.txt (outer)
- MyLib
- CMakeLists.txt (MyLib)
- top_level
- CMakeLists.txt ("top-level")
Outer CMakeLists.txt:
cmake_minimum_required(VERSION 3.18)
project(outer)
add_subdirectory(MyLib)
# Help inner project to find config file for MyLib.
#
# Here we use *internal* knowledge of MyLib project,
# that it generates 'MyLibConfig.cmake' directly in its build directory.
#
# Note: find_package expects 'XXX_DIR' variable to be CACHE one.
set(MyLib_DIR "${CMAKE_CURRENT_BINARY_DIR}/MyLib"
CACHE INTERNAL "Directory with MyLibConfig.cmake"
)
add_subdirectory(top_level)

Why my <package>-config.cmake have <package>_include_dir and <package>_librairies empty

I am trying to make a cross-platform CMake for my project (Windows and Linux).
I need to use external libraries (yaml-cpp). On Linux, I just had to do an apt get and use find_package. But on Windows, I need to append the CMAKE_MODULE_PATH in order for my program to find the yaml-cpp-config.cmake.
So I start by installing yaml-cpp (https://github.com/jbeder/yaml-cpp) with CMake GUI 3.16 and mingw32 (mingw32-make install).
I have tried the library on a hello world project, and it works fine.
cmake_minimum_required(VERSION 3.1)
project (yaml_test)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
message (STATUS "Yaml-cpp include = $ENV{YAML_CPP_INCLUDE_DIR}")
message (STATUS "Yaml-cpp library = $ENV{YAML_CPP_LIBRARIES}")
include_directories ($ENV{YAML_CPP_INCLUDE_DIR})
add_executable(yaml_test main.cpp)
target_link_libraries(yaml_test $ENV{YAML_CPP_LIBRARIES})
But now, I want to include the library in my project and use find_package. But the yaml-cpp-config.cmake looks like this:
# - Config file for the yaml-cpp package
# It defines the following variables
# YAML_CPP_INCLUDE_DIR - include directory
# YAML_CPP_LIBRARIES - libraries to link against
# Compute paths
get_filename_component(YAML_CPP_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
set(YAML_CPP_INCLUDE_DIR "")
# Our library dependencies (contains definitions for IMPORTED targets)
include("${YAML_CPP_CMAKE_DIR}/yaml-cpp-targets.cmake")
# These are IMPORTED targets created by yaml-cpp-targets.cmake
set(YAML_CPP_LIBRARIES "")
The YAML_CPP_INCLUDE_DIR and YAML_CPP_LIBRARIES variables are empty, and even if CMake found yaml-cpp-config.cmake, It doesn't work. So what do I have missing in the installation of yaml-cpp? Should I have set the paths by hand?
The absence of definition of YAML_CPP_INCLUDE_DIR and YAML_CPP_LIBRARIES variables is the issue with the yaml-cpp project which is already reported here.
Instead of variables described in this config file, use target yaml-cpp:
add_executable(yaml_test main.cpp)
# This provides both include directories and libraries.
target_link_libraries(yaml_test yaml-cpp)
Linking with an IMPORTED target (yaml-cpp in this case) is known as CMake "modern way".

Antlr4 with C++ and CMake

I'm trying to use antlr4 with C++. I have the following CMakeLists.txt in my root directory:
cmake_minimum_required(VERSION 3.10)
project(demo VERSION 0.1 DESCRIPTION "demo")
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(CMAKE_CXX_STANDARD 17)
#############################################################################
# ANTLR SPECIFIC CMAKE COMMANDS
# This is derived from: https://github.com/blorente/antlr-4.7-cpp-cmake-base
#############################################################################
# Set the ocation of the JAR.
set(ANTLR4CPP_JAR_LOCATION ${CMAKE_SOURCE_DIR}/antlr/jar/antlr.jar)
# Add external build for antlrcpp.
include (${CMAKE_SOURCE_DIR}/antlr/runtime/ExternalAntlr4Cpp.cmake)
# add antrl4cpp artifacts to project environment.
include_directories( ${ANTLR4CPP_INCLUDE_DIRS} )
link_directories( ${ANTLR4CPP_LIBS} )
# message(WARNING "Found antlr4cpp libs: ${ANTLR4CPP_LIBS} and includes: ${ANTLR4CPP_INCLUDE_DIRS} ")
# Build the lexer/parser .h/.cpp files off the g4 grammar files.
antlr4cpp_process_grammar(demo demoparser
${CMAKE_SOURCE_DIR}/grammar/DemoLexer.g4
${CMAKE_SOURCE_DIR}/grammar/DemoParser.g4)
# include the generated files from the grammar/lexer.
include_directories(${antlr4cpp_include_dirs_demoparser})
#############################################################################
# Build demo
add_executable(demo main.cpp ${antlr4cpp_src_files_demoparser})
# Add dependencies for antlr
add_dependencies(demo antlr4cpp antlr4cpp_generation_demoparser)
target_link_libraries(demo antlr4-runtime)
I have, more or less, copied the example from: the "official" cmake/antlr tutorial. For brevity I have omitted the contents of include (${CMAKE_SOURCE_DIR}/antlr/runtime/ExternalAntlr4Cpp.cmake)
So the files are all in the same structure, except the jar. I just include it as a project file and, as you can see from the set(ANTLR4CPP_JAR_LOCATION) command, it resides where I want it.
When I build this, I get this error:
dyld: Library not loaded: libantlr4-runtime.4.7.1.dylib
Referenced from: /path/to/demo/cmake-build-debug/demo
Reason: image not found
However, when I look in: /path/to/demo/cmake-build-debug/external/antlr4cpp/lib/ I see the libantlr4-runtime.4.7.1.dylib file that should be referenced by using the target_link_libraries(demo antlr4-runtime) command. There are static libraries in there as well.
Am I missing something? Why is CMake not finding the static or dylib files it's supposed to?
I didn't realize, right before I started this, that I ran brew install antlr4-cpp-runtime. Which installed the headers to the path. I did this on my machine, but not on any of the other machines that build this project. The outstanding problem is why is it not deterministic is if the runtime isn't installed. But it works immediately upon installing the runtime.

How do I detect that I am cross-compiling in CMakeLists.txt?

The CMake documentation suggests that CMAKE_CROSSCOMPILING is set when cross-compiling. In my CMakeLists.txt I have the lines:
IF(CMAKE_CROSSCOMPILING)
message(STATUS "Cross-compiling so skipping unit tests.")
option(GAME_PORTAL_UNIT_TEST "Enable unit testing of Game Portal code" OFF)
ELSE()
message(STATUS "Enabling unit testing of Game Portal code")
option(GAME_PORTAL_UNIT_TEST "Enable unit testing of Game Portal code" ON)
ENDIF()
The output from running:
cmake -DCMAKE_TOOLCHAIN_FILE=../crosscompile/raspberry_pi/CMakeCross.txt .
Includes the text "Enabling unit testing of Game Portal code", so clearly this variable is not being set, or not so it evaluates to true anyway.
I tried modifying CMakeCross.txt to include:
set(CMAKE_CROSSCOMPILING ON CACHE BOOL "Cross-compiling" FORCE)
and after cleaning the old CMakeCache.txt and rerunning my cmake command I can see that the new CMakeCache.txt now includes this variable, but I still get the same result as previously with regards to the unit tests being enabled.
How can I reliably detect that I am cross-compiling so I can properly disable the unit tests?
As requested, the full cross-compile file is:
# Set minimum cmake version required for cross-compiling to work.
cmake_minimum_required(VERSION 2.6)
# Build with rm CMakeCache.txt; cmake -DCMAKE_TOOLCHAIN_FILE=/home/crosscompile/dev/raspberry_pi/CMakeCross.txt ..
# Set target system name.
SET (CMAKE_SYSTEM_NAME Linux)
# Set compiler name.
SET (CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
SET (CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
# Set path(s) to search for libraries/binaries/headers.
SET (CMAKE_FIND_ROOT_PATH /home/crosscompile/dev/raspberry_pi/rootfs/)
# Ensure only cross-compiler directories are searched.
SET (ONLY_CMAKE_FIND_ROOT_PATH TRUE)
# search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# Set output/install directory to safe place.
SET (CMAKE_INSTALL_PREFIX /home/crosscompile/dev/raspberry_pi/install/)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rpath-link=/lib/arm-linux-gnueabihf")
set(THREADS_PTHREAD_ARG 0)
set(CMAKE_CROSSCOMPILING ON CACHE BOOL "Cross-compiling" FORCE)
The test for CMAKE_CROSSCOMPILING must come after the "project" instruction in CMakeLists.txt.
With in-source builds, one need to manually cleanup build files when change configuration parameters a lot.
E.g., if you did native build before, and then decide to cross-compile, you need to perform manual cleanup: CMake cannot automatically adjust build directory from one build type to another.
This is one of the reasons why in-source builds are not recommended and should be replaced with out-of-source builds.
This is working in my example:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(STM32F4Examples C)
set(CMAKE_CROSSCOMPILE OFF CACHE BOOL "is crosscompiled")
message(STATUS "CMAKE_CROSSCOMPILE ${CMAKE_CROSSCOMPILE}")
CMakeToolChain_STM32F4.txt
# cmake toolchain
# Use this file with cmake -DCMAKE_TOOLCHAIN_FILE=[PATH/TO/This/FILE] PATH/TO/SOURCES
set(CMAKE_CROSSCOMPILE ON CACHE BOOL "is crosscompiled" FORCE)
This cmake -DCMAKE_TOOLCHAIN_FILE=.. command will set CMAKE_TOOLCHAIN_FILE.
Check cross compiling with following:
if (CMAKE_TOOLCHAIN_FILE)
# This is in cross compiling condition.
set(PROJECT_OUT_NAME ${PROJECT_NAME}.elf)
else ()
set(PROJECT_OUT_NAME ${PROJECT_NAME})
endif()
add_executable(${PROJECT_OUT_NAME} "main.cpp")

Simple CMakeLists.txt that reflects typical directory structure (/src, /inc, /bin subdirectories)

I am struggling to make a CMakeList.txt file to reflect a simple, typical makefile. The original is here http://pastebin.com/S9Czr1pt .
I tried many things (like SET(SOURCE ... and SET(HEADERS... ) to add /src, /lib and /third-party//include ), but I have no luck.
Can anyone either help me out or point to a tutorial that does this thing?
This is just an out of the blue skeleton - please see the CMake documentation for details of each function:
cmake_minimum_required(VERSION 2.6)
# Project name, can be used as target name too
project(Example)
# Search all .cpp files within src - recursive!
# You can add all source files by hand here too
file(GLOB_RECURSE SRCS "src/*.cpp")
# Add include path (you can also add it's sub directories
include_directories("include")
# Search for packages -- PLEASE NOTE: many libraries provide alternative's to this
# which often provide more functionality
find_package(PkgConfig REQUIRED)
# TODO: add proper pkg modul search info's and change the variable's name
pkg_search_module(PACKAGE_NO1 ...)
# Show some messages (optional)
if( (PACKAGE_NO1 )
include_directories(${(PACKAGE_NO1_INCLUDE_DIRS})
message(STATUS "Using OpenSSL ${(PACKAGE_NO1_VERSION}")
else()
# do error handling
endif()
# Add compiler flags
add_definitions(-std=c++11 -Wall) # -g O3 etc are added according to Release / Debug compilation
# Build a executable target (1st param: Target name; 2nd: source files)
add_executable(${PROJECT_NAME} ${SRCS})