4

How would one handle this kind of compile situation in CMake? Suppose we have a source code directory with subdirectories like

src:
 helper:
  innerpkg:
   a.cpp
   a.h
  helper.cpp
  helper.h
 main.cpp
 main.h

File a.cpp calls stuff from file helper.cpp, though helper.cpp may not call anything in a.cpp. So if we make the helper subdirectory a library, and innerpkg a library, we get a problem where helper needs to include innerpkg, but innerpkg also needs helper -- a cyclical dependency. What is the right way to handle this and still leave some source files in helper? I'm pretty new to using CMake, by the way.

EDIT: The CMakeLists.txt file in innerpkg might look like:

 include_directories(${CCMBS_SOURCE_DIR})
 set(INNERPKG_SRC a.cpp)
 add_library(innerpkg ${INNKERPKG_SRC})

and in helper:

 include_directories(${PROJ_SOURCE_DIR})
 add_subdirectory(innerpkg)
 set(HELPER_SRC helper.cpp)
 add_library(helper ${HELPER_SRC})

 target_link_libraries(ui "-Wl,--whole-archive")
 target_link_libraries(ui innerpkg)
 target_link_libraries(ui "-Wl,--no-whole-archive")

in case this helps. How would these need to be set up to solve this problem?

EDIT: The exact dependency structure is this: The stuff in innerpkg depends on that in helper. innerpkg is used by main. The other scenario is that helper also depends on stuff in innerpkg, i.e. a cyclical dependency, while main uses helper or innerpkg.

The_Sympathizer
  • 1,191
  • 9
  • 17
  • If innerpkg is not in dependency with helper, why is it located in helper? – Patrick B. Jan 16 '14 at 08:03
  • @Patrick B.: It calls stuff in helper.cpp, as I mentioned. Or do you mean, "helper does not depend on innerpkg"? It could also implement interfaces or base classes in helper, so it's in its directory as a specific implementation. – The_Sympathizer Jan 16 '14 at 08:22
  • For better understanding maybe you could post a link-dependency-diagram which shows who need what to work. Or your CMakeLists.txt. – Patrick B. Jan 16 '14 at 12:15
  • Well the CMakeLists.txt for the "real" program is more complicated than the illustration above, which just shows the problem, so it'd require delving into the details of the "real program". I suppose I could give an imaginary CMakeLists.txt for the hypothetical example program, though. But the point of this question is I'm not sure what to put in there to handle the kind of dependency I mention (subdirectory depending on files in parent). I suppose I could give one for the directory "helper", though. Would you want that? And as for the diagram, what isn't clear about what I've written? – The_Sympathizer Jan 16 '14 at 22:39
  • Just gave simplified CMakeLists.txt example. – The_Sympathizer Jan 18 '14 at 00:17

1 Answers1

7

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.

SirDarius
  • 41,440
  • 8
  • 86
  • 100
  • I added a note mentioning what uses the helper/innerpkg. Does this help? Also, this is supposed to be *one program*, not multiple programs. I also thought you needed to make them libraries, since some of the material I was reading to try and familiarize myself with CMake was saying that you make the subdirectories libraries, and you needed one CMakeLists.txt file for each subdirectory. So yes, that was the assumption I was making because that's what the material I was using seemed to be suggesting was the "right" way to do it. – The_Sympathizer Jan 19 '14 at 00:06
  • So we are in the scope of section 2. of my answer. I'm interested in what your reference material is, because as I said, C++ and CMake don't enforce any specific directory layout. – SirDarius Jan 19 '14 at 10:56
  • Well I don't have a list of all the references I used, but here's an example of something that might have been something I used which implies that a subdirectory "equals" a library: http://stackoverflow.com/questions/5628927/cmake-subdirectories-dependency (see top answer) – The_Sympathizer Jan 19 '14 at 22:27
  • I also found this, which IS one of the things I used since it was still highlighted on my browser: http://www.cs.swarthmore.edu/~adanner/tips/cmake.php "Each directory in your project should have a CMakeLists.txt file." and it shows the CMakeLists.txt for the subdirectory as building a library. – The_Sympathizer Jan 19 '14 at 22:29