CMake dependency conflict with same header file names - c++

I am stuck in CMake dependency issue. CMake does not rebuild the program when I modified the CXX header files.
My file structure is as such
$ tree
.
├── a
│   ├── a.h
│   └── c.h
├── b
│   ├── b.h
│   └── c.h
├── CMakeLists.txt
└── main.cxx
File contents:
a.h
$ cat a/a.h
#include "c.h"
b.h
$ cat b/b.h
#include "c.h"
main.cxx
$ cat main.cxx
#include "a/a.h"
#include "b/b.h"
int main()
{
}
CMake depend.internal
$ cat CMakeFiles/hello.dir/depend.internal
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.7
CMakeFiles/hello.dir/main.cxx.o
/proj/mtk09331/work/tmp/cmake/a/a.h
/proj/mtk09331/work/tmp/cmake/a/c.h
/proj/mtk09331/work/tmp/cmake/b/b.h
/proj/mtk09331/work/tmp/cmake/main.cxx
CMakeLists.txt:
$ cat CMakeLists.txt
add_executable(hello main.cxx)
As you can see, b/c.h does not exist in the dependency list.
Therefore, CMake won't rebuild the program if I modified b/c.h.
Does cmake not allow the same header file names?
Altough it works with renaming the header files.
But I would like to find a "correct" way to prevents this.
Thanks in advance

As #MABVT notes in the comment, CMake is unrelated here: the question is about proper projects' organization.
From the point of projects' organization, headers for every project can be devided into two parts:
private headers, which are used only for compile the project itself,
public headers, which are used by the outer world (by other projects).
Naming and organization of private headers are fully up to the project's author.
But for scalable projects naming and organization of their public headers should follow several rules:
Name of the public header should be unique among all projects.
Usually this is achived by moving these headers into the directory contained the name of the project.
Public header shouldn't include private header.
Public header should include other public header using only its unique name.
As you can see, headers a/a.h and b/b.h are public in your case.
But for headers a/c.h and b/c.h you should decide, whether they are public or private.
If you want to make a/c.h private, then public header a/a.h cannot include it (rule 2).
If you want to make a/c.h public, then public header a/a.h should include it using its unique name, that is #include <a/c.h> (rule 3).

This appears to be a feature of make. As stated by #Tsyvarev, adding the sub-directory name in the #include statement helps make find the correct dependencies but looks awkward in the source code.
ninja correctly determines the dependencies in the case above. To use with cmake install ninja and select it with ...
cmake -G Ninja

Related

CMake: Some include directories defined in source files, but undefined in headers for nested target

Given the following project structure:
.
└── library/
├── CMakeLists.txt
├── include/
│ └── class1.hpp
├── src/
│ └── class1.cpp
├── build/
│ ├── _deps/
│ │ └── fmt-build
│ └── sample/
│ └── app_using_library.exe
└── sample/
├── CMakeLists.txt
└── main.cpp
CMakeLists.txt of library:
project(library)
include(FetchContent)
FetchContent_Declare(
fmt
URL https://github.com/fmtlib/fmt/releases/download/9.1.0/fmt-9.1.0.zip
)
FetchContent_MakeAvailable(fmt)
add_library(library STATIC
"src/class1.cpp"
"include/class1.hpp"
target_include_directories(library PUBLIC include)
target_link_libraries(library PRIVATE fmt::fmt)
CMakeLists.txt of app_using_library:
project(app_using_library)
add_executable(app_using_library main.cpp)
target_link_libraries(app_using_library PRIVATE library)
class1.hpp:
#include "fmt/core.h" // cannot open source file "fmt/core.h"
class1.cpp:
#include "fmt/core.h" // works fine
Is there a possible cause or explanation for why the fmt headers are includable in class1.cpp, but not in class1.hpp when building both library and app_using_library? The include works correctly in the header when just building library
The compiler couldn't find the fmt headers in "class1.hpp", but it could find them in "class1.cpp".
The full error:
FAILED: sample/CMakeFiles/app_using_library.dir/main.cpp.obj
C:\msys64\mingw64\bin\g++.exe -ID:/Programming/cpp/library/include -g -std=gnu++20 -MD -MT sample/CMakeFiles/app_using_library.dir/main.cpp.obj -MF sample\CMakeFiles\app_using_library.dir\main.cpp.obj.d -o sample/CMakeFiles/app_using_library.dir/main.cpp.obj -c D:/Programming/cpp/library/sample/main.cpp
In file included from D:/Programming/cpp/library/sample/main.cpp:2:
D:/Programming/cpp/library/include/class1.hpp:4:10: fatal error: fmt/core.h: No such file or directory
4 | #include "fmt/core.h"
| ^~~~~~~~~~~~
compilation terminated.
It fails while compiling "app_using_library" because only "library" knows about fmt.
To resolve that you need to also include the information you have defined about fmt into the app_using_library CMakeLists.txt
Based on the answer from Tsyarev in the comments:
Such includes should only be done in private headers, i.e. headers not included in the INCLUDE_DIRECTORIES for that target. This prevents any executable target using library from also having to link to the library included in public headers.
Tsyarev's answers:
There is a notion about library's public header: that header is intended to be available to the consumers of the library. E.g., class1.hpp is public header for your library. For make a header available to the consumers, the header should be located in the PUBLIC include directory of your library. Any header included by a public header is public by yourself. E.g., since class1.hpp is a public header of the library and includes fmt/core.h, then fmt/core.h is a public header for your library too. And you need to make this headers accessible via PUBLIC include directory.
If you use fmt/core.h only in the private headers of your library, then your current code should work: library is PRIVATE linked with fmt::fmt, app_using_library is linked with library.

CMake C/C++ macro generating [duplicate]

This question already has answers here:
Getting base name of the source file at compile time
(12 answers)
Closed 5 years ago.
I use __FILE__ during logging and debug my program. And when I use it, I get full source file path. So, I wanna write my own macro similar the macro __FILE__.
I looked for a solution, but I found nothing about it.
So, does it exist a CMake way to implement user macro, that will be generate something data similar the __FILE__ macro?
Just remove the path from __FILE__.
Possible solution:
#include <libgen.h>
#include <stdio.h>
#define LOG(message) \
do { \
char* filename = basename(__FILE__); \
printf("In %s: %s", filename, message); \
} while(false);
Example has to be extended by check of log level. And instead of a fixed message a variadic number of arguments should be used.
Note: This is for Unix. Assume your OS provides a similar function.
As I mentioned in a comment already, you can create a CMake function that loops over all source files of a given target and adds a macro definition to the compile flags for that source file.
function(add_filepath_macro target)
get_target_property(SOURCE_FILES ${target} SOURCES)
foreach (FILE_PATH IN LISTS SOURCE_FILES)
file(RELATIVE_PATH RELATIVE_FILE_PATH ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH})
set_property(SOURCE ${FILE_PATH} APPEND PROPERTY COMPILE_DEFINITIONS FILEPATH="${RELATIVE_FILE_PATH}")
endforeach()
endfunction()
This now defines the macro FILEPATH in every TU that is part of the target you pass to it.
There is one caveat though: In contrast to the regular __FILE__ macro you can't use the FILEPATH macro in header files, because including one such header in different translation units immediately causes an ODR violation and thus undefined behaviour.
Demo
Let's see how this works for a very small example. Start with the following project structure:
.
├── cmake
│   └── add_filepath_macro.cmake
├── CMakeLists.txt
├── lib
│ ├── a.cpp
│   ├── a.h
│   └── CMakeLists.txt
└── main.cpp
The cmake/add_filepath_macro.cmake file contains nothing but the code shown above.
The we have the library files
// lib/a.h
#pragma once
char const* foo();
// lib/a.cpp
#include "a.h"
char const* foo() {
return FILEPATH;
}
with corresponding CMakeLists.txt
# lib/CMakeLists.txt
add_library(liba
a.cpp
)
target_include_directories(liba PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_filepath_macro(liba)
as well as a small demo executable
// main.cpp
#include <iostream>
#include "a.h"
int main() {
std::cout << foo() << '\n' << FILEPATH << '\n';
}
and finally the project CMakeLists.txt
# CMakeLists.txt
# not tested with older version, but I guess it should work with anything > 3.0
cmake_minimum_required(VERSION 3.9)
project(cmake_filepath_macro_demo LANGUAGE CXX)
include(cmake/add_filepath_macro.cmake)
add_subdirectory(lib)
add_executable(demo
main.cpp
)
add_filepath_macro(demo)
Running the resulting executable then produces the following output
$ ./demo
lib/a.cpp
main.cpp

Execute output of cmake target as dependency for another

I have the following directory structure:
.
├── CMakeLists.txt
├── generator
│   ├── CMakeLists.txt
│   └── main.cpp
├── include
└── src
├── CMakeLists.txt
└── mylib.cpp
I would like to build generator, then use generator to generate a source file that will be used to build mylib. I tried this:
generator/CMakeLists.txt:
add_executable(gen main.cpp)
add_custom_command(
OUTPUT
${CMAKE_BINARY_DIR}/generated.cpp
DEPENDS
gen
COMMAND
${CMAKE_BINARY_DIR}/gen -d /tmp
VERBATIM
)
src/CMakeLists.txt:
add_library(py-cppast
${CMAKE_BINARY_DIR}/generated.cpp
mylib.cpp)
CMakeLists.txt:
cmake_minimum_required(VERSION 3.1.2)
project(my-project)
add_subdirectory(generator)
add_subdirectory(src)
However, the command is not executed. Instead, I just get the error:
CMake Error at src/CMakeLists.txt:2 (add_library):
Cannot find source file:
/home/....(the cmake binary dir)..../generated.cpp
Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp
.hxx .in .txx
How can I tell cmake to execute the program I'm building with it? Is this even possible in a single build?
The problem comes from the fact that you are generating the file generated.cpp in one directory and then trying to add it to a target defined in a different directory. CMake only supports adding generated files to targets defined in the same directory scope. The add_custom_command() documentation explicitly mentions this restriction.
You probably want to move the generation of generated.cpp into the src directory. You should also use just the gen target name to refer to the executable to run, not ${CMAKE_BINARY_DIR}/gen which is not going to be correct with all CMake generator types. It would also be better style to use the current binary directory rather than the top level binary directory as the output dir. Your src/CMakeLists.txt should look something like this:
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated.cpp
DEPENDS gen
COMMAND gen -d /tmp
VERBATIM
)
add_library(py-cppast
${CMAKE_CURRENT_BINARY_DIR}/generated.cpp
mylib.cpp
)
CMake will automatically substitute the location of the gen target for you, even though it was defined in a different directory. There are some further subtleties to be aware of when using generated sources, particularly relating to dependencies. You may find this article helpful to fill in some gaps.

CMake: How to reuse an object files for the tests subdirectory project?

My project is something like this:
project
├── test
│ ├── CMakeLists.txt
│ ├── main.cpp
│ └── test_class1.cpp
├── CMakeLists.txt
├── main.cpp
├── ...
├── class1.h
└── class1.cpp
I want to reuse class1.o which was compiled for project binary.
Default CMake behavior compiled it twice for test too.
I try to use OBJECT library but it puts all objects in this library variable. Then compiler print
main.cpp:(.text.startup+0x0): multiple definition of `main'
It means that 2 different main.o in one target. And all other *.o files from main project compilation are.
How to exlude unneeded *.o files?
What I normally do is that I separate the application logic into a static library (subdirectory), which is then used by both the main application and the tests.
You could keep it in a single dir as well, but then you still need to exclude the main.cpp when building the object library, because otherwise you will indeed have multiple definitions of main when building tests (as defined in both main.cpp and the test main).
If you are listing the files explicitly (which is highly recommended for CMake), you can simply omit the main.cpp from the list. If you use globbing to list the files, you can remove the file from the list as described here: How do I exclude a single file from a cmake `file(GLOB ... )` pattern?

cpplint.py & cmake: how to specify include files

Assume I have a project of the following directory structure:
myproject
├── .git [...]
├── CMakeLists.txt
└── src
├── CMakeLists.txt
├── foo.cc
└── foo.h
If in src/foo.cc I include the header file like #include "foo.h" and then run Google's cpplint.py on it, it complains with
src/foo.cc:8: Include the directory when naming .h files [build/include] [4]
So I include it as #include "./foo.h". Now I get another complaint:
src/foo.cc:8: src/foo.cc should include its header file src/foo.h [build/include] [5]
However, if I include it as #include "src/foo.h", the compiler won't find it, with my current setup of CMake. This is how my two CMakeLists.txt files look like:
CMakeLists.txt:
project(myproject)
add_subdirectory(src)
src/CMakeLists.txt:
set(SRCS foo.cc)
add_executable(foo ${SRCS})
Is the way I'm using CMake somehow fundamentally wrong? Should I remove the src/CMakeLists.txt file entirely, and specify all source files in the base CMakeLists.txt with their full path?
Or should I simply ignore cpplint's complaints, as they don't really fit to how CMake projects are to be set up?
Add include_directories(${CMAKE_SOURCE_DIR}) in your top level CMakeLists.txt, like Wander suggested:
project(myproject)
include_directories(${CMAKE_SOURCE_DIR})
add_subdirectory(src)