Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to preserve directory layout of public headers when generating macOS frameworks

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?

like image 953
RAM Avatar asked Oct 19 '25 05:10

RAM


1 Answers

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}")
like image 158
vre Avatar answered Oct 21 '25 22:10

vre