cmake project() requirement

I created a framework to build audio plugins and in order for the build to work there are some variables that needs to be defined BEFORE calling project(). So right now, the users of my framework must write something like this:

include("cmake/re-cmake.cmake")

# RE_CMAKE_ROOT_DIR is defined when including cmake/re-cmake.cmake
include("${RE_CMAKE_ROOT_DIR}/cmake/RECMakeOptions.cmake")

project(Test LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)

include("${RE_CMAKE_ROOT_DIR}/sdk.cmake")

Although you could argue it is not a lot of code to write, I wanted to create a macro like this (so that I can also expand in the future…)

macro(re_cmake_project NAME)
  include("${RE_CMAKE_ROOT_DIR}/cmake/RECMakeOptions.cmake")  
  project(${NAME} LANGUAGES CXX)
  set(CMAKE_CXX_STANDARD 17)
  include("${RE_CMAKE_ROOT_DIR}/sdk.cmake")
endmacro()

So that the user would only have to do this:

include("cmake/re-cmake.cmake")
re_cmake_project(Test)

But when you do this, cmake complains (it is a warning) that there is no “project” defined

CMake Warning (dev) in CMakeLists.txt:
  No project() command is present.  The top-level CMakeLists.txt file must
  contain a literal, direct call to the project() command.  Add a line of
  code such as

    project(ProjectName)

  near the top of the file, but after cmake_minimum_required().

  CMake is pretending there is a "project(Project)" command on the first
  line.
This warning is for project developers.  Use -Wno-dev to suppress it.

I understand that you can use -Wno-dev to suppress it, but I cannot ask all my users to do that. Nor is it easy to do in CLion for example, without having to go and tweak the way the command line is invoked which again, I don’t want to ask all my users to do…

I was hoping that using a macro (since macros are essentially like defines in C) would help but it does not.

Is there a way to turn off this warning message directly in my cmake/re-cmake.cmake included file because this kind of project is defined differently?

no, calling project in a macro is not allowed.

Well, it’s allowed, but is not sufficient for this.

They’re not textual replacement. They have some similar behaviors, but thinking of them as #define is not a good mental model for them.

It is ambiguous if it is allowed for the root project() call.

From https://cmake.org/cmake/help/latest/command/project.html#usage

The top-level CMakeLists.txt file for a project must contain a literal, direct call to the project() command

The question would be does a macro meet these requirements. Or would a macro loaded via include violate this rule.

this was a response to my comment. I wrote that calling project in a macro is not allowed, which is false.

the top-level CMakeLists.txt for CMake in project mode must follow this requirement as described in the error message (which is what i meant). A call via macro, function, include or subdirectory doesn’t violate this rule but it doesn’t meet the requirement either.

independently of the requirement, project() can additionally be called multiple times in the same scope or anywhere else like other functions, except it cannot be before the first cmake_minimum_required. Also, it does set various checks and enables languages so it must be used with caution.

I guess I am still unclear. Clearly it works since I am getting a warning but then it doesn’t prevent the rest of the CMake file to be executed and completes successfully. But the warning states ‘CMake is pretending there is a “project(Project)” command on the first line’, so does it mean that the include that happens before the project() call inside the macro is executed AFTER the “pretend” project that CMake mentions in the warning? The reason is that some variables must be set before calling project() for the right toolchain to be set up…

At the end of the day, I strongly believe that what I am doing is not using CMake for what it has not been designed to be used for. It is a very valid use case. I just want to shield my users to have to copy some boiler plate codes over and over in all projects.

Yes. CMake first parses the entire file, sees no literal, direct call to project, issues the warning, adds a synthesized project() call on the first virtual line of the file contents, then executes it. So yes, it happens before your macro does the include.

This piece of boilerplate will just have to be dealt with manually. I don’t know the reason for this synthesized project() behavior, but it’s probably to handle some backwards compat behavior. Alas, it cannot be managed by policies because it happens before the contents are executed to be able to manage policies.

Ok. So when I say it worked, it really didn’t, because I call set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE STRING "") which, according to the documentation ‘should be set prior to the first project()’ and the synthesized project() then happens before…

That is a real bummer :frowning:

Yes, that probably appears to work because the variable setting is visible, but the compiler detection almost certainly didn’t use it.