How to set project root when "No project() command is present"?

I am a just few days into CMake, and I need to understand if it is possible to reference project root from CMakeLists.txt in subdirectories to get project variables right?

This is a typical C project with Python binding.

./
├── c/
│   ├── Makefile.am
│   ├── prog.h
│   └── prog.cpp
├── include/
│   ├── prog/
│   └── prog.h
├── python/
│   ├── CMakeLists.txt
│   └── setup.py.in
├── src/
├── tests/
├── CMakeLists.txt
└── README.md

I want to build the python binding, so I do cd python && cmake . to get setup.py and some warnings. I know that I can do `cmake

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.

-- The C compiler identification is GNU 11.2.1
...
-- Generating done
-- Build files have been written to: /tmp/proj/python

The problem is that values in generated setup.py are wrong. Here is the lines from setup.py.in that are substituted.

      version="@PACKAGE_VERSION@",
...
          ["@CURRENT_SOURCE_DIR@/prog.py"],
          include_dirs = ['@PROJECT_SOURCE_DIR@/include'],
...
          library_dirs = ['@CURRENT_BINARY_DIR@', '@PROJECT_BINARY_DIR@']

And here is the result.

      version="",
          ["/tmp/prog/python/prog.py"],
          include_dirs = ['/tmp/prog/python/include'],
...
          library_dirs = ['/tmp/prog/python', '/tmp/prog/python']

version is not set, include_dirs should point to /tmp/prog/python. The contents of the python/CMakeFile.txt that does this.

✗ cat python/CMakeLists.txt 

set(CURRENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})

configure_file(setup.py.in setup.py)

add_custom_target(python ALL
    COMMAND python3 setup.py build
    DEPENDS prog prog.py)

install(CODE "execute_process(COMMAND python3 setup.py install --root=\$ENV{DESTDIR}/ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")

As I see the problem is that this CMakeFile.txt build gets project root wrong. That’s why it can not read version and other data.

So, is it possible to have a workflow of doing cd python && cmake .?

Yes, this is possible. Just write the CMakeLists.txt file in that directory to be a standalone project. Add a project command to the top of it, then you should be able to build from that directory.

I don’t want to duplicate what is already written in the root file. How to reuse it?

If you want to configure directly from the python directory, it must have a project command. This is a requirement of CMake. Besides, it shouldn’t be duplication, you can have different details there. For example, you might have:

Top level repo root:

project (MyCoolProject
   VERSION 1.0.0
   DESCRIPTION "A cool project")

Python directory:

project (MyCoolProjectPythonBindings
   VERSION 1.0.0
   DESCRIPTION "Python bindings for MyCoolProject")

So there is no way to refer to root CMakeLists.txt from CMakeLists.txt in subdirectory?

So that @PACKAGE_VERSION@, @PROJECT_SOURCE_DIR@ and @PROJECT_BINARY_DIR@ won’t have to be redefined when run from subdir.

No. If you configure from the top level directory, then any subdirectories would have the top-level variables defined. But if you want to run cmake in one of the subdirectories, that subdirectory needs to support that use case by setting itself up as a top level standalone project.

1 Like

Thanks for confirming. Any best practices how to make cmake in subdirectory fail with instruction to rerun it from top directory?

CMake already warned you in that output you posted above:

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.

To make this an actual error, you can make dev warnings errors. I forget the specific command line flag, I’d have to check.