Best practices for structuring a C program (for CMake build) - c

I have a C program that was handed down to me by a developer who left. I am trying to figure out exactly what he had goign on and get the software re-arranged into something more logical so I can build it easier. I am using CMake to build, whereas he was using Make.
There is one src/ folder that had several source files in it, and out of those, about 4 had main() methods. The files with the main() methods are in files that are named more as if they are utilities, or tools, or whatever. This kind of strikes me as odd, because he also had a lib folder, with some other things in it that were built and looked more like libraries. Should I split out those main methods into "driver" source files, and make the methods that are also defined in those files to be other libraries? If I do this, I know how to make CMake go look for a library and build and link it to the driver for execution.
If it is acceptable to build those "library" source files where they are, in the src folder, should I just set CMake up to build everything in that folder all at once, or should I create a directory structure for at least some logical separation?
Just as an idea, here is the current directory structure
project
.../src
......file1.c
......file2.c <-has a main() as well as other methods
......file3.c
......file4.c <- has a main() as well as other methods
......file5.c
.../lib
....../lib1
........./file1.c <-references top level include folder files
........./file2.c
....../lib2
........./file1.c <-refs top level and local include files
........./file2.c
........./file2.h
.../scripts
.../include
.
.
.
Any advice on best practices for restructuring this build or configuring it in CMake is appreciated.

It's never too late for an answer, so I'd propose:
project
.../CMakeLists.txt
include_directories(include/)
add_subdirectory(lib/lib1)
add_subdirectory(lib/lib2)
add_subdirectory(src/)
.../lib/lib1/CMakeLists.txt
add_library(lib1 file1.c file2.c)
.../src/CMakeLists.txt
add_executable(test1 test1.c test2.c)
target_link_libraries(test1 lib1)
Why does it work: include_directories are derived in sub-directories, all targets (and thus libraries) from add_subdirectory are exported throughout the whole project.

Related

How to Make Lua in subdirectory of a larger project autoconf?

I have a C project I want to integrate with Lua.
This project has to be built on multiple platforms, so I want to build Lua in-tree with the rest of the C code, instead of depending on the system's Lua installation. Previously, we were using ax_lua macro to configure the system's Lua dependency, but I want to remove it and build Lua with the rest.
Unlike the other parts of the project, Lua already has a Makefile, and I don't want to convert this to Makefile.am just to get it converted back to Makefile.in then Makefile (unless, this is the only way.) Rather, I'd want something to the effect of running make inside the Lua folder and the rest of the build to proceed with the appropriate env vars (LUA_INCLUDE, LUA_FLAGS, LUA) set. To which files (configure.ac or Makefile.am) and what lines should I add to?
project/
lua-5.3.6/
Makefile
src/
a.c
b.c
configure.ac
Makefile.am
...
Rather, I'd want something to the effect of running make inside the
Lua folder and the rest of the build to proceed with the appropriate
env vars (LUA_INCLUDE, LUA_FLAGS, LUA) set. To which files
(configure.ac or Makefile.am) and what lines should I add to?
Unless you are willing to do it manually, it doesn't fit very well to try to build Lua before configuring the project. Moreover, even if you did build Lua manually in advance, unless you also installed it to the build system, it would be pretty optimistic to suppose that the macros from ax_lua would work as intended.
If instead you are content to build Lua via a recursive make during the overall project build, however, then the thing you're looking for is Automake's SUBDIRS variable. As its documentation describes, the subdirectories to be built do not have to be Automake-based. They just have to have makefiles (after configuration). The documentation also lists which targets the top-level makefile might try to build in the subdirectory, but it's not necessarily a showstopper if the subdirectory makefile does not support all of them. You would add this to your Makefile.am:
SUBDIRS = lua-5.3.6
The environment variables are a different question, whose answer depends in part on how the project depends on them. Probably you can just set the one you need (as make variables) in your Makefile.am. Since you are taking control of the Lua build, you can determine the needed values in advance. Everything in your Makefile.am is copied into the configured Makefile, so you don't need to do more to get those variables to the ultimate make.
For example, something along these lines might suffice:
LUA_INCLUDE = -I$(srcdir)/lua-5.3.6
LUA_LIB = lua-5.3.6/liblua-5.3.6.a
You might also consider dumping the variables in favor of just hardcoding the values, which, after all, will no longer vary.

How to install C source files and headers?

I've been given these source files and headers. In the README.md the authors explain how to launch the test executables without the need of a proper installation. It is just a make command to run. They explain how to generate the .so files. I think these latter are meant to be used if I wanted to install the APIs at a system level (the definitions should be in api.h). My question is: where should I copy the shared objects generated by the Makefile and the api.h header? I aim to write a source file from scratch where I use those APIs (e.g. crypto_sign()) just including the headers, if it is possible. Thanks
where should I copy the shared objects generated by the Makefile and the api.h header? I aim to write a source file from scratch where I use those APIs (e.g. crypto_sign()) just including the headers, if it is possible
Nowhere.
The project comes with CMake support. Use CMake in your project and just add_subdirectory the repository directory.
Anyway, if you really wish to install the library system-wide, then FHS specifies directory structure on linux. For local system administration use /usr/local/lib for local libraries .so files and /usr/local/include for local C header files.

How does CMake add_executable work internally (dependencies and automatic source discovery)?

I am working on my first CMake/C project and I crossed a problem with my build that makes me re-question how compilation work.
I would like people to explain to me what exactly happens when you call add_executable and how make builds C file dependencies.
What I thought
I thought that, when calling add_executable (name, sources), CMake would see sources as a superset of the necessary source files to build the target name. So internally, he analyzes the REAL MINIMAL dependencies, by analyzing the file containing main, and recursively adding the included .h files, with their associated declarations in the .c files.
What seem to happen (I want confirmation)
CMake sees sources as the real minimum dependencies for the executable. It seems it will compile ANYTHING in sources, whether or not it is used anywhere.
Consequence
This for me is really annoying. In my project I use source discovery, meaning that anything in the src directory is added to the sources. Then, if I want to compile a unit test (make this_unit_test), it is, in fact, going to compile every .o file in my source directory instead of compiling the necessary files only. Which means that if something does not build in a part of my project, I can not build any tests anymore.
What can I do?
If CMake is indeed done in a way that you need to specify yourself the minimal dependencies for any executable, how can I still use automatic source/test discovery? The best solution would be from the CMakeList file, a function that takes a list of source files, and return the subset of it corresponding to what is actually included at some point by the file containing main. What do people do to resolve that problem?
No, CMake does not scan for minimal source dependencies for your executable, and how could it? CMake doesn't scan the source files, and has no knowledge about what includes/definitions your executable ultimately needs or doesn't need. This functionality would have to happen at the compilation stage (after CMake completes), because it is the pre-processor/compiler that parses your source files and interprets them for semantics (meaning). There are static analysis tools out there that can help achieve something like this, such as include-what-you-use.
Using source discovery techniques in CMake (such as file(GLOB ...)) can be error prone:
As you have seen, the source discovery mechanism may grab files that you do not need for building a particular target.
Even worse, source discovery may leave out files that are required for building a particular target.
The CMake documentation itself even warns against doing this!
Note: We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate.
As a general rule of thumb, it is always safest to list each individual file that is required for each target explicitly, for example:
add_executable(MyExe
main.c
MyHelperFunctions.c
MyOtherStructs.c
)

go install: add non-source files to built package

My $GOPATH looks like this:
src/
mypkg/
source.go
config.txt
bin/
mypkg
pkg/
somestuff/
When I build my package with go install (that builds and places the executable in bin), I'd like config.txt to be copied in that folder together with the executable. Is there a way to do so?
Sorry if I didn't find a way that may look obvious to you, but I'm new to programming, especially to Go.
You can "pack" the static files (text, images etc.) by including it into a .go file (usually automatically generated) and then compiling it into the stand-alone binary. You can do it by using https://github.com/golang/tools/blob/master/godoc/static/makestatic.go or with https://github.com/jteeuwen/go-bindata. See http://blog.ralch.com/tutorial/golang-embedded-resources/ for more information and examples.

best practices on my library coded in C

it is awkward, but until now i always copy the *.h and the *.c files to my projekts location. this is a mess and i want to change it!
i want to build my own c library and have a few questions about it.
where should i locate the *.h files?
should i copy them in the global /usr/include/ folder or should i create my own folder in $HOME (or anywhere else)?
where should i locate the *.a files and the *.o files and where the *.c files.
i am using debian and gcc. my c projects are in $HOME/dev/c/.
i would keep my lib-sources in $HOME/dev/c/lib (if this is the way you could recommend) and copy the *.o, *.a and *.h files to the location i am asking for.
how would you introduce the lib-location to the compiler? should i add it to the $PATH or should i introduce it in the makefiles of my projekt (like -L PATH/TO/LIBRARY -l LIBRARY).
do you have anny further tips and tricks for building a own library?
I would recommend putting the files somewhere in your $HOME directory. Let's say you've created a library called linluk and you want to keep that library under $HOME/dev/c/linluk. That would be your project root.
You'll want a sensible directory structure. My suggestion is to have a lib directory containing your library and an include directory with the header files.
$PROJECT_ROOT/
lib/
liblinluk.so
include/
linluk.h
src/
linluk.c
Makefile
Compiling: When you want to use this library in another project, you'd then add -I$PROJECT_ROOT/include to the compile line so that you could write #include <linluk.h> in the source files.
Linking: You would add -L$PROJECT_ROOT/lib -llinluk to the linker command line to pull in the compiled library at link time. Make sure your .so file has that lib prefix: it should be liblinluk.so, not linluk.so.
A src directory and Makefile are optional. The source code wouldn't be needed by users of the library, though it might be helpful to have it there in case someone wants to remake the .so file.
As I commented, you should try first to build and install from its source code several free software libraries, e.g. like libonion or gdbm or GNU readline etc (don't use any distribution package, but compile and install these from source code). This will teach you some good practice.
You probably want to install these libraries system-wide, not for your particular user. Then, assuming that your library is called foo (you need to give it some name!),
the header files *.h go into /usr/local/include/foo/
the shared objects (dynamic libraries) go into /usr/local/lib/libfoo.so (with perhaps some version numbering)
if relevant, static library go into /usr/local/lib/libfoo.a
You need to add once /usr/local/lib/ into /etc/ld.so.conf and run ldconfig(8)
and you usually don't want to copy any source file *.c or object file *.o
(except for homoiconic programs, bootstrapped compilers, Quine programs, generated C code, etc... See RefPerSys or Jacques Pitrat systems as an incomplete example of self generated C or C++). In some weird cases, you may want to copy such "source" or "generated C" code under /usr/src/.
Read Program Library HowTo (and later, Drepper's paper: How To Write Shared Libraries)
You could consider making your library known by pkg-config. Then install some foo.pc under /usr/local/lib/pkgconfig/
BTW, I strongly suggest you to publish your library as free software, perhaps on github or elsewhere. You'll get useful feedback and could get some help and some bug reports or enhancements.
You probably should use some builder like make and have an install target in your Makefile (hint: make it use the DESTDIR convention).

Resources