[CMake] Replacing compiler flags for certain project subdirectories

Michael Hertling mhertling at online.de
Fri Apr 1 01:09:50 EDT 2011


On 03/30/2011 08:00 PM, Whitcomb, Mr. Tim wrote:
> Shortening to reduce wall-of-text:
> 
> I have a Fortran project with a top-level CMakeLists.txt file with 47 add_subdirectory calls.  Three of the subdirectories require a different set of preprocessor definitions and compiler flags than the other 47.  I can add the preprocessor definitions, but am having trouble with the compiler flags.  I need to have a completely different set in those subdirectories, so COMPILE_FLAGS target property won't work as it augments what's already there.
> 
> How can I replace (not extend) the compiler flags for these few subdirectories?
> 
> Tim
> 
>> -----Original Message-----
>> From: cmake-bounces at cmake.org 
>> [mailto:cmake-bounces at cmake.org] On Behalf Of Whitcomb, Mr. Tim
>> Sent: Friday, March 25, 2011 3:18 PM
>> To: 'cmake at cmake.org'
>> Subject: [CMake] Replacing compiler flags for certain project 
>> subdirectories
>>
>> I'm in the process of adding Cmake-build capability to a 
>> Fortran project that currently follows a traditional 
>> recursive-make build structure.  I've converted all the 
>> makefiles to CMakeLists.txt files and can now produce all the 
>> target libraries and executables with their dependencies 
>> correctly recognized.  These dependencies have made a huge 
>> difference in allowing us to perform parallel builds and have 
>> the project ready to go *much* faster than before.  I have a 
>> top-level CMakeLists.txt file that contains a few library 
>> searches (e.g. LAPACK, BLAS) and then an add_subdirectory 
>> call for each subdirectory in the project.  Each subdirectory 
>> corresponds to a library and/or an executable.
>>
>> Now that the listing files contain all the sources files they 
>> need, my current task is to go through and set the proper 
>> compiler/preprocessor flags to match the original makefiles.  
>> We have several directories in our source tree that are 
>> auxiliary libraries that we store in our Subversion 
>> repository and build as part of our regular build process.  
>> The libraries that are built in these directories are 
>> compiled with different compiler flags and different 
>> preprocessor definitions than the rest of the project.  I've 
>> been able to handle the preprocessor definitions by using 
>> add_definitions in the CMakeLists.txt files in the 
>> lower-level directories.  I see based on reading through some 
>> past threads (including the recent "Different CMAKE_CXX_FLAGS 
>> for different executables") on this list and on some 
>> StackOverflow questions that the COMPILE_FLAGS target 
>> property is very close to what I need (and actually *was* 
>> what I needed in several cases) but only appends flags to 
>> those currently  in use.  What I need is to be able to define 
>> a new set of compile flags (i.e. "forget everything you were 
>> using before and use THIS set in this directory").
>>
>> I've started going through the documentation for Building 
>> External Projects[*] but the first line states that "[a]n 
>> "external project" is one that you can get the source code 
>> for, but does not readily build with a simple 
>> ADD_SUBDIRECTORY call in your CMakeLists.txt file."  This 
>> feels like it's designed to solve a slightly different 
>> problem than what I'm trying to do - I have an 
>> add_subdirectory call in my CMakeLists.txt file - is the 
>> proper fix for this issue to make the pieces that require 
>> different flags external projects?  Will making some of these 
>> libraries external projects mess with the dependency calculation?
>>
>> I'm just getting started and trying to wrap my head around 
>> the options available.  What is the standard way of dealing 
>> with this issue?  Is there anything I'm missing?  Thank you 
>> for your assistance!
>>
>> Tim
>> [w] 
>>
>> [*] 
>> http://www.kitware.com/products/html/BuildingExternalProjectsW
>> ithCMake2.8.html

The difficulty w.r.t. a thorough management of compilation flags and
preprocessor definitions arises from their diverse origins (variables,
directory/target/source properties) and addressees (the whole project,
directories, targets, source files) so they can't be accessed centrally
at one go. If you want to control them entirely, you must control their
origins. AFAIK, these are mainly:

- CMAKE_<LANG>_FLAGS[_<CONFIG>] variables; usual directory scope but
  with lazy evaluation, i.e. the last value of these variables in a
  CMakeLists.txt file applies to all targets in this CMakeLists.txt.
- COMPILE_DEFINITIONS[_<CONFIG>] directory/target/source properties.
- COMPILE_FLAGS target/source properties.

Because of the directory-wide effect of the above-mentioned variables,
it's convenient to have only targets with the need for the same flags
in a CMakeLists.txt, but if I understand correctly, this is already
realised in your project. Otherwise, it gets even more complicated,
e.g. setting CMAKE_<LANG>_FLAGS[_<CONFIG>] to "" and working with
target and source properties only.

A possible approach to your concern might be the definition of
directory-specific variables for flags and assign them to the
CMAKE_<LANG>_FLAGS[_<CONFIG>] variables; see the following:

# CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(TOPLEVEL C)
# Define the project's flags:
SET(${PROJECT_NAME}_C_FLAGS "-O3" CACHE STRING
    "${PROJECT_NAME} C FLAGS")
# Enable the project's flags:
SET(CMAKE_C_FLAGS "${${PROJECT_NAME}_C_FLAGS}")
# Enter the subdirectories:
ADD_SUBDIRECTORY(xyz)
# Do the local stuff:
FILE(WRITE ${CMAKE_BINARY_DIR}/main.c "int main(void){return 0;}\n")
ADD_EXECUTABLE(main main.c)

# xyz/CMakeLists.txt:
# Define this directory's flags:
SET(${PROJECT_NAME}_XYZ_C_FLAGS "-O2" CACHE STRING
    "${PROJECT_NAME} XYZ C FLAGS")
# Enable this directory's flags:
SET(CMAKE_C_FLAGS "${${PROJECT_NAME}_XYZ_C_FLAGS}")
# Do the local stuff:
FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/f.c "void f(void){}\n")
ADD_LIBRARY(f SHARED f.c)

Here, there are project-wide ${PROJECT_NAME}_C_FLAGS and directory-
specific ${PROJECT_NAME}_XYZ_C_FLAGS variables that are assigned to
CMAKE_C_FLAGS in each CMakeLists.txt file to achieve a local effect.
With this pattern applied consistently to all affected directories,
you'll have a well-defined set of flag variables for those parts of
your project that need them; moreover, these variables can be easily
set on the command line or a GUI. A similar pattern could be applied
to preprocessor definitions which would be locally enabled by use of
the COMPILE_DEFINITIONS directory property. Usually, the properties
aren't as problematic as cached variables because they are under the
CMakeLists.txt author's control. Nevertheless, there're some further
issues to consider:

- Like variables, the COMPILE_DEFINITIONS _directory_ property is
  inherited by subdirectories if it is in effect at the place of the
  ADD_SUBDIRECTORY() command. Thus, it's generally advisable to enter
  subdirectories before setting up that property or variables for the
  current directory unless the inheritance is actually intentional.
- Be aware the user might define custom build types with associated
  CMAKE_<LANG>_FLAGS_<CONFIG> variables etc. and enable them on the
  command line or the GUI, so you possibly need to handle this, e.g.
  by regarding CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE} or by interating
  over CMAKE_CONFIGURATION_TYPES for multi-config generators.
- The user is basically free to choose the compiler, and the
  compilation flags are basically compiler-specific, so it's
  basically critical to hard code them in CMakeLists.txt files.
- Some compilation flags must be provided to the linker, too [1,2].
- The user might choose the compiler via environment variables like
  CC when CMake is run initially on the project, and CC et al. might
  already contain flags, e.g. CC="gcc -m32" cmake <path/to/toplevel>.

Using CMake's external project module in order to manage compilation
flags is inappropriate, IMO, especially if the concerned parts of the
project integrate perfectly with ADD_SUBDIRECTORY(). However, e.g., if
those parts must be compiled with a particular compiler different from
the project's usual one, external projects would be a reasonable choice.

'hope that helps.

Regards,

Michael

PS: If you disable CMAKE_C_FLAGS et al. for the user in the above-
    mentioned manner, don't forget to document it for your project.

[1] http://www.mail-archive.com/cmake@cmake.org/msg30015.html
[2] http://www.mail-archive.com/cmake@cmake.org/msg35381.html


More information about the CMake mailing list