I am using a third-party cmake based project as a git submodule. Lets say we have the following structure:
+ my_project/
- CMakeLists.txt
+ their_project/
- CMakeLists.txt
+ cmake/
- foobar.cmake
their_project/CMakeLists.txt does
include(cmake/foobar.cmake)
Foo(42)
and inside cmake/foobar.cmake we have
macro(Foo)
...
endmacro()
Inside my_project/CMakeLists.txt I do, say,
macro(Foo)
...my definition...
endmacro()
add_subdirectory(their_project)
The result of all this is that their_project still uses their definition of Foo because they redefine it by including cmake/foobar.cmake.
What can I do inside my CMakeLists.txt file to stop this from happening, so that their_project will use my macro?
Related
Please see the below minimal example:
├───CMakeLists.txt
├───bar
│ ├───CMakeLists.txt
CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(foo)
add_subdirectory(bar)
bar/CMakeLists.txt
project(bar)
cmake_path(GET CMAKE_CURRENT_LIST_DIR PARENT_PATH BAR_PARENT_DIR)
# how can I get `foo` given ${BAR_PARENT_DIR}?
# or is there a better way to get `foo`?
The real use case is that originally foo was extracted via ${CMAKE_PROJECT_NAME}, but recently there's a need to make the repo submodule compatible. Once this repo is being used as a submodule, ${CMAKE_PROJECT_NAME} won't be equivalent to foo anymore. Additionally, bar is a submodule of foo, so we aren't allowed to hard code foo into bar/CMakeLists.txt because that would break other repos that are using bar as a submodule.
Is there a way to extract the project name of a CMakeLists.txt from a parent directory?
Edit: I am looking for a solution that will make the below scenario work. Meaning foo is submoduled by another project. e.g.
baz
├───CMakeLists.txt
├───foo
│ ├───CMakeLists.txt
│ ├───bar
│ ├───CMakeLists.txt
Yes, the project name of the immediate parent.
This is not so hard. The project() command always sets the PROJECT_NAME variable when it is called. So the value of that variable just before you call project() is the name of the immediate parent.
There's nothing standard for this, but it's trivial to implement:
cmake_minimum_required(VERSION 3.23)
set(PARENT_PROJECT_NAME "${PROJECT_NAME}")
project(bar)
if (PARENT_PROJECT_NAME)
message(STATUS "Found parent: ${PARENT_PROJECT_NAME}")
else ()
message(STATUS "No parent!")
endif ()
Why this statement works if mylib is a static library and does not work if it's a dynamic(shared) one:
find_library(_found mylib PATHS ${mydirs})
where mydir keeps a list of directories, containing libraries (static and shared).
I'd like to find a dynamic library in the same manner as it is done for the static libraries (in order to get it close to the executable). There would be a list of directories but for simplicity sake, first this must be made working with a trivial test case:
cmake_minimum_required(VERSION 3.21)
project(myproj)
add_executable(myproj main.cpp)
find_library(a a${CMAKE_STATIC_LIBRARY_SUFFIX} PATHS "c:/work/libs/debug")
find_library(b b${CMAKE_SHARED_LIBRARY_SUFFIX} PATHS "c:/work/libs/debug")
message("dll=" "${CMAKE_SHARED_LIBRARY_SUFFIX}")
message("dll=" "${CMAKE_STATIC_LIBRARY_SUFFIX}")
message("a=" "${a}")
message("b=" "${b}")
And, the directory structure is as this:
c:/work (CMakeLists.txt_main.cpp)
\_libs
\_debug (a.lib b.dll)
\_release (a.lib b.dll)
The output:
1>a=C:/work/libs/debug/a.lib
1>b=b-NOTFOUND
1>dll=.dll
1>dll=.lib
Anything?
At the beginning I had a bunch of CMake projects handled separately: each one had its own target for generating documentation called doc. Now I need to wrap all these projects with a super-project: the problem is that super-project compilation fails complaining that exist multiple targets with the same name doc.
The simple solution I thought is to prepend each doc target with the name of the project, but it does not satisfy me.
I would like not to have to use make projectX_doc when compiling a single sub-project and to have a global target make doc for generating the documentation of all projects when compiling super-project.
Are my requests possible? Is there any mechanism to handle target collision?
well each subproject could verify if there are inside a super project with:
if("^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$")
set(IS_SUBPROJECT FALSE)
else()
set(IS_SUBPROJECT TRUE)
endif()
Thus in your projects CMakeLists.txt you can do:
if (NOT IS_SUBPROJECT)
set(DOC_TGT doc)
else()
set(DOC_TGT ${PROJECT_NAME}_doc)
endif()
add_custom_target(${DOC_TGT} EXCLUDE_FROM_ALL ...
note: you can merge both snippets to avoid IS_SUBPROJECT variable
In your super project CMakeLists.txt:
add_custom_target(doc EXCLUDE_FROM_ALL
DEPENDS projectX_doc projectY_doc...
So when configuring/building each sub project standalone you have
make doc otherwise when you are in your super project target doc become a meta target...
note: You can also use this trick to modify default options etc...
e.g. gflags:
https://github.com/gflags/gflags/blob/master/CMakeLists.txt#L126
https://github.com/gflags/gflags/blob/master/cmake/utils.cmake#L61
https://github.com/gflags/gflags/blob/master/CMakeLists.txt#L163
What I want to do is to create a CMakeLists.txt that defines a convenience macro to use in parent scope. I can use the macro just fine. However, I used the ${CMAKE_CURRENT_SOURCE_DIR} which is unfortunately not the directory of the CMake script the macro is defined in, but the one it is called from. Is there any way I can change that?
MWE
cmake_minimum_required(VERSION 3.6)
project(git_info CXX)
macro(do_stuff)
message("This CMakeLists.txt file is in ${CMAKE_CURRENT_SOURCE_DIR}")
endmacro()
One ugly way I found was to export variables to the parent scope that contain the path and use that in the macro. But I would prefer to only export the macro definition, if possible, to keep things clean.
Edit:
To clarify a bit. I have one folder with my top-levelCMakeLists.txt and one folder (my_folder) inside with the above MWE CMakeLists.txt. The top-level CMakeLists.txt looks as follows:
cmake_minimum_required(VERSION 3.6)
project(top_project CXX)
add_subdirectory(my_folder)
do_stuff()
You have to transfer CMAKE_CURRENT_LIST_DIR outside the macro into another variable or - in your case - a user defined global property:
set_property(GLOBAL PROPERTY DoStuffPath "${CMAKE_CURRENT_LIST_DIR}")
macro(do_stuff)
get_property(_my_marcros_file GLOBAL PROPERTY DoStuffPath)
message("This CMakeLists.txt file is in ${_my_marcros_file}")
endmacro()
That also works when the macros are defined in a file added with include().
References
In CMake, how can I find the directory of an included file?
What's the CMake syntax to set and use variables?
Use CMAKE_SOURCE_DIR to get a path to outermost parent CMakeLists.txt.
I'm trying to call add_library for all files with certain endings.
The dir structure is:
src
| - CMakeLists.txt (1)
| - main.cpp
| - gui
| - CMakeLists.txt (2)
| - some source and header files
So currently all cc files are in the gui directory.
(1) CMakeLists.txt:
file( GLOB_RECURSE my_sources *.cc )
message(STATUS "my_sources = ${my_sources}")
add_subdirectory( gui )
add_library( my_src ${my_SOURCES} )
target_link_libraries( my_src
my_gui
)
qt5_use_modules( my_src Core Gui Widgets)
(2) CMakeLists.txt:
file( GLOB my_gui_sources *.cc)
add_library( my_gui ${my_gui_sources} )
qt5_use_modules( my_gui Core Gui Widgets)
But I keep getting this output:
You have called ADD_LIBRARY for library my_src without any source files. This typically indicates a problem with your CMakeLists.txt file
-- my_sources = /home/bla/bla/src/gui/BorderLayout.cc;...;/home/bla/bla/my/src/gui/MainWindow.cc
-- my_gui_sources = /home/bla/bla/my/src/gui/BorderLayout.cc;...;/home/bla/bla/my/src/gui/MainWindow.cc
-- Configuring done
-- Generating done
-- Build files have been written to: /home/bla/bla/my/build
I know that I currently don't need the add_library in the first CMakeLists.txt, but later I will. I changed the first GLOB to GLOB_RECURSE, so that it finds at least anything.
For some reason your
file( GLOB my_gui_sources *.cc *.h)
Is not finding any file. To debug, you can print:
message(STATUS "my_gui_sources = ${my_gui_sources}")
Probably you want to use GLOB_RECURSE, which search in sub-directories:
file( GLOB_RECURSE my_gui_sources *.cc *.h)
Note that you don't need to add headers files to the source list.
Take care that you will have to rerun cmake every time you add a file to your project (cmake won't be called automatically, thing that instead happens if you touch one of the cmake files).
Link to documentation of command "file"
Edit:
The actual problem is that in your first CMakeLists.txt file you are using inconsistent naming for your variable (note that casing is important), therefore you have to change your add_library command to:
add_library( my_src ${my_sources} )
Note (off the records :-) ): the fact that casing is important for variable names might be confusing because, on the other hand, in cmake command names are case insensitive. It's also sometimes weird to notice that the character - (minus) might be used as part of the variable name: using _ (underscore) is most of the time preferable.