First CMakeLists.txt feedback please

Hello,

I try to learn c++ by doing this course : https://www.learncpp.com/

Now I like to use CMake instead of doing all by myself.

So I have a directory that contains :

main.cpp
io.cpp
io.h

Is this a good CMakeLists to get things compiling

project (CHALLENGE3)
cmake_minimum_required(VERSION 3.0)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

add_executable(Challenge3 main.cpp io.cpp)

or should I add the header file also ?

That project already looks quite good. Except for perhaps cmake_minimum_required() - that should be the first line in the file. And probably with a newer version of CMake too, I would put 3.22 there (that’s the one that comes packaged in Ubuntu 22.04).

should I add the header file also

Not required, but I see people are doing so, usually to get them to show up in their IDEs. I mostly build stuff from CLI, so I never needed to add headers to target sources.

Also, I would probably recommend you not to add sources right in the add_executable(), so instead it would be something like:

add_executable(${PROJECT_NAME})
set(sources
    ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/io.cpp
)
target_sources(${PROJECT_NAME} PRIVATE ${sources})

In newer versions of CMake there is now also file sets, which is likely an even better way to handle target sources, but I haven’t tried those myself yet.

Thanks,

What does the line of target-sources exactly do

It is documented in the article that I linked, but actually, given the context, one could probably say that it’s pretty much self-explanatory :slight_smile:

There’s a few things here I wouldn’t do:

  • Don’t use ${PROJECT_NAME} for the target name. They are two distinct concepts, a target doesn’t have to match the project name. Most projects will have more than one target (your project has tests, right? right…? :wink: ).
  • Don’t use variables to hold lists of files if you can put that list of files directly in the command instead. The sources variable here is unnecessary.
  • The poster’s original approach of putting the list of files directly in the add_executable() call is fine. If you can do it that way and it doesn’t make the project any more complicated to do so, then that’s what I would do. Simpler is better. The main reasons you’d have a separate target_sources() call would be you either need to only add things conditionally (say, platform-specific files) or if you want to add source files progressively from subdirectories.
  • There’s no need to prefix the source files with ${CMAKE_CURRENT_SOURCE_DIR}, as long as you specify 3.13 or later as the version in the cmake_minimum_required(VERSION X.Y) call at the start of your CMakeLists.txt file. If you list files directly in the call to add_executable() instead of target_sources(), the CMake version doesn’t matter, you can leave out ${CMAKE_CURRENT_SOURCE_DIR} because it’s always assumed for relative paths in that case.

Apart from swapping the order of the cmake_minimum_required() and project() calls at the start of the CMakeLists.txt file and specifying a higher minimum CMake version (as @retif already highlighted), I think the poster’s original example is ok. As you get more familiar with CMake, follow @retif’s suggestion to take a look at file sets. They are fast becoming a recommended way of listing your headers. This will become more relevant to you as your project gets bigger and you start using subdirectories, linking targets together across directories, and so on.

2 Likes

Most projects will have more than one target

You are right here, it’s just my own “deformation” - I rarely have more than one target per project (yeah, no tests :slight_smile:), and most of my nested targets are their own projects (as any component can be built “standalone”).

Good recommendations otherwise, and I probably should re-consider my reluctance towards adding sources right in the add_executable()/add_library().