Skip to content

Commit 8805d50

Browse files
committed
Add public headers only target and umbrella header for shared libraries. Add ability demo and convenience function to link against the public headers target for demos (they'll be excluded if
1 parent f50dcd0 commit 8805d50

4 files changed

Lines changed: 98 additions & 3 deletions

File tree

cmake/idi/functions/framework/idi_init.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ macro(idi_init)
6666
set("${IDICMAKE_PREFIX}_USE_BUILD_TIMESTAMPS" 0 CACHE BOOL "Set the current time for the build as build info. Disabled by default as it can increase build time during development.")
6767
set("${IDICMAKE_PREFIX}_DO_TEMPLATE_COMPONENT_TEST" 0 CACHE BOOL "Generate unit test template component and build unit tests for template.")
6868
set("${IDICMAKE_PREFIX}_FORCE_PIC" 0 CACHE BOOL "Force the use of Position Independent Code (PIC). This is useful if this library is a static library being included in a shared library.")
69+
set("${IDICMAKE_PREFIX}_GENERATE_UMBRELLA_HEADER" 1 CACHE BOOL "Generate a single umbrella header that includes all public headers.")
6970
set("${IDICMAKE_PREFIX}_CI_GIT_BRANCH_NAME" "" CACHE STRING "The branch name of the git repo. If not set it will be interogated from Git itself, but could result in a value of HEAD.")
7071

7172
include(${PROJECT_SOURCE_DIR}/platform-compile-options.cmake)
@@ -79,6 +80,7 @@ macro(idi_init)
7980
set("IDICMAKE_DO_TEMPLATE_COMPONENT_TEST" "${${IDICMAKE_PREFIX}_DO_TEMPLATE_COMPONENT_TEST}")
8081
set("IDICMAKE_CI_GIT_BRANCH_NAME" "${${IDICMAKE_PREFIX}_CI_GIT_BRANCH_NAME}")
8182
set("IDICMAKE_FORCE_PIC" "${${IDICMAKE_PREFIX}_FORCE_PIC}")
83+
set("IDICMAKE_GENERATE_UMBRELLA_HEADER" "${${IDICMAKE_PREFIX}_GENERATE_UMBRELLA_HEADER}")
8284

8385
if(IDICMAKE_APP_NAMESPACE)
8486
set(__idi_namespace "${IDICMAKE_VENDOR_NAMESPACE}::${IDICMAKE_APP_NAMESPACE}")

cmake/idi/functions/framework/idi_src.cmake

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ macro(idi_src)
1212
#####################################################################
1313
# CORE LIBRARY #
1414
#####################################################################
15-
if (IDICMAKE_IS_LIBRARY AND NOT IDICMAKE_IS_SHARED)
15+
if(IDICMAKE_IS_LIBRARY AND NOT IDICMAKE_IS_SHARED)
1616
set(IDICMAKE_CORE "${IDICMAKE_PROJECT_NAME}")
1717
else()
1818
set(IDICMAKE_CORE "${IDICMAKE_PROJECT_NAME}_core")
@@ -26,6 +26,27 @@ macro(idi_src)
2626
idi_target_compile_settings("${IDICMAKE_CORE}")
2727
set_target_properties("${IDICMAKE_CORE}" PROPERTIES LINKER_LANGUAGE CXX)
2828

29+
#####################################################################
30+
# PUBLIC CONSUMER INCLUDE TRACKING #
31+
#####################################################################
32+
if(IDICMAKE_IS_SHARED)
33+
# Components that register public headers will append their
34+
# include/ base directory to this global property. After all
35+
# components are processed the _public consumer target uses it.
36+
define_property(GLOBAL PROPERTY IDICMAKE_PUBLIC_INCLUDE_DIRS
37+
BRIEF_DOCS "Include dirs of components that expose public headers"
38+
FULL_DOCS "Accumulated by idi_add_public_includes(); consumed by the _public interface target.")
39+
set_property(GLOBAL PROPERTY IDICMAKE_PUBLIC_INCLUDE_DIRS "")
40+
41+
# Relative paths of every public header
42+
# (e.g. <project>/public/some_header.h).
43+
# Used to generate the umbrella header.
44+
define_property(GLOBAL PROPERTY IDICMAKE_PUBLIC_HEADER_RELPATHS
45+
BRIEF_DOCS "Relative include paths of public headers"
46+
FULL_DOCS "Accumulated by idi_add_public_includes(); used to generate the umbrella header.")
47+
set_property(GLOBAL PROPERTY IDICMAKE_PUBLIC_HEADER_RELPATHS "")
48+
endif()
49+
2950
#####################################################################
3051
# CORE COMPONENTS #
3152
#####################################################################
@@ -72,10 +93,49 @@ macro(idi_src)
7293
"${CMAKE_CURRENT_LIST_DIR}/main/dll_main.cpp"
7394
)
7495
install(TARGETS "${IDICMAKE_SHARED_NAME}"
75-
LIBRARY FILE_SET HEADERS DESTINATION includes/${IDICMAKE_PROJECT_NAME}/public)
96+
LIBRARY FILE_SET HEADERS DESTINATION includes/${IDICMAKE_PROJECT_NAME}/public)
7697
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/main")
7798

99+
# Consumer-facing INTERFACE target that only exposes include
100+
# dirs from components that registered public headers via
101+
# idi_add_public_includes(). Demos and external projects can
102+
# link against this to verify they do not depend on internal
103+
# headers from non-public components.
104+
add_library("${IDICMAKE_PROJECT_NAME}_public" INTERFACE)
105+
target_link_libraries("${IDICMAKE_PROJECT_NAME}_public" INTERFACE
106+
"$<LINK_ONLY:${IDICMAKE_PROJECT_NAME}>")
107+
get_property(_pub_inc_dirs GLOBAL PROPERTY IDICMAKE_PUBLIC_INCLUDE_DIRS)
108+
foreach(_dir IN LISTS _pub_inc_dirs)
109+
target_include_directories("${IDICMAKE_PROJECT_NAME}_public" INTERFACE "${_dir}")
110+
endforeach()
111+
112+
# Generate umbrella header: <project>/<project>.h
113+
if(IDICMAKE_GENERATE_UMBRELLA_HEADER)
114+
set(_umbrella_dir "${CMAKE_BINARY_DIR}/generated")
115+
set(_umbrella_file "${_umbrella_dir}/${IDICMAKE_PROJECT_NAME}/${IDICMAKE_PROJECT_NAME}.h")
116+
get_property(_pub_rels GLOBAL PROPERTY IDICMAKE_PUBLIC_HEADER_RELPATHS)
117+
set(_umbrella_content "/* Auto-generated umbrella header — do not edit. */\n")
118+
string(APPEND _umbrella_content "#pragma once\n\n")
119+
foreach(_rel IN LISTS _pub_rels)
120+
# Skip internal configure outputs (__ prefixed filenames)
121+
get_filename_component(_fname "${_rel}" NAME)
122+
string(SUBSTRING "${_fname}" 0 2 _prefix)
123+
if(NOT _prefix STREQUAL "__")
124+
string(APPEND _umbrella_content "#include \"${_rel}\"\n")
125+
endif()
126+
endforeach()
127+
file(MAKE_DIRECTORY "${_umbrella_dir}/${IDICMAKE_PROJECT_NAME}")
128+
file(WRITE "${_umbrella_file}" "${_umbrella_content}")
129+
target_include_directories("${IDICMAKE_PROJECT_NAME}_public" INTERFACE "${_umbrella_dir}")
130+
131+
# Install the umbrella header alongside the other public headers
132+
install(FILES "${_umbrella_file}"
133+
DESTINATION includes/${IDICMAKE_PROJECT_NAME}/${IDICMAKE_PROJECT_NAME})
134+
message(STATUS "------ Generated umbrella header ${IDICMAKE_PROJECT_NAME}/${IDICMAKE_PROJECT_NAME}.h")
135+
endif()
136+
78137
message(STATUS "------ Added SHARED Library ${IDICMAKE_PROJECT_NAME}")
138+
message(STATUS "------ Added PUBLIC consumer target ${IDICMAKE_PROJECT_NAME}_public")
79139
else()
80140
message(STATUS "------ Added STATIC Library ${IDICMAKE_PROJECT_NAME}")
81141
endif()

cmake/idi/functions/idi_add_sources.cmake

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,17 @@ function(idi_add_public_includes)
6565
# - core_public_includes: used by shared-library installs (public only)
6666
target_sources("${CURRENT_LIBRARY_NAME}" PUBLIC FILE_SET core_includes TYPE HEADERS BASE_DIRS ${CURRENT_LIBRARY_DIR}/include FILES ${FILE_LIST})
6767
target_sources("${CURRENT_LIBRARY_NAME}" PUBLIC FILE_SET core_public_includes TYPE HEADERS BASE_DIRS ${CURRENT_LIBRARY_DIR}/include FILES ${FILE_LIST})
68+
69+
# Record this component's include/ base directory so the _public
70+
# consumer target can expose it. Only components that call
71+
# idi_add_public_includes() with actual files will be recorded.
72+
if(IDICMAKE_IS_SHARED AND FILE_LIST)
73+
set_property(GLOBAL APPEND PROPERTY IDICMAKE_PUBLIC_INCLUDE_DIRS
74+
"${CURRENT_LIBRARY_DIR}/include")
75+
# Also record relative paths for umbrella-header generation.
76+
foreach(_hdr IN LISTS FILE_LIST)
77+
file(RELATIVE_PATH _rel "${CURRENT_LIBRARY_DIR}/include" "${_hdr}")
78+
set_property(GLOBAL APPEND PROPERTY IDICMAKE_PUBLIC_HEADER_RELPATHS "${_rel}")
79+
endforeach()
80+
endif()
6881
endfunction()

cmake/idi/functions/idi_demo.cmake

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function(__idi_demo demo_name demo_file)
2020
set(ADD_MODE "ADDITIONAL_SOURCES")
2121
set(ADD_CORE true)
2222
set(ADD_CATCH true)
23+
set(USE_PUBLIC false)
2324

2425
foreach(var IN LISTS ARGN)
2526
if(var STREQUAL "ADDITIONAL_LIBRARIES")
@@ -34,6 +35,8 @@ function(__idi_demo demo_name demo_file)
3435
set(ADD_CATCH false)
3536
elseif(var STREQUAL "EXCLUDE_CORE")
3637
set(ADD_CORE false)
38+
elseif(var STREQUAL "PUBLIC_ONLY")
39+
set(USE_PUBLIC true)
3740
else()
3841
if(ADD_MODE STREQUAL "ADDITIONAL_LIBRARIES")
3942
target_link_libraries("${CURRENT_DEMO}" PUBLIC "${IDICMAKE_PROJECT_NAME}_${var}")
@@ -55,7 +58,11 @@ function(__idi_demo demo_name demo_file)
5558
endif()
5659

5760
if(ADD_CORE)
58-
target_link_libraries("${CURRENT_DEMO}" PUBLIC "${IDICMAKE_CORE}")
61+
if(USE_PUBLIC AND TARGET "${IDICMAKE_PROJECT_NAME}_public")
62+
target_link_libraries("${CURRENT_DEMO}" PUBLIC "${IDICMAKE_PROJECT_NAME}_public")
63+
else()
64+
target_link_libraries("${CURRENT_DEMO}" PUBLIC "${IDICMAKE_CORE}")
65+
endif()
5966
list(APPEND __LIBRARY_LIST ${CURRENT_DEMO})
6067
list(APPEND __LIBRARY_LIST ${IDICMAKE_CORE})
6168
endif()
@@ -71,3 +78,16 @@ endfunction()
7178
macro(idi_demo demo_name demo_file)
7279
__idi_demo(${demo_name} ${demo_file} ${ARGN})
7380
endmacro()
81+
82+
# Convenience wrapper that links against the _public consumer target
83+
# instead of the core target. This restricts the demo to only the
84+
# public API headers, matching what an installed consumer would see.
85+
# The demo is skipped entirely when not building a shared library,
86+
# since the _public target only exists in shared mode.
87+
macro(idi_demo_public demo_name demo_file)
88+
if(IDICMAKE_IS_SHARED AND TARGET "${IDICMAKE_PROJECT_NAME}_public")
89+
__idi_demo(${demo_name} ${demo_file} PUBLIC_ONLY ${ARGN})
90+
else()
91+
message(STATUS "------ Skipping public-only demo '${demo_name}': not building as a shared library")
92+
endif()
93+
endmacro()

0 commit comments

Comments
 (0)