I have a CMake project for a C++ library (current version, CMake v3.21) which I'm in the process of porting to macOS, and I'm looking into generating the library as a OSX Framework.
So far I've followed CMake's documentation on add_library, and added the FRAMEWORK
property to the library target as per CMake's documentation on its FRAMEWORK target property. This seems to mostly work, except that header files exported as part of its PUBLIC_HEADER
property have their directory structure stripped and flattened from the export.
To illustrate this problem, let's consider the following project tree:
include
└── A
├── A.hpp
└── subA
└── SubA.hpp
src
├── A.cpp
└── SubA.cpp
The following CMake project generates a OSX framework when compiled on macOS:
cmake_minimum_required(VERSION 3.21)
project(libA LANGUAGES CXX)
set(a_INCLUDES
include/A/A.hpp
include/A/subA/SubA.hpp
)
set(a_SOURCES
src/A.cpp
src/SubA.cpp
)
add_library(a
${a_INCLUDES}
${a_SOURCES}
)
set_target_properties(a
PROPERTIES
FRAMEWORK YES
PUBLIC_HEADER "${a_INCLUDES}"
)
target_include_directories(a
PUBLIC
${CMAKE_SOURCE_DIR}/include
)
However, the resulting framework flattens the directory structure of the header files included in its Headers
, as shown below:
build/a.framework
├── Headers -> Versions/Current/Headers
├── Resources -> Versions/Current/Resources
├── Versions
│ ├── A
│ │ ├── Headers
│ │ │ ├── A.hpp
│ │ │ └── SubA.hpp
│ │ ├── Resources
│ │ │ └── Info.plist
│ │ └── a
│ └── Current -> A
└── a -> Versions/Current/a
So, here are my questions: does CMake suppport preserving the directory tree layout of public headers shipped as part of a OSX framework? If it does, what do we need to do in a project in order to ensure that the directory tree layout is preserved?
While you would expect to have your public and private headers placed including their relative path under the Headers
directory this is not the case as you experienced. The entire path components are stripped. The same applies to hierarchically structured resources in the Resource
directory.
What you can do to achieve this is to set the MACOSX_PACKAGE_LOCATION
property on every single header using the set_source_files_properties
command. The location is the path to the header plus a Headers
prefix, e.g. Headers/A
for your A/A.hpp
header and Headers/A/subA
for your A/subA/SubA.hpp
header.
Typically projects use a small convenience function for this that strips unwanted prefixes and file names from the given headers and add the install prefix.
function(set_macosx_properties _file_prefixes _install_prefix _source_files)
foreach(_file ${_source_files})
get_filename_component(_loc "${_file}" DIRECTORY)
foreach(_prefix ${_file_prefixes})
string(REPLACE "${_prefix}" "" _loc "${_loc}")
endforeach()
set_source_files_properties(${_file} PROPERTIES MACOSX_PACKAGE_LOCATION ${_install_prefix}${_loc})
endforeach()
endfunction()
Dependent on how your header and resource files are give you would use it like this:
set_macosx_properties("include" "Headers" "${a_INCLUDES}")
set_macosx_properties("data" "Resources" "${a_RESOURCES}")
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