@@ -71,7 +71,7 @@ enum AppImageBundler: Bundler {
71
71
{
72
72
Self . copyDynamicLibraryDependencies (
73
73
of: appExecutable,
74
- to: appDir. appendingPathComponent ( " usr/bin " )
74
+ to: appDir. appendingPathComponent ( " usr/lib " )
75
75
)
76
76
} ,
77
77
{ Self . createSymlinks ( at: appDir, appName: context. appName) } ,
@@ -91,59 +91,96 @@ enum AppImageBundler: Bundler {
91
91
92
92
// MARK: Private methods
93
93
94
+ /// Copies dynamic library dependencies of the specified executable file into
95
+ /// the `AppDir`, and updates the runpaths of the executable and moved dynamic
96
+ /// libraries accordingly.
94
97
private static func copyDynamicLibraryDependencies(
95
98
of appExecutable: URL ,
96
99
to destination: URL
97
100
) -> Result < Void , AppImageBundlerError > {
101
+ log. info ( " Copying dynamic library dependencies of main executable " )
98
102
return Process . create (
99
103
" ldd " ,
100
104
arguments: [ appExecutable. path] ,
101
105
runSilentlyWhenNotVerbose: false
102
- )
103
- . getOutput ( )
104
- . mapError { error in
105
- . failedToEnumerateDynamicDependencies( error)
106
- }
107
- . flatMap { output in
108
- let lines = output. split ( separator: " \n " )
109
- for line in lines {
110
- guard let libraryPath = try ? lddLineParser. parse ( line) else {
111
- continue
112
- }
113
-
114
- let libraryURL = URL ( fileURLWithPath: libraryPath)
115
- let destination = destination. appendingPathComponent (
116
- libraryURL. lastPathComponent
117
- )
118
-
119
- #if os(Linux)
120
- // URL.resolvingSymlinksInPath is broken on Linux
121
- let resolvedLibraryPath = String ( unsafeUninitializedCapacity: 4097 ) { buffer in
122
- realpath ( libraryPath, buffer. baseAddress)
123
- return strlen ( UnsafePointer ( buffer. baseAddress!) )
106
+ ) . getOutput ( )
107
+ . mapError { error in
108
+ . failedToEnumerateDynamicDependencies( error)
109
+ }
110
+ . flatMap { ( output: String ) -> Result < Void , AppImageBundlerError > in
111
+ // Parse the output of ldd line-by-line and copy the located libraries to
112
+ // the destination directory (updating runpaths appropriately).
113
+ let lines = output. split ( separator: " \n " )
114
+ for line in lines {
115
+ guard let libraryPath = try ? lddLineParser. parse ( line) else {
116
+ continue
124
117
}
125
- let resolvedLibraryURL = URL ( fileURLWithPath: resolvedLibraryPath)
126
- #else
127
- let resolvedLibraryURL = libraryURL. resolvingSymlinksInPath ( )
128
- #endif
129
-
130
- do {
131
- try FileManager . default. copyItem (
132
- at: resolvedLibraryURL,
133
- to: destination
118
+
119
+ let libraryURL = URL ( fileURLWithPath: libraryPath)
120
+ let destination = destination. appendingPathComponent (
121
+ libraryURL. lastPathComponent
134
122
)
135
- } catch {
136
- return . failure(
137
- . failedToCopyDynamicLibrary(
138
- source: libraryURL,
139
- destination: destination,
140
- error
123
+
124
+ // Resolve symlinks in case the library itself is a symlinnk (we want
125
+ // to copy the actual library not the symlink).
126
+ #if os(Linux)
127
+ // URL.resolvingSymlinksInPath is broken on Linux
128
+ let resolvedLibraryPath = String ( unsafeUninitializedCapacity: 4097 ) { buffer in
129
+ realpath ( libraryPath, buffer. baseAddress)
130
+ return strlen ( UnsafePointer ( buffer. baseAddress!) )
131
+ }
132
+ let resolvedLibraryURL = URL ( fileURLWithPath: resolvedLibraryPath)
133
+ #else
134
+ let resolvedLibraryURL = libraryURL. resolvingSymlinksInPath ( )
135
+ #endif
136
+
137
+ // Copy the library to the provided destination directory.
138
+ do {
139
+ try FileManager . default. copyItem (
140
+ at: resolvedLibraryURL,
141
+ to: destination
141
142
)
143
+ } catch {
144
+ return . failure(
145
+ . failedToCopyDynamicLibrary(
146
+ source: libraryURL,
147
+ destination: destination,
148
+ error
149
+ )
150
+ )
151
+ }
152
+
153
+ // Update the library's runpath so that it only looks for its dependencies in
154
+ // the current directory (before falling back to the system wide default runpath).
155
+ switch PatchElfTool . setRunpath ( of: destination, to: " $ORIGIN " ) {
156
+ case . success:
157
+ break
158
+ case . failure( let error) :
159
+ return . failure(
160
+ . failedToCopyDynamicLibrary(
161
+ source: libraryURL,
162
+ destination: destination,
163
+ error
164
+ )
165
+ )
166
+ }
167
+ }
168
+ return . success( )
169
+ }
170
+ . flatMap { ( _: Void ) -> Result < Void , AppImageBundlerError > in
171
+ // Update the main executable's runpath
172
+ guard
173
+ let relativeDestination = destination. relativePath (
174
+ from: appExecutable. deletingLastPathComponent ( )
142
175
)
176
+ else {
177
+ return . failure( . failedToUpdateMainExecutableRunpath( executable: appExecutable, nil ) )
143
178
}
179
+ return PatchElfTool . setRunpath ( of: appExecutable, to: " $ORIGIN/ \( relativeDestination) " )
180
+ . mapError { error in
181
+ . failedToUpdateMainExecutableRunpath( executable: appExecutable, error)
182
+ }
144
183
}
145
- return . success( )
146
- }
147
184
}
148
185
149
186
private static func copyResources(
@@ -212,13 +249,15 @@ enum AppImageBundler: Bundler {
212
249
// bundlers.
213
250
let appDir = outputDirectory. appendingPathComponent ( " \( appName) .AppDir " )
214
251
let binDir = appDir. appendingPathComponent ( " usr/bin " )
252
+ let libDir = appDir. appendingPathComponent ( " usr/lib " )
215
253
let iconDir = appDir. appendingPathComponent ( " usr/share/icons/hicolor/1024x1024/apps " )
216
254
217
255
do {
218
256
if fileManager. itemExists ( at: appDir, withType: . directory) {
219
257
try fileManager. removeItem ( at: appDir)
220
258
}
221
259
try fileManager. createDirectory ( at: binDir)
260
+ try fileManager. createDirectory ( at: libDir)
222
261
try fileManager. createDirectory ( at: iconDir)
223
262
return . success( )
224
263
} catch {
0 commit comments