File: RefixupMacOS.cmake

package info (click to toggle)
kicad 9.0.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 770,320 kB
  • sloc: cpp: 961,692; ansic: 121,001; xml: 66,428; python: 18,387; sh: 1,010; awk: 301; asm: 292; makefile: 227; javascript: 167; perl: 10
file content (216 lines) | stat: -rw-r--r-- 9,250 bytes parent folder | download | duplicates (3)
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# RefixupMacOS.cmake

# Now that we don't use BundleUtilities and instead use GET_RUNTIME_DEPENDENCIES,
# the binaries that are built all have absolute path library load commands.
# What we need to do here is update all the paths for _the runtime dependencies
# that we installed into the bundle_ to live in @rpath, with Python getting
# special treatment in that it lives in @rpath/Frameworks.

# Make sure GLOB_RECURSE doesn't follow symlinks
cmake_policy( PUSH )
cmake_policy( SET CMP0009 NEW )

function( refix_kicad_bundle target )
    # target should be the path to the kicad.app directory

    string( TIMESTAMP start_time )

    cleanup_python( ${target} )

    file( GLOB_RECURSE items ${target}/*.dylib ${target}/*.so ${target}/*.kiface )

    foreach( item ${items} )
        message( "Refixing prereqs for '${item}'" )
        refix_prereqs( ${item} )
    endforeach( )

    # For binaries, we need to fix the prereqs and the rpaths
    file( GLOB subdirs ${target}/Contents/Applications/*.app )
    foreach( subdir ${subdirs} )
        file( GLOB binaries ${subdir}/Contents/MacOS/* )
        foreach( binary ${binaries} )
            message( "Refixing rpaths and prereqs for '${binary}'" )
            #refix_rpaths( ${binary} )
            refix_prereqs( ${binary} )
        endforeach( )
    endforeach( )

    file( GLOB pythonbinbinaries ${target}/Contents/Frameworks/Python.framework/Versions/3.*/bin/python3 )
    foreach( pythonbinbinary ${pythonbinbinaries} )
        message( "Refixing rpaths and prereqs for '${pythonbinbinary}'" )
        refix_rpaths( ${pythonbinbinary} )
        refix_prereqs( ${pythonbinbinary} )
    endforeach()

    file( GLOB pythonresbinaries ${target}/Contents/Frameworks/Python.framework/Versions/3.*/Resources/Python.app/Contents/MacOS/Python )
    foreach( pythonresbinary ${pythonresbinaries} )
        message( "Refixing rpaths and prereqs for '${pythonresbinary}'" )
        refix_rpaths( ${pythonresbinary} )
        refix_prereqs( ${pythonresbinary} )
    endforeach()

    file( GLOB binaries ${target}/Contents/MacOS/* )
    foreach( binary ${binaries} )
        message( "Refixing prereqs for '${binary}'" )
        refix_prereqs( ${binary} )
    endforeach( )

    message( "Removing Python pyc files" )
    file( GLOB_RECURSE pycs ${target}/*.pyc )
    file( REMOVE ${pycs}  )

    string( TIMESTAMP end_time )
    # message( "Refixing start time: ${start_time}\nRefixing end time: ${end_time}" )
endfunction( )

function( cleanup_python bundle)
    # Remove extra Python
    file( REMOVE_RECURSE ${bundle}/Contents/MacOS/Python )
    file( GLOB extra_pythons LIST_DIRECTORIES true ${bundle}/Contents/Applications/*/Contents/MacOS/Python )

    if( NOT "${extra_pythons}" STREQUAL "" )
        message( "Removing extra Pythons copied into Contents/MacOS: ${extra_pythons}" )
        file( REMOVE_RECURSE ${extra_pythons} )
    endif()

    # Make sure Python's Current is a symlink to 3.x
    file( REMOVE_RECURSE ${bundle}/Contents/Frameworks/Python.framework/Versions/Current )
    file( GLOB python_version LIST_DIRECTORIES true  RELATIVE ${bundle}/Contents/Frameworks/Python.framework/Versions ${bundle}/Contents/Frameworks/Python.framework/Versions/3* )
    execute_process( COMMAND ln -s ${python_version} ${bundle}/Contents/Frameworks/Python.framework/Versions/Current )
endfunction()

function( refix_rpaths binary )
    get_filename_component( executable_path ${binary} DIRECTORY )

    set( desired_rpaths )
    file( RELATIVE_PATH relative_kicad_framework_path ${executable_path} ${target}/Contents/Frameworks )
    string( REGEX REPLACE "/+$" "" relative_kicad_framework_path "${relative_kicad_framework_path}" ) # remove trailing slash
    file( RELATIVE_PATH relative_python_framework_path ${executable_path} ${target}/Contents/Frameworks/Python.framework )
    string( REGEX REPLACE "/+$" "" relative_python_framework_path "${relative_python_framework_path}" ) # remove trailing slash
    list( APPEND desired_rpaths "@executable_path/${relative_kicad_framework_path}" "@executable_path/${relative_python_framework_path}" )

    foreach( desired_rpath ${desired_rpaths} )
        execute_process(
                COMMAND install_name_tool -add_rpath ${desired_rpath} ${binary}
                RESULT_VARIABLE add_rpath_rv
                OUTPUT_VARIABLE add_rpath_ov
                ERROR_VARIABLE add_rpath_ev
        )
        if( NOT add_rpath_rv STREQUAL "0" )
            message( FATAL_ERROR "adding rpath failed: ${add_rpath_rv}\n${add_rpath_ev}" )
        endif( )
    endforeach( )
endfunction( )

function( refix_prereqs target )
    # file(GET_RUNTIME_DEPENDENCIES) does not seem to work properly on libraries, it returns empty
    # results.  So, to figure out which ones we can remap to rpath, we make use of ${items}, which
    # happens to contain all the shared libs we found in the bundle.  This is a big hack, because
    # we're not actually checking that these shared libs live *in* the rpath, but in practice it
    # should work.  If this stops being the case, we can always add more logic...

    execute_process(
            COMMAND otool -L ${target}
            RESULT_VARIABLE gp_rv
            OUTPUT_VARIABLE gp_cmd_ov
            ERROR_VARIABLE gp_ev
    )

    if( NOT gp_rv STREQUAL "0" )
        message( FATAL_ERROR "otool failed: ${gp_rv}\n${gp_ev}" )
    else()
        message( DEBUG "otool -L ${target} returned: ${gp_cmd_ov}" )
    endif( )

    string( REPLACE ";" "\\;" candidates "${gp_cmd_ov}" )
    string( REPLACE "\n" "${eol_char};" candidates "${candidates}" )
    # check for install id and remove it from list, since otool -L can include a
    # reference to itself
    set( gp_install_id )
    execute_process(
            COMMAND otool -D ${target}
            RESULT_VARIABLE otool_rv
            OUTPUT_VARIABLE gp_install_id_ov
            ERROR_VARIABLE otool_ev
    )
    if( NOT otool_rv STREQUAL "0" )
        message( FATAL_ERROR "otool -D failed: ${otool_rv}\n${otool_ev}" )
    endif()
    # second line is install name
    string( REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}" )
    if( gp_install_id )
        # trim
        string( REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}" )
    endif( )

    set( changes "" )

    set( otool_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$" )

    foreach( candidate ${candidates} )
        if( "${candidate}" MATCHES "${gp_regex}" )
            string( REGEX REPLACE "${otool_regex}" "\\1" raw_prereq "${candidate}" )

            message( DEBUG "processing ${raw_prereq}")
            if( raw_prereq MATCHES "^@rpath.*" )
                message( DEBUG "    already an rpath; skipping" )
                continue()
            endif()

            get_filename_component( prereq_name ${raw_prereq} NAME )
            message( DEBUG "    prereq name: ${prereq_name}" )
            set( changed_prereq "" )

            foreach( item ${items} )
                get_filename_component( item_name ${item} NAME )
                if( "${item_name}" STREQUAL "${prereq_name}" )
                    message( DEBUG "    found match at ${item}" )

                    if( item MATCHES "^.*/Contents/PlugIns/.*" )
                        string( REGEX REPLACE "^.*/Contents/PlugIns/(.*)$"
                                "@rpath/../PlugIns/\\1"
                                changed_prereq
                                "${item}" )
                    else()
                        set( changed_prereq "@rpath/${item_name}" )
                    endif()
                endif()
            endforeach()

            if( "${changed_prereq}" STREQUAL "" )
                message( DEBUG "    not found in items; assumed to be system lib" )
                continue()
            endif()

            # Because of the above continue()s, we know we changed the prereq if we're here

            if( raw_prereq STREQUAL gp_install_id )
                set( cmd install_name_tool -id ${changed_prereq} "${target}" )
                message( DEBUG "     updating install id: ${cmd}" )
                execute_process( COMMAND ${cmd} RESULT_VARIABLE install_name_tool_result )
                if( NOT install_name_tool_result EQUAL 0 )
                    string( REPLACE ";" "' '" msg "'${cmd}'" )
                    message( FATAL_ERROR "Command failed setting install id:\n ${msg}" )
                endif( )
                continue( )
            endif( )

            if ( NOT raw_prereq STREQUAL changed_prereq )
                # we know we need to change this prereq
                set( changes ${changes} "-change" "${raw_prereq}" "${changed_prereq}" )
            endif( )
        endif( )
    endforeach( )

    if( changes )
        set( cmd install_name_tool ${changes} "${target}" )
        message( DEBUG "changing prereqs: ${changes}" )
        execute_process( COMMAND ${cmd} RESULT_VARIABLE install_name_tool_result )
        if( NOT install_name_tool_result EQUAL 0 )
            string( REPLACE ";" "' '" msg "'${cmd}'" )
            message( FATAL_ERROR "Command failed:\n ${msg}" )
        endif( )
    endif( )
endfunction( )

cmake_policy( POP )