I’m building a framework that needs to use CUDA internally, which is currently annoyingly complicated to use with Bazel, which is the build system I’m very familiar with. For this reason as well as wider compatibility, I’m planning on using CMake for the library. This is actually my first project that I’m using CMake for, so I’m somewhat confused with how to use it given the constraints that I have as well as how to set up everything in an idiomatic fashion.
Some of the things I’d like to do:
- The framework is itself a shared library that is dynamically linked by the user.
- I don’t want to maintain a separate public header, so I’d like to use the same main headers for both the external users as well as the code in the internal library.
- The main() function is contained in the shared object and calls back into client code, i.e. the linker needs to be told that it’s fine to have unresolved symbols in the dynamic library.
To be more concrete, here’s the most bare bones structure that I can come up with, which could be used as a template for the build system I need:
shared library (i.e. my project):
foo/foo.h
foo/internal/log.h
foo/foo.cc
client application:
bar/bar.cc
Contents of files as follows:
foo/foo.h:
#pragma once
#include <iostream>
#include <string>
#ifdef FOO_SHARED_LIB
#include "internal/log.h"
#else
static void LogClient(std::string msg) {
std::cout << "Client: " << msg << std::endl;
}
#define LOG(msg) LogClient(msg)
#endif
Here the point is that the folder named “internal” should not actually have its contents copied with the installation (later I would want to figure out how to run tools like “unifdef” on the code to strip out the internal branch).
foo/foo.cc:
#include "foo.h"
extern void PrintHello();
int main() {
LOG("Printing hello");
PrintHello();
return 0;
}
foo/internal/log.h:
#pragma once
#include <iostream>
#include <string>
static void LogInternal(std::string msg) {
std::cout << "Library: " << msg << std::endl;
}
#define LOG(msg) LogInternal(msg)
bar/bar.cc:
#include <foo/foo.h>
void PrintHello() {
LOG("hello");
}
To compile this manually to what I want, I would do:
clang++ -shared -fpic foo/foo.cc -DFOO_SHARED_LIB -o bin/libfoo.so
clang++ bar/bar.cc -o bin/bar -lfoo -I. -Lbin/
LD_LIBRARY_PATH="bin/" bin/bar
and running it would give me the output:
$ LD_LIBRARY_PATH="bin/" bin/bar
Library: Printing hello
Client: hello
There are a couple of things here regarding code organization:
- I’d like to header to be next to the code and not in a specific “include” folder. This means I would need to somehow specify the headers that I want installed and where to put them, i.e . “foo/foo.h” should be discoverable to library users as “#include <foo/foo.h>”
- Certain headers like the ones under “internal/” should not be installed.
- When building the library itself, the “FOO_SHARED_LIB” preprocessor define should be set.
I’m trying to piece together a cmake file for this that follows modern idioms. Some of the points like allowing unresolved symbols in the linked shared library made me trip up with trying to produce anything that works on MacOS X. I only managed to put together something working by manually using “non-modern” cmake commands that I found in some old tutorials.
I assume that an expert on cmake could whip together working templates for the above bare bones project in no time, so I could continue from there.