CMake: Using Control Flow

CMake: Using Control Flow
CMake: Using Control Flow

Introduction:

In previous examples, we have used if-else-endif. CMake also provides language tools for creating loops: foreach-endforeach and while-endwhile. Both can be combined with break to exit the loop early. This article also serves as the conclusion of the first chapter, marking our official entry into learning CMake.

CMake: Using Control Flow

Project Structure

.
├── cal_add.h
├── cal_subtract.cpp
├── cal_subtract.h
├── CMakeLists.txt
├── main.cpp
├── message.cpp
└── message.h

This project structure returns to a simple structure, mainly to quickly demonstrate the main purpose of this article: how to use control flow to implement certain functions.

Project Address:

https://gitee.com/jiangli01/tutorials/tree/master/cmake-tutorial/chapter1/13

CMakeLists.txt

cmake_minimum_required(VERSION 3.10 FATAL_ERROR)

project(control-flow LANGUAGES CXX)

set(CXX_STANDARD 11)

add_library(test_lib
    STATIC
    ${CMAKE_SOURCE_DIR}/message.h
    ${CMAKE_SOURCE_DIR}/message.cpp
    ${CMAKE_SOURCE_DIR}/cal_add.h
    ${CMAKE_SOURCE_DIR}/cal_subtract.h
    ${CMAKE_SOURCE_DIR}/cal_subtract.cpp
)

# Compile the library with -O3 optimization level and set a private compiler option for the target
target_compile_options(test_lib PRIVATE -O3)

# Generate a list of source files to be compiled with lower optimization options
list(APPEND sources_with_lower_optimization
     cal_subtract.cpp
)

# Loop through these source files and adjust their optimization level to -O2. Use their source file properties to achieve this
message(STATUS "Setting source properties using IN LISTS syntax:")
foreach(_source IN LISTS sources_with_lower_optimization)
  set_source_files_properties(${_source} PROPERTIES COMPILE_FLAGS -O2)
  message(STATUS "Appending -O2 flag for ${_source}")
endforeach()

# To ensure properties are set, loop again and print each source file's COMPILE_FLAGS property
message(STATUS "Querying sources properties using plain syntax:")
foreach(_source ${sources_with_lower_optimization})
  get_source_file_property(_flags ${_source} COMPILE_FLAGS)
  message(STATUS "Source ${_source} has the following extra COMPILE_FLAGS: ${_flags}")
endforeach()

add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} test_lib)
set_source_files_properties(file PROPERTIES property value)

Sets the properties to the given file’s passing values. Similar to targets, files also have properties in CMake that allow for very fine-grained control over the build system.

get_source_file_property(VAR file property)

Retrieves the value of the required property for the given file and stores it in the CMakeVAR variable.

Note: In CMake, lists are strings of groups separated by ;. Lists can be created by list or set commands. For example, set(var a b c d e) and list(APPEND a b c d e) both create the list a;b;c;d;e.

foreach(loop_var range total)
or
foreach(loop_var range start stop [step])

Loop through integers by specifying a range

Related Source Code

cal_add.h

#ifndef CALCULATE_ADD_HEADER
#define CALCULATE_ADD_HEADER

template <typename T, typename U>
auto Add(T t, U u) -> decltype(t + u){
    return t + u;
}
#endif // ! CALCULATE_ADD_HEADER

cal_substruct.h

#ifndef CALCULATE_SUBSTRACT_HEADER
#define CALCULATE_SUBSTRACT_HEADER

float Substract(float a, float b);
#endif // ! CALCULATE_SUBSTRACT_HEADER

cal_substruct.cpp

float Substract(float a, float b) { return a - b; }

message.h

#ifndef MESSAGE_HEADER_H_
#define MESSAGE_HEADER_H_
#include <string>

class Message {
 public:
  Message() {}
  void Print(const std::string& message);
};
#endif // ! MESSAGE_HEADER_H_

message.cpp

#include "message.h"

#include <iostream>
void Message::Print(const std::string& message) {
  std::cout << message << std::endl;
}

main.cpp

#include "message.h"
#include "cal_add.h"
#include "cal_subtract.h"
int main() {
  int a = 1;
  int b = 2;
  auto c = Add(a , b);
  Message message;
  message.Print(std::to_string(c));
  return 0; 
}

Compilation Result

-- The CXX compiler identification is GNU 9.4.0
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Setting source properties using IN LISTS syntax:
-- Appending -O2 flag for cal_add.cpp
-- Appending -O2 flag for cal_subtract.cpp
-- Configuring done
-- Generating done
-- Build files have been written to: /home/jiangli/repo/tutorials/cmake-tutorial/chapter1/13/build

Appendix

foreach() has four usage forms:

  • foreach(loop_var arg1 arg2 ...): where the loop variable and explicit item list are provided. This form is used when printing compiler flags for items in sources_with_lower_optimization. Note that if the project list is in a variable, it must be explicitly expanded; that is, ${sources_with_lower_optimization} must be passed as an argument. You can loop through integers by specifying a range, for example: foreach(loop_var range total) or foreach(loop_var range start stop [step]).

  • Looping over list value variables, for example: foreach(loop_var IN LISTS [list1[...]]). The parameter is interpreted as a list, and its contents will be automatically expanded.

  • Looping over variables, for example: foreach(loop_var IN ITEMS [item1 [...]]). The contents of the parameter are not expanded.

Finally: Don’t let bad moods affect your day! Peace & Love!

CMake: Using Control Flow
CMake: Using Control Flow

Leave a Comment