I’m working on my first CMake project, a C library.
I’ll ask for help and suggestions as I make progress (like an “art review”, but for CMake)
I’ve read plenty of material on the subject (documentation included, of course, it’s pretty good), but nevertheless I keep discovering better more modern alternatives everywhere, or I can’t find material at all, so I came to this forum to build a solid understanding of what I need (and why I need it) and very useful “how I would have done that, and why” from experts.
The project layout I came up with is the following:
visualt
│ CMakeLists.txt (1)
│ Doxyfile
│ Doxyfile.in // info such as current version are provided by CMake
│
├───src
│ CMakeLists.txt (2)
│ version.h // i found very ugly to put the generated header in the build directory, i'd rather put it in the same directory, and use it normally. i don't get why should it be in other places.
│ version.h.in // info such as version and build date are provided by CMake
│ visualt.c
│
├───include
│ └───visualt
│ visualt.h
│ visualtUnprefixed.h // alternative header, a wrapper around "visualt.h"
│
├───res // diagnostic and assets for development
│ CMakeLists.txt (3) // target for test.c
│ test.c // not a "test" in the classical meaning, it's just a diagnostic program
│ ...
│
└───examples // small programs that uses the library (with it's own "target_link_libraries()")
│ CMakeLists.txt (4) // adds every subdirectory
│
├───01
│ │ CMakeLists.txt (6) // target for example01.c
│ │ example01 // the target's RUNTIME_OUTPUT_DIRECTORY is set as CMAKE_CURRENT_LIST_DIR, because the executable might require files like "cat.obj"
│ │ example01.c
│ │
│ └───res
│ cat.obj
│ cat.txt
│
├───... // the same folder structure is repeated for every example
│
└───getch
CMakeLists.txt (5) // an INTERFACE library (header only) used by examples
getch.h
I’m stuck on: Installing
Here’s CMakeLists.txt (2), still incomplete:
# set headers
set(private_headers_path "${VisualT_SOURCE_DIR}/src")
set(public_headers_path "${VisualT_SOURCE_DIR}/include")
set(private_headers
"${private_headers_path}/version.h")
set(public_headers
"${public_headers_path}/visualt/visualt.h"
"${public_headers_path}/visualt/visualtUnprefixed.h")
# set source files
set(srcs "visualt.c")
add_library(visualt ${private_headers} ${public_headers} ${srcs})
target_include_directories(visualt
PRIVATE ${private_headers_path}
PUBLIC ${public_headers_path})
set_target_properties(visualt PROPERTIES
PRIVATE_HEADER "${private_headers}"
PUBLIC_HEADER "${public_headers}")
target_compile_features(visualt PUBLIC c_std_99)
here’s CMakeLists.txt (1):
# Works with 3.11 and tested through 3.16
cmake_minimum_required(VERSION 3.11...3.16)
# Project name and a few useful settings. Other commands can pick up the results
project(VisualT
VERSION 3.2.0
DESCRIPTION "a text-based graphic library"
LANGUAGES C)
if("${CMAKE_PROJECT_NAME}" STREQUAL "VisualT")
string(TIMESTAMP VisualT_BUILD_DATE "%d %B %Y" UTC)
configure_file(
"${VisualT_SOURCE_DIR}/src/version.h.in"
"${VisualT_SOURCE_DIR}/src/version.h"
@ONLY
)
configure_file(
"${VisualT_SOURCE_DIR}/Doxyfile.in"
"${VisualT_SOURCE_DIR}/Doxyfile"
@ONLY
)
endif()
# library code is here
add_subdirectory(src)
# examples are here
add_subdirectory(examples)
# tests and resources are here
add_subdirectory(res)
if(${CMAKE_})
install(TARGETS visualt
EXPORT VisualT
RUNTIME DESTINATION bin #temporary
LIBRARY DESTINATION lib #temporary
ARCHIVE DESTINATION lib #temporary
PUBLIC_HEADER DESTINATION include)
install(EXPORT my_library DESTINATION "${lib_dest}")
-
As you can see I made an attempt to detect if the project is being compiled as the “main project”, or as a subProject. I have no idea if this is the right way to do that. Perhaps I should use
CMAKE_BUILD_TYPE
to discern? -
I’ve used
PRIVATE_HEADER
andPUBLIC_HEADER
properties to avoid usinginstall(FILE)
. But the main problem remains: I have no idea where I should put the files. I’ve read many discussion about this and I decided to discard the idea of having “one path for all” systems, and I decided instead to make it os-dependent.(picked from here) I guess I should use anif
overCMAKE_SYSTEM_NAME
's value? -
I’d like to use
components
to modularize a bit the installation (like for the examples, and make themOPTIONAL
), but I don’t know how they correlate with export groups. What are the differences? -
I’ve seen the INCLUDES DESTINATION but I didn’t get it. What are the differences with
target_include_directories()
? I understood that they are applied only on the exported target? how? why?
That’s all for now, I hope to untangle everything step by step. In the meantime I’ll keep reading. Thank you!