In the multi-platform/os project I'm working on, I'm striving to simplify the platform specific code into subdirectories, with a common directory holding a common implementation. I have a prototype implementation in autotools, but want to move to a cmake implementation if possible. My current sticking point is how to support multiple OSes. My current filesystem structure looks like the following:
/* A common header file for all platforms that presents an interface */
include/my_socket_utils.h
include/my_time_utils.h
/* Platform specific source files */
src/common/my_socket_utils.cpp
src/linux/my_socket_utils.cpp
src/vxworks/my_socket_utils.cpp
src/qnx/my_socket_utils.cpp
src/common/my_time_utils.cpp
src/vxworks/my_time_utils.cpp
The idea is that there is a common interface and a "common" implementation. The implementation is either a stub or a common implementation written to a posix standard that allows it to work for most platforms. Those platforms that require a custom implementation MAY override the common one, but it's optional.
With autotools, I am able to achieve this using VPATH to set a source tree hierarchy, so I set:
VPATH=@srcdir@/src/@target_platform@;@srcdir@/src/common
This makes autotools look for the source file in src/@target_platform@ first, then, if it wasn't found, grab it from src/common.
What is the cmake way to do this?
Update: To help all those lost souls in need, this is what I ended up doing for the time being. I'm not sure it's the best solution, but it works well enough.
FILE(GLOB common_files "src/common/.c") FILE(GLOB platform_files "src/${os}/.c)
Then, do the dirty n^2 algorithm to override. Not sure how to do any better in cmake "script", but the number of files is low, so it's plenty fast. The diag messages are, of course, optional.
#
# For each common file, check to see if a platform file exists to override it.
#
foreach(fqfn ${common_files})
set(platform_override FALSE)
get_filename_component(filename ${fqfn} NAME)
#
# If filename exists in platform, override it with the platform,
# otherwise fall back to the common implementation. Oh for a real
# language.
#
foreach(platform_fqfn ${platform_files})
get_filename_component(platform_filename ${platform_fqfn} NAME)
message("pf=${platform_filename} cf=${filename}")
if(filename STREQUAL platform_filename)
message("filename == platform_filename")
list(APPEND proj_files ${platform_fqfn})
set(platform_override TRUE)
endif(filename STREQUAL platform_filename)
endforeach(platform_fqfn ${platform_files})
if(NOT ${platform_override})
list(APPEND proj_files ${fqfn})
message("Appended ${fqfn}")
endif(NOT ${platform_override})
endforeach(fqfn ${common_files})
message("proj_files=${proj_files}")
add_executable (cc_dfi_main ${proj_files})
One possible way is to define variable TARGET_BUILD_PLATFORM
and set it to exact platform you want to build (linux/qnx/vxworks).
set(PROJECT_NAME some_name_for_project)
project(${PROJECT_NAME} CXX)
file(GLOB COMMON_SRC ${PROJECT_SOURCE_DIR}/common/*.cpp)
file(GLOB PLATFORM_SRC ${PROJECT_SOURCE_DIR}/${TARGET_BUILD_PLATFORM}/*.cpp)
set(SRC_FILES ${COMMON_SRC} ${PLATFORM_SRC})
add_executable(${PROJECT_NAME} ${SRC_FILES})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With