Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CMake Behaviour: Custom configuration types with Visual Studio need multiple cmake runs to display correctly. Is this intended?

Context:

I'm migrating a project from plain Makefiles to CMake to add support for multiple compilers (gfortran + ifort) and operating systems (Windows + Linux). As I'm wrapping up the whole thing I'm trying to streamline the CMake behaviour and provide some extra functionality for the users.

After normalizing and tweaking the Compiler Options accross the possible combinations of operating systems and used compilers, it seemed reasonable to add a user-defined build type "DEBUGVERBOSE" with all checks and warnings enabled. Because we are dealing with very old legacy Fortran 77 code these settings generate >3000 warnings, which is why I didn't want to include all warnings in the standard Debug build type. For future code cleanup and debugging it seems appropriate though.


Issue:

I want to add this user-defined build type "DEBUGVERBOSE" to the list of available configurations in Visual Studio 2012. At the same time I want to get rid of the cmake-generated build types "MinSizeRel" and "RelWithDebInfo" to streamline the interface for the users (and because we never use these configurations).

According to CMake-Wiki - How can I extend the build modes with a custom made one? I set up my build type as follows:

# Set Project Name and supported Languages(optional)
project (s4 Fortran C)
# Set Version Number
set (S4_VERSION_MAJOR 1)
set (s4_VERSION_MINOR 0)

# Set Source Language
enable_language (Fortran)

...

if(WIN32)
  # Release flags
  set(CMAKE_Fortran_FLAGS_RELEASE " ${CMAKE_Fortran_FLAGS_RELEASE} /D__WIN_intel__ /assume:byterecl")
  # Debug Flags
  set(CMAKE_Fortran_FLAGS_DEBUG " ${CMAKE_Fortran_FLAGS_DEBUG} /D__WIN_intel__ /assume:byterecl /Od /warn:all /check:all
                                                              /warn:notruncated_source /warn:nodeclarations /warn:nounused") 
  # verbose Debug flags (user-defined build type)
  set(CMAKE_Fortran_FLAGS_DEBUGVERBOSE  " /debug:full /dbglibs /D__WIN_intel__ /assume:byterecl /Od /warn:all /check:all"
                                      CACHE STRING  "Extended Debug Flags used by the Fortran compiler during verbose Debug builds.") 
  set(CMAKE_CXX_FLAGS_DEBUGVERBOSE      " ${CMAKE_CXX_FLAGS_DEBUG}"
                                      CACHE STRING "Flags used by the C++ compiler during verbose Debug builds."  FORCE )
  set(CMAKE_C_FLAGS_DEBUGVERBOSE        " ${CMAKE_C_FLAGS_DEBUG}"
                                      CACHE STRING "Flags used by the C compiler during verbose Debug builds."    FORCE )
  set(CMAKE_EXE_LINKER_FLAGS_DEBUGVERBOSE "${CMAKE_EXE_LINKER_FLAGS_DEBUG}"
                                      CACHE STRING "Flags used for linking binaries during verbose Debug builds." FORCE )
  set(CMAKE_SHARED_LINKER_FLAGS_DEBUGVERBOSE "${CMAKE_SHARED_LINKER_FLAGS_DEBUG}"
                                      CACHE STRING "Flags used by the shared libraries linker during verbose Debug builds." FORCE )
elseif(UNIX)
   ...<definition of Release, Debug, DebugVerbose Compiler Options>
endif()

# Edit available Configrations to make them available in IDE that support multiple-configuration (for example Visual Studio)
if(CMAKE_CONFIGURATION_TYPES)
  set(CMAKE_CONFIGURATION_TYPES Release, Debug, Debugverbose)
  set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING
      "Reset the configurations to what we need" FORCE)
endif()

...<definition of lots of source files and build targets>...

# Visual Studio Userfile (set the debugging environment):
CreateUserfile()

This sets (according to cmake-gui and CMakeCache.txt) the corresponding variables at the first cmake-configure + generate run. But they do not show up in the Visual Studio solution until after a second cmake-configure + generate. I also tested this in the command line and there I also need two runs to get the settings into Visual Studio.

This behaviour also seems to be present in older cmake and Visual Studio versions, as can be seen in this answer to "How to create a new configuration with CMake". This also leads to a cmake bug report 0005811: Creating new configurations for MSVC that might be responsible for this behaviour.

Every other generator we use (Code:Blocks + MinGW; UNIX Makefiles) does not have this behaviour and gets the job done with the first run. I know Visual Studio is special because it is the only multi-configuration IDE in our setup, but I generally frown upon inconsistent behaviour because it confuses the user.


The Question:

Is there anything wrong with my approach, or is this cmake behaviour (->multiple runs needed) intended/ not changeable at the moment?



Current Workaround:

I have found a workaround - here - where I only append my user-defined build type to the already existing configurations. Note the placement of the "CMAKE_CONFIGURATION_TYPES" if-statement between setting "project" and "language"!

# Set Project Name and supported Languages(optional)
project (s4 Fortran C)
# Set Version Number
set (S4_VERSION_MAJOR 1)
set (s4_VERSION_MINOR 0)

# Edit available Configrations to make them available in IDE that support multiple-configuration (for example Visual Studio)
if(CMAKE_CONFIGURATION_TYPES)
 set(CMAKE_CONFIGURATION_TYPES Debugverbose)
 set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING
     "Append user-defined configuration to list of configurations to make it usable in Visual Studio" FORCE)
endif()

# Set Source Language
enable_language (Fortran)

...

if(WIN32)
  ...<definition of Release, Debug, DebugVerbose Compiler Options>
elseif(UNIX)
  ...<definition of Release, Debug, DebugVerbose Compiler Options>
endif

...<definition of lots of source files and build targets>...

# Visual Studio Userfile (set the debugging environment):
CreateUserfile()

With this solution the "DEBUGVERBOSE" Configuration is added to the existing configurations after the first cmake-configure + generate run and also shows in Visual Studio. It seems not possible though to change the list of configuraions to ommit the cmake-generated build types "MinSizeRel" and "RelWithDebInfo" (even after several cmake runs).

With the above mentioned post from the cmake discussion group, is it possible to improve my workaround so that only the configurations "Release, Debug, DebugVerbose" would be present in Visual Studio?



EDIT :


As pointed out by Peter in this answer there was an error in my implementation. Fixing this issue unfortunately did not change the Visual Studio 2012 behaviour.

# Set Project Name and supported Languages(optional)
project (s4)

...

# Set Source Language
enable_language (Fortran C)

...

The only difference I experienced with this modification was while comparing the new CMakeCache.txt to the previous one: CMake added Compiler Flags for "CXX". I assume this means C++, C#, etc. compilerflags?

Building from this and after rereading the corresponding post from the cmake discussion group I started experimenting. Testing with several combinations of PROJECT, enable_language and CMAKE_CONFIGURATION_TYPES positions relative to eachother I'd like to share my findings.

  1. CMAKE_CONFIGURATION_TYPE after PROJECT(s4 Fortran C), e.g. omitting enable_language:
    Without enable_language the code needs 2 CMake passes to make Visual Studio recognise the new settings while the Cache is updated with the first pass. It seems therefore that enable_language propagates the settings to the Visual Studio *.sln file.

  2. CMAKE_CONFIGURATION_TYPES before PROJECT(s4) statement:
    A Second cmake pass is necessary to propagate the settings to CMakeCache.txt. Visual Studio doesn't recognise the settings though. With this setup it seems possible to change the available build types but because Visual Studio doesn't recognise them and a second cmake pass is needed to propagate them this option is not viable for my case.

  3. CMAKE_CONFIGURATION_TYPES between PROJECT(s4) and enable_language(Fortran C):
    The settings are recognised with the first pass both in CMakeCache.txt and Visual Studio. Subsequent cmake passes don't alter the Cache. I therefore consider this method adequate.

It seems not possible though to delete certain build types or rewrite CMAKE_CONFIGURATION_TYPES. I tested the suggestions from the above mentioned discussion group, e.g. explicitly append the new build type(s) with LIST(APPEND ...) and check for/ delete duplicates. It appears though that these measures are implicitly implemented in the configurations routine, because adding (or omitting) these didn't change the behaviour or the Cache File (whether using the statement LIST(APPEND ...) or SET(...)).

Another behaviour I noticed is that the user defined string in

   set(CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES}
                                 CACHE STRING "Append user-defined configuration to list of configurations to make it usable in Visual Studio" FORCE)

is only recognised for the 2. Case. Using the 3. Case it seems to be ignored and a standard CMake string appears to be set in the Cache.


RESULT :

From these findings follows the current code as implemented.

# Set Project Name
project (s4)
# Set Version Number
set (S4_VERSION_MAJOR 1)
set (s4_VERSION_MINOR 0)

# Edit available Configrations to make them available in IDE that support multiple-configuration (for example Visual Studio)
# has to be between "project" and "enable_language" to work as intended!
if(CMAKE_CONFIGURATION_TYPES)
   list(APPEND CMAKE_CONFIGURATION_TYPES Debugverbose)
   set(CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES}
                                 CACHE STRING"Append user-defined configuration to list of configurations to make it usable in Visual Studio" FORCE)
endif()

# Set Source Language
enable_language (Fortran C)

...

I adopted the LIST(APPEND ...) statement because it improves the readability and states explicitly what the code is doing. As mentioned above it does not change the behaviour of CMake, e.g. automatic checking for duplicates.


OPEN QUESTIONS :

Are there any wrong conclusions from my experiments? Maybe someone knows the internal CMake procedures well enough to elaborate on the behaviour mentioned above? For example why is the string in the set(CMAKE_CONFIGURATION_TYPES ...) statement not recognised?

The original question still stands:

Is there a way to reset the CMAKE_CONFIGURATION_TYPES to a user defined list and pass these settings to Cache and Visual Studio with only 1 CMake pass?

like image 645
Max Graser Avatar asked Dec 17 '13 16:12

Max Graser


People also ask

How do I change CMake settings?

You can also edit the file using the CMake Settings Editor. Right-click on CMakeSettings. json in Solution Explorer and choose Edit CMake Settings. Or, select Manage Configurations from the configuration drop-down at the top of the editor window.

What is the default build type in CMake?

In this answer, it says Debug is the default cmake build configuration.

What version of CMake does Visual Studio use?

Visual Studio uses a CMake configuration file to drive CMake generation and build. CMakePresets. json is supported by Visual Studio 2019 version 16.10 or later and is the recommended CMake configuration file.

What is Cmake_configuration_types?

CMAKE_CONFIGURATION_TYPES. Specifies the available build types (configurations) on multi-config generators (e.g. Visual Studio, Xcode , or Ninja Multi-Config ). Typical values include Debug , Release , RelWithDebInfo and MinSizeRel , but custom build types can also be defined.


1 Answers

In the post you mentioned cmake-discussion, it is stated you should either

  • setting CMAKE_CONFIGURATION_TYPES before PROJECT()
  • appending to CMAKE_CONFIGURATION_TYPES after PROJECT() - invoked without languages - but before ENABLE_LANGUAGE()

In your workaround you set languages before CMAKE_CONFIGURATION_TYPES:

project (s4 Fortran C)

Best is to set CMAKE_CONFIGURATION_TYPES as an argument to cmake command

cmake ..... -DCMAKE_CONFIGURATION_TYPES:STRING=Debug ...
like image 199
Peter Avatar answered Sep 20 '22 05:09

Peter