I feel that you are making the assumption that every directory in a CMake based project must contain its own CMakeLists.txt file and build its own library/executable.
This problem is not exactly related to CMake actually. You'll have the same issue with pretty much any build system. The problem here is that your dependency tree is unclear. Cyclic dependencies should be avoided at any cost.
Contrary to Java and its package-based system, there is no directory layout enforced for C++ applications, not even by CMake.
Dependencies should only be expressed in terms of libraries and executable programs, the former being mainly used in order not to duplicate code between separate programs that use the same functionality.
In your specific case, it is unclear who is using the helper
and innerpkg
libraries. So let's make several assumptions.
1. You have several programs using both libraries
Then it makes perfect sense to have separate libraries except that it's much better to express them as a flat directory structure, in order to clearly separate each library and program.
src
├─ helper
│ ├─ helper.cpp
│ └─ helper.h
├─ innerpkg
│ ├─ a.cpp
│ └─ a.h
├─ main
│ ├─ main.cpp
│ └─ main.h
└─ main2
├─ main2.cpp
└─ main2.h
Apparently, since you're saying that helper.cpp doesn't need to call things defined in a.cpp, then the helper
library does not depend on innerpkg
, therefore there is no cyclic dependency.
In terms of CMake, you'll have one CMakeLists.txt file in each directory, including at the root, for a total of five.
The top one will just include each subdirectory, while the remaining four will be responsible for building each binary.
Only the CMake lists in main
and main2
will actually call target_link_libraries()
.
2. The source code only compiles one program, and only it uses both libraries
Then why do you need libraries in the first place ?
Since there is only one executable program main
, there is no duplication of code and functionality.
The solution in that case is to keep your current directory structure, where sub-directories are not libraries but topics.
By topic, I mean a subset of the final program functionality, clearly separated for the ease of maintenance, but which does NOT imply dependencies with other topics.
Why not care about dependencies in that case ? Simply because you don't need to create libraries. Just add every cpp file as source of the main
program, so dependencies are automatically resolved all at once at link-time.
In terms of CMake, you only need one CMakeLists.txt file at the top, which will just build one program and list every cpp file in every sub-directory, like this:
add_executable(main
main.cpp
helper/helper.cpp
helper/innerpkg/a.cpp
)
3. The main program uses one library, and each library uses the other one
This is also assuming that you really need to generate a library, that will be part of the installation part of your project, for the sake of being used by third-party applications.
In this case, the cyclic dependency issue is solved by making helper
and innerpkg
only one library.
You should then have a directory structure looking like this:
src
├─ helper
│ ├─ helper.cpp
│ ├─ helper.h
│ └─ innerpkg
│ ├─ a.cpp
│ └─ a.h
└─ main
├─ main.cpp
└─ main.h
Like in the previous point, there is no dependency implied by the directory structure under helper
, since only one library will be compiled.
Also, even if main
uses functionality from innerpkg
it is just transparently exposed as a part of helper
.
In terms of CMake, you'll have a top CMakeLists.txt including both subdirectories, one in helper
listing every cpp file in helper and innerpkg to build a library, and one in main
listing every file in main, and linking with the helper library.