1

I'm getting multiple definition link errors after conditionally compiling platform-specific code.

My project is laid out like this:

/
|__+ include/
|  |__+ native/
|  |  |__ impl.h
|  |
|  |__ general.h
|
|__+ src/
   |__+ native/
   |  |__ impl.linux.c
   |  |__ impl.win32.c
   |
   |__ general.c

At the top of the general.c file:

#if defined(LIBRARY_PLATFORM_LINUX)
    #include "native/impl.linux.c"
#elsif defined(LIBRARY_PLATFORM_WIN32)
    #include "native/impl.win32.c"
#endif

I set up introspection in CMake in order to detect the operating system and define the corresponding constants. The thing is, I didn't want to maintain one CMakeLists.txt file in every directory, so I simply globbed all the .c files as suggested in this answer:

file(GLOB_RECURSE LIBRARY_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/*.c")

Apparently, this is what is causing the problem. It seems to be compiling the code #included in general.c as well as the individual src/native/impl.*.c files.

CMakeFiles/lib.dir/src/native/impl.linux.c.o: In function `declared_in_impl_h':
impl.linux.c:(.text+0x0): multiple definition of `declared_in_impl_h'
CMakeFiles/lib.dir/src/general.c.o:general.c:(.text+0x0): first defined here

How can I untangle this situation?

Community
  • 1
  • 1
Matheus Moreira
  • 17,106
  • 3
  • 68
  • 107

1 Answers1

2

The best practice for that sort of cross-platform situation is to create two libraries, one for linux and one for windows and stop doing conditional includes. Each platform only compiles and links the relevant library.

The recommended way to do that with cmake is to stop globbing and just include each file. There are some situations where it can get confused and not realize that it needs to recompile. You can make an argument that non-changing legacy code won't have that problem.

If you really want to avoid doing either of these things, I would put the included code in a header instead of a c file. You don't really want the include guards so that people don't get it confused for something that should be used like a regular header. Put a bunch of comments in the file to warn them off of said behavior as well.

Tom Kerr
  • 10,444
  • 2
  • 30
  • 46
  • I hadn't considered that. Now that you mention it, separate libraries sound _much_ more efficient. Please, could you elaborate? Is it reasonable to make the implementations themselves shared libraries inside the same project? Should they be statically linked? – Matheus Moreira Mar 18 '12 at 05:32
  • @MatheusMoreira Hehe, dunno about efficient. Each file does one specific thing, so you get less confusion about what is going on is all. Usually you'd have each implementation in their own library, then have cmake only include one or the other. Static is usually less moving parts, but it really doesn't matter. Whatever you think makes the most sense for the project. – Tom Kerr Mar 18 '12 at 05:51
  • @MatheusMoreira I'm more of a C++ guy, so labeling that a best practice for C may be over-selling. I could see situations where that could be tricky with C. – Tom Kerr Mar 18 '12 at 05:57