CMake and C++11 on MacOS – Set your variables before creating your target

Taylor Built Solutions Logo

I’ve been toying with using the Microsoft CPPRestSDK library to practice with REST services and see how easily they’re done in C++. Given that I’d like the project to run on more than one OS, I thought it’d be a good idea to use CMake to build it in several places. This, of course, has required me to refresh my knowledge of CMake. And, of course, I’ve run into a problem with CMake: Getting it to generate a Makefile or XCode project on MacOS that builds and links against C++11.

One big note that I’ll make here is that this post is a description of a problem that I’m having. I will post links to this post in various places where I think I might find answers other than Google, StackOverflow, and the CMake docs where I’ve already looked. If I get a good answer from somebody I will update this post to reflect the changes made.

Problem Solved!

It turns out that I had a misunderstanding of how CMake expected targets to be declared. It expects all properties related to a target before you declare that target with add_executable or add_library. I have updated the CMakeLists.txt to have the properties set before the add_executable call and, lo and behold, it works appropriately. I would like to thank the folks in the CPPLang Slack community for helping me identify that this was the problem. Please see the conclusion for more thoughts on this.

What does the CMakeLists.txt look like?

This, of course, is likely the first question that comes to mind for anybody who knows CMake well. You can find the project here and the CMakeLists.txt here. Given that my project is relatively simple (so far) I’m attracted to the idea that Craig Scott posted. That is to use the following settings:

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

As you can see in my CMakeLists.txt I’ve got these set. You’ll also notice that they’re only set for MSVC. Everybody else gets the following to set -std=c++11 directly:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
add_definitions(-std=c++11)

Why don’t I use CMAKE_CXX_STANDARD for MacOS?

This is setup this way because, at present, using the CMake variables did not set the command line options correctly for clang on MacOS. If I use CMAKE_CXX_STANDARD to generate Makefiles on MacOS here are the beginning of the compile time errors. These come from the CPPRestSDK headers and are only errors if you’re not compiling with C++11 or above:

[ 50%] Building CXX object CMakeFiles/EveDataRetriever.dir/EveDataRetriever.cpp.o
In file included from /Users/taylor/development/EveDataRetriever/EveDataRetriever.cpp:4:
In file included from /Users/taylor/development/EveDataRetriever/EveDataRetriever.h:6:
In file included from /usr/local/include/cpprest/http_client.h:47:
In file included from /usr/local/include/cpprest/asyncrt_utils.h:17:
In file included from /usr/local/include/pplx/pplxtasks.h:61:
In file included from /usr/local/include/pplx/pplx.h:47:
In file included from /usr/local/include/pplx/pplxlinux.h:27:
In file included from /usr/local/include/boost/thread/condition_variable.hpp:16:
In file included from /usr/local/include/boost/thread/pthread/condition_variable.hpp:9:
In file included from /usr/local/include/boost/thread/detail/platform_time.hpp:19:
In file included from /usr/local/include/boost/chrono/duration.hpp:42:
In file included from /usr/local/include/boost/type_traits/common_type.hpp:22:
In file included from /usr/local/include/boost/type_traits/detail/common_type_impl.hpp:12:
/usr/local/include/boost/type_traits/detail/common_arithmetic_type.hpp:209:75: error: expected expression
    BOOST_STATIC_CONSTANT(int, selector = sizeof(select(cond() ? T() : U())));

You can see the same error displayed in Xcode if I generate an Xcode project and build with it:

Why doesn’t CMAKE_CXX_STANDARD work for me on MacOS?

That is a very good question! I’ve done some googling for how to set the C++ standard to C++11. Most of the pages I’ve found have recommended using the CMake variables to set the standard and, if that doesn’t work, to just set the appropriate flags for the command line. You can see in the CMakeLists.txt that this is what I’ve ended up doing.

These flags are, of course, not portable. This defeats some of the purpose of using CMake. I know there will always have to be some settings that are OS specific but if there’s a way to use CMake to set something for all platforms in a generic manner I’d prefer to do that.

Conclusion

I have re-written this section now that I’ve solved this problem. The Cmake documentation surrounding variables describes that they are dynamically scoped. If add_executable reads the variables at the time it is executed and it is what generates all of the command line flags for the compiler then it makes sense that the variables must be set beforehand.

So, in short, my misunderstanding is where in the CMake process the compile time properties are set. The way it is implemented means that these properties only have to be generated once. This is more efficient than having to regenerate them whenever a variable is changed. I did not find anywhere in the CMake documentation that explicitly warns users to have variables set before creating the target. But, to be fair, I haven’t read ALL the documentation. I hope this helps you in your endeavors with CMake.

Leave a Reply

Your email address will not be published. Required fields are marked *