Skip to content

Commit 19af7a5

Browse files
committed
Add SourceArchiver that uses tar command
1 parent edfa76d commit 19af7a5

File tree

3 files changed

+75
-47
lines changed

3 files changed

+75
-47
lines changed

Sources/PackageRegistry/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
add_library(PackageRegistry
1010
Registry.swift
1111
RegistryConfiguration.swift
12-
RegistryManager.swift)
12+
RegistryManager.swift
13+
SourceArchiver.swift)
1314
target_link_libraries(PackageRegistry PUBLIC
1415
TSCBasic
1516
PackageLoading

Sources/PackageRegistry/RegistryManager.swift

Lines changed: 1 addition & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public enum RegistryError: Error {
3333

3434
public final class RegistryManager {
3535
internal static var archiverFactory: (FileSystem) -> Archiver = { fileSystem in
36-
return ZipInPlaceArchiver(fileSystem: fileSystem)
36+
return SourceArchiver(fileSystem: fileSystem)
3737
}
3838

3939
private static let sharedClient: HTTPClientProtocol = HTTPClient()
@@ -295,48 +295,3 @@ private extension URLComponents {
295295
path += (path.last == "/" ? "" : "/") + components.joined(separator: "/")
296296
}
297297
}
298-
299-
// FIXME: Update existing archiver to support `-j` argument
300-
private struct ZipInPlaceArchiver: Archiver {
301-
public var supportedExtensions: Set<String> { ["zip"] }
302-
303-
/// The file-system implementation used for various file-system operations and checks.
304-
private let fileSystem: FileSystem
305-
306-
/// Creates a `ZipArchiver`.
307-
///
308-
/// - Parameters:
309-
/// - fileSystem: The file-system to used by the `ZipArchiver`.
310-
public init(fileSystem: FileSystem = localFileSystem) {
311-
self.fileSystem = fileSystem
312-
}
313-
314-
public func extract(
315-
from archivePath: AbsolutePath,
316-
to destinationPath: AbsolutePath,
317-
completion: @escaping (Result<Void, Error>) -> Void
318-
) {
319-
guard fileSystem.exists(archivePath) else {
320-
completion(.failure(FileSystemError(.noEntry, archivePath)))
321-
return
322-
}
323-
324-
guard fileSystem.isDirectory(destinationPath) else {
325-
completion(.failure(FileSystemError(.notDirectory, destinationPath)))
326-
return
327-
}
328-
329-
DispatchQueue.global(qos: .userInitiated).async {
330-
do {
331-
let result = try Process.popen(args: "unzip", "-j", archivePath.pathString, "-d", destinationPath.pathString)
332-
guard result.exitStatus == .terminated(code: 0) else {
333-
throw try StringError(result.utf8stderrOutput())
334-
}
335-
336-
completion(.success(()))
337-
} catch {
338-
completion(.failure(error))
339-
}
340-
}
341-
}
342-
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import Basics
12+
import TSCBasic
13+
import TSCUtility
14+
15+
import Dispatch
16+
17+
/// An `Archiver` that handles source archives.
18+
///
19+
/// Source archives created with `swift package archive-source`
20+
/// have a top-level directory prefix (for example, `LinkedList-1.1.1/`).
21+
/// Unfortunately, the `unzip` command used by `ZipArchiver` doesn't have an option for
22+
/// ignoring this top-level prefix.
23+
/// Rather than performing additional (possibly unsafe) file system operations,
24+
/// this `Archiver` delegates to `tar`, which has a built-in `--strip-components=` option.
25+
struct SourceArchiver: Archiver {
26+
public var supportedExtensions: Set<String> { ["zip"] }
27+
28+
/// The file-system implementation used for various file-system operations and checks.
29+
private let fileSystem: FileSystem
30+
31+
/// Creates a `SourceArchiver`.
32+
///
33+
/// - Parameters:
34+
/// - fileSystem: The file-system to used by the `SourceArchiver`.
35+
public init(fileSystem: FileSystem = localFileSystem) {
36+
self.fileSystem = fileSystem
37+
}
38+
39+
public func extract(
40+
from archivePath: AbsolutePath,
41+
to destinationPath: AbsolutePath,
42+
completion: @escaping (Result<Void, Error>) -> Void
43+
) {
44+
guard fileSystem.exists(archivePath) else {
45+
completion(.failure(FileSystemError(.noEntry, archivePath)))
46+
return
47+
}
48+
49+
guard fileSystem.isDirectory(destinationPath) else {
50+
completion(.failure(FileSystemError(.notDirectory, destinationPath)))
51+
return
52+
}
53+
54+
// TODO: consider calling `libarchive` or some other library directly instead of spawning a process
55+
DispatchQueue.global(qos: .userInitiated).async {
56+
do {
57+
let result = try Process.popen(args: "bsdtar",
58+
"--strip-components=1",
59+
"-xvf",
60+
archivePath.pathString,
61+
"-C", destinationPath.pathString)
62+
guard result.exitStatus == .terminated(code: 0) else {
63+
throw try StringError(result.utf8stderrOutput())
64+
}
65+
66+
completion(.success(()))
67+
} catch {
68+
completion(.failure(error))
69+
}
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)