aboutsummaryrefslogtreecommitdiff
path: root/cmake/HomebrewPackages.cmake
blob: cfd8d61d71e5bc3b8a854283783b4ab6770c2631 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

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()