Skip to content

Instantly share code, notes, and snippets.

@dolinenkov
Last active April 11, 2020 07:39
Show Gist options
  • Save dolinenkov/e5c7154e5f6ab1729746667a8b0b8401 to your computer and use it in GitHub Desktop.
Save dolinenkov/e5c7154e5f6ab1729746667a8b0b8401 to your computer and use it in GitHub Desktop.
CMake function for adding standard layout modules
# universal function for adding a standard layout module
# usage: add_module({MODULE_NAME} [NO_CONSOLE] [DIR direcory] [TYPE executable|library] [DEPENDS target1 target2 ...])
# NO_CONSOLE (Win32): set entry point to WinMain instead of main()
# TYPE: module type, default is library
# DIR: module base directory
# DEPENDS: list of libraries(another modules) to link with
# DEBUG_DIR: debugger startup directory (Visual Studio only)
function(add_module MODULE_NAME)
if(NOT MODULE_NAME)
message("add_module(): module name not specified")
return()
endif()
cmake_parse_arguments(
"ARG"
"NO_CONSOLE;MAKE_DEFAULT"
"TYPE;DIR;DEBUG_DIR"
"DEPENDS"
${ARGN}) # prefix, options, one-valued keywords, multi-valued keywords, input arguments
if(NOT ARG_TYPE)
set(ARG_TYPE "library")
endif()
# default directory
if(NOT ARG_DIR)
message("add_module(${MODULE_NAME}): DIR not specified, using " ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_NAME})
set(ARG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_NAME})
endif()
if(NOT IS_DIRECTORY ${ARG_DIR})
message("add_module(${MODULE_NAME}): module directory doesn't exist")
return()
endif()
set(INCLUDE_DIR ${ARG_DIR}/include)
set(SOURCES_DIR ${ARG_DIR}/sources)
# collect public headers
set(PUBLIC_SOURCES "")
foreach(LOCATION "${INCLUDE_DIR}/${MODULE_NAME}/*.h;${INCLUDE_DIR}/${MODULE_NAME}/*.hh")
set(_PUBLIC_SOURCES "")
file(GLOB_RECURSE _PUBLIC_SOURCES CONFIGURE_DEPENDS ${LOCATION})
foreach(FILENAME ${_PUBLIC_SOURCES})
list(APPEND PUBLIC_SOURCES ${FILENAME})
endforeach()
endforeach()
# collect sources and private headers
set(PRIVATE_SOURCES "")
foreach(LOCATION "${SOURCES_DIR}/*.h;${SOURCES_DIR}/*.hh;${SOURCES_DIR}/*.c;${SOURCES_DIR}/*.cc")
set(_PRIVATE_SOURCES "")
file(GLOB_RECURSE _PRIVATE_SOURCES CONFIGURE_DEPENDS ${LOCATION})
foreach(FILENAME ${_PRIVATE_SOURCES})
list(APPEND PRIVATE_SOURCES ${FILENAME})
endforeach()
endforeach()
# make whole source file list
set(ALL_SOURCES "")
if(PUBLIC_SOURCES)
set(ALL_SOURCES ${ALL_SOURCES} ${PUBLIC_SOURCES})
endif()
if(PRIVATE_SOURCES)
set(ALL_SOURCES ${ALL_SOURCES} ${PRIVATE_SOURCES})
endif()
if(NOT ALL_SOURCES)
message("add_module(${MODULE_NAME}): no sources found, skipping")
return()
endif()
# add target
if(ARG_TYPE STREQUAL "executable")
if(NO_CONSOLE)
set(ALL_SOURCES WIN32 ${ALL_SOURCES})
endif()
add_executable(${MODULE_NAME} ${ALL_SOURCES})
if(ARG_DEBUG_DIR)
if(MSVC_IDE)
# Visual Studio: debug launches at root directory
set_target_properties(${MODULE_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${ARG_DEBUG_DIR})
else()
message("add_module(${MODULE_NAME}: DEBUG_DIR is not supported by current generator, ignoring")
endif()
endif()
elseif(ARG_TYPE STREQUAL "library")
add_library(${MODULE_NAME} ${ALL_SOURCES})
target_include_directories(${MODULE_NAME} PUBLIC ${INCLUDE_DIR})
else()
message("add_module(${MODULE_NAME}): unknown type: ${ARG_TYPE}, skipping")
return()
endif()
# Visual Studio: mark project as startup
if(ARG_MAKE_DEFAULT)
if(ARG_TYPE STREQUAL "executable")
set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT "${MODULE_NAME}")
else()
message("add_module(${MODULE_NAME}): only executable target can be marked as default")
endif()
endif()
# configure dependencies
if(ARG_DEPENDS)
set(LINK_PUBLIC_ARGS "")
set(LINK_INTERFACE_ARGS "")
set(DEPEND_ARGS "")
foreach(DEPENDENCY ${ARG_DEPENDS})
message(${DEPENDENCY})
if(TARGET ${DEPENDENCY})
get_target_property(TARGET_TYPE ${DEPENDENCY} TYPE)
# handle linking
if(${TARGET_TYPE} STREQUAL "EXECUTABLE")
#?
elseif(${TARGET_TYPE} STREQUAL "INTERFACE_LIBRARY")
list(APPEND LINK_INTERFACE_ARGS ${DEPENDENCY})
elseif(${TARGET_TYPE} STREQUAL "STATIC_LIBRARY" OR ${TARGET_TYPE} STREQUAL "SHARED_LIBRARY" OR ${TARGET_TYPE} STREQUAL "MODULE_LIBRARY")
list(APPEND LINK_PUBLIC_ARGS ${DEPENDENCY})
endif()
# handle regular dependencies
list(APPEND DEPEND_ARGS ${DEPENDENCY})
else()
message("add_module(${MODULE_NAME}): '${TARGET}' is not a valid target name, skipping")
endif()
endforeach()
# build link libraries list
set(LINK_ARGS "")
if(LINK_PUBLIC_ARGS)
set(LINK_ARGS PUBLIC ${LINK_PUBLIC_ARGS})
endif()
if(LINK_INTERFACE_ARGS)
set(LINK_ARGS INTERFACE ${LINK_INTERFACE_ARGS})
endif()
if(LINK_ARGS)
target_link_libraries(${MODULE_NAME} ${LINK_ARGS})
endif()
# build dependency list
if(DEPEND_ARGS)
add_dependencies(${MODULE_NAME} ${DEPEND_ARGS})
endif()
endif()
endfunction()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment