function(cpsc_homebrew_path input output) set(path "${input}") foreach(prefix "@@HOMEBREW_PREFIX@@" /opt/homebrew /usr/local) string(REPLACE "${prefix}/" "${CPSC_DEPS_ROOT}/" path "${path}") endforeach() string(REPLACE "@@HOMEBREW_CELLAR@@" "${CPSC_DEPS_ROOT}/Cellar" path "${path}") set(${output} "${path}" PARENT_SCOPE) endfunction() function(cpsc_prepare_homebrew_packages) file(GLOB kegs LIST_DIRECTORIES TRUE "${CPSC_DEPS_ROOT}/Cellar/*/*") file(MAKE_DIRECTORY "${CPSC_DEPS_ROOT}/opt") foreach(keg IN LISTS kegs) if(NOT IS_DIRECTORY "${keg}") continue() endif() get_filename_component(rack "${keg}" DIRECTORY) get_filename_component(formula "${rack}" NAME) set(link "${CPSC_DEPS_ROOT}/opt/${formula}") if(EXISTS "${link}") message(FATAL_ERROR "Multiple versions of ${formula} in deps/packages; keep only one") endif() execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${keg}" "${link}" RESULT_VARIABLE result) if(NOT result EQUAL 0) message(FATAL_ERROR "Cannot create local Homebrew prefix for ${formula}") endif() file(GLOB_RECURSE texts "${keg}/*.cmake" "${keg}/*.pc") foreach(path IN LISTS texts) file(READ "${path}" contents) cpsc_homebrew_path("${contents}" contents) file(WRITE "${path}" "${contents}") endforeach() endforeach() # Bottles carry placeholder install names; extraction alone is insufficient. cpsc_find_dependency(PROGRAM cpsc_otool NAMES otool REQUIRED) cpsc_find_dependency(PROGRAM cpsc_install_name_tool NAMES install_name_tool REQUIRED) cpsc_find_dependency(PROGRAM cpsc_codesign NAMES codesign REQUIRED) cpsc_find_dependency(PROGRAM cpsc_brew NAMES brew) if(cpsc_brew) execute_process(COMMAND "${cpsc_brew}" --prefix OUTPUT_VARIABLE system_prefix OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE result) if(NOT result EQUAL 0) message(FATAL_ERROR "Cannot determine the system Homebrew prefix") endif() endif() foreach(keg IN LISTS kegs) file(GLOB binaries "${keg}/bin/*") file(GLOB_RECURSE libraries "${keg}/lib/*.dylib" "${keg}/lib/*.so") foreach(binary IN LISTS binaries libraries) if(IS_SYMLINK "${binary}" OR IS_DIRECTORY "${binary}") continue() endif() execute_process(COMMAND "${cpsc_otool}" -L "${binary}" OUTPUT_VARIABLE linkage ERROR_QUIET RESULT_VARIABLE result) if(NOT result EQUAL 0) continue() # Scripts and data files are not Mach-O binaries. endif() string(REGEX MATCHALL "\n[ \t]+[^ \t\n]+ \\(" references "${linkage}") set(options) foreach(reference IN LISTS references) string(REGEX REPLACE "^\n[ \t]+([^ \t]+) \\($" "\\1" old "${reference}") cpsc_homebrew_path("${old}" new) if(NOT old STREQUAL new) if(NOT EXISTS "${new}" AND system_prefix) string(REPLACE "${CPSC_DEPS_ROOT}/" "${system_prefix}/" system "${new}") if(EXISTS "${system}") set(new "${system}") endif() endif() list(APPEND options -change "${old}" "${new}") endif() endforeach() execute_process(COMMAND "${cpsc_otool}" -l "${binary}" OUTPUT_VARIABLE commands ERROR_QUIET RESULT_VARIABLE result) if(NOT result EQUAL 0) message(FATAL_ERROR "Cannot inspect Mach-O load commands in ${binary}") endif() string(REGEX MATCHALL "cmd LC_RPATH\n[^\n]*\n[ \t]*path [^\n]+" rpaths "${commands}") foreach(rpath IN LISTS rpaths) string(REGEX REPLACE "^cmd LC_RPATH\n[^\n]*\n[ \t]*path (.*) \\(offset [0-9]+\\)$" "\\1" old "${rpath}") cpsc_homebrew_path("${old}" new) if(NOT old STREQUAL new) list(APPEND options -rpath "${old}" "${new}") endif() endforeach() execute_process(COMMAND "${cpsc_otool}" -D "${binary}" OUTPUT_VARIABLE identity ERROR_QUIET) string(REGEX MATCH "\n([^\n]+)" match "${identity}") if(match) cpsc_homebrew_path("${CMAKE_MATCH_1}" new_identity) list(APPEND options -id "${new_identity}") endif() if(options) execute_process(COMMAND "${cpsc_install_name_tool}" ${options} "${binary}" RESULT_VARIABLE result ERROR_VARIABLE error) if(NOT result EQUAL 0) message(FATAL_ERROR "Cannot relocate ${binary}: ${error}") endif() execute_process(COMMAND "${cpsc_codesign}" --force --sign - "${binary}" RESULT_VARIABLE result ERROR_VARIABLE error) if(NOT result EQUAL 0) message(FATAL_ERROR "Cannot sign relocated ${binary}: ${error}") endif() endif() endforeach() endforeach() endfunction()