Skip to content

Commit 743434a

Browse files
hartbitaciidgh
authored andcommitted
Introduce the new binary target changes to the Manifest
1 parent e3d1761 commit 743434a

File tree

15 files changed

+619
-16
lines changed

15 files changed

+619
-16
lines changed

Sources/Build/BuildPlan.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1219,7 +1219,7 @@ public class BuildPlan {
12191219
target: target,
12201220
buildParameters: buildParameters,
12211221
fileSystem: fileSystem))
1222-
case is SystemLibraryTarget:
1222+
case is SystemLibraryTarget, is BinaryTarget:
12231223
break
12241224
default:
12251225
fatalError("unhandled \(target.underlyingTarget)")
@@ -1418,6 +1418,9 @@ public class BuildPlan {
14181418
// Add system target targets to system targets array.
14191419
case .systemModule:
14201420
systemModules.append(target)
1421+
case .binary:
1422+
// TODO: Implement
1423+
break
14211424
}
14221425

14231426
case .product(let product):

Sources/PackageDescription/Target.swift

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
99
*/
1010

11+
import Foundation
12+
1113
/// A target, the basic building block of a Swift package.
1214
///
1315
/// Each target contains a set of source files that are compiled into a module or test suite.
@@ -24,6 +26,8 @@ public final class Target {
2426
case test
2527
/// A target that adapts a library on the system to work with Swift packages.
2628
case system
29+
/// A target that references a binary artifact.
30+
case _binary = "binary"
2731
}
2832

2933
/// The different types of a target's dependency on another entity.
@@ -56,6 +60,13 @@ public final class Target {
5660
/// Do not escape the package root; that is, values like `../Foo` or `/Foo` are invalid.
5761
public var path: String?
5862

63+
/// The url of the binary target.
64+
public var _url: String? {
65+
get { __url }
66+
set { __url = newValue }
67+
}
68+
public var __url: String?
69+
5970
/// The source files in this target.
6071
///
6172
/// If this property is `nil`, all valid source files in the target's path will be included and specified paths are relative to the target path.
@@ -136,11 +147,19 @@ public final class Target {
136147
}
137148
private var _linkerSettings: [LinkerSetting]?
138149

150+
/// The binary target's checksum.
151+
public var _checksum: String? {
152+
get { __checksum }
153+
set { __checksum = newValue }
154+
}
155+
public var __checksum: String?
156+
139157
/// Construct a target.
140158
private init(
141159
name: String,
142160
dependencies: [Dependency],
143161
path: String?,
162+
_url: String? = nil,
144163
exclude: [String],
145164
sources: [String]?,
146165
resources: [Resource]? = nil,
@@ -151,11 +170,13 @@ public final class Target {
151170
cSettings: [CSetting]? = nil,
152171
cxxSettings: [CXXSetting]? = nil,
153172
swiftSettings: [SwiftSetting]? = nil,
154-
linkerSettings: [LinkerSetting]? = nil
173+
linkerSettings: [LinkerSetting]? = nil,
174+
_checksum: String? = nil
155175
) {
156176
self.name = name
157177
self.dependencies = dependencies
158178
self.path = path
179+
self.__url = _url
159180
self.publicHeadersPath = publicHeadersPath
160181
self.sources = sources
161182
self._resources = resources
@@ -167,11 +188,44 @@ public final class Target {
167188
self._cxxSettings = cxxSettings
168189
self._swiftSettings = swiftSettings
169190
self._linkerSettings = linkerSettings
191+
self.__checksum = _checksum
170192

171193
switch type {
172194
case .regular, .test:
173-
precondition(pkgConfig == nil && providers == nil)
174-
case .system: break
195+
precondition(
196+
_url == nil &&
197+
pkgConfig == nil &&
198+
providers == nil &&
199+
_checksum == nil
200+
)
201+
case .system:
202+
precondition(
203+
_url == nil &&
204+
dependencies.isEmpty &&
205+
exclude.isEmpty &&
206+
sources == nil &&
207+
resources == nil &&
208+
publicHeadersPath == nil &&
209+
cSettings == nil &&
210+
cxxSettings == nil &&
211+
swiftSettings == nil &&
212+
linkerSettings == nil &&
213+
_checksum == nil
214+
)
215+
case ._binary:
216+
precondition(
217+
dependencies.isEmpty &&
218+
exclude.isEmpty &&
219+
sources == nil &&
220+
resources == nil &&
221+
publicHeadersPath == nil &&
222+
pkgConfig == nil &&
223+
providers == nil &&
224+
cSettings == nil &&
225+
cxxSettings == nil &&
226+
swiftSettings == nil &&
227+
linkerSettings == nil
228+
)
175229
}
176230
}
177231

@@ -432,13 +486,65 @@ public final class Target {
432486
pkgConfig: pkgConfig,
433487
providers: providers)
434488
}
489+
490+
/// Create a binary target referencing a remote artifact.
491+
///
492+
/// A binary target provides the url to a pre-built binary artifact for the target. Currently only supports
493+
/// artifacts for Apple platforms.
494+
///
495+
/// - Parameters:
496+
/// - name: The name of the target.
497+
/// - url: The URL to the binary artifact. Should point to a `zip` archive containing a `XCFramework`.
498+
/// - checksum: The checksum of the artifact archive for validation.
499+
@available(_PackageDescription, introduced: 5.2)
500+
public static func _binaryTarget(
501+
name: String,
502+
url: String,
503+
checksum: String
504+
) -> Target {
505+
return Target(
506+
name: name,
507+
dependencies: [],
508+
path: nil,
509+
_url: url,
510+
exclude: [],
511+
sources: nil,
512+
publicHeadersPath: nil,
513+
type: ._binary,
514+
_checksum: checksum)
515+
}
516+
517+
/// Create a binary target referencing an artifact on disk.
518+
///
519+
/// A binary target provides the path to a pre-built binary artifact for the target. Currently only supports
520+
/// artifacts for Apple platforms.
521+
///
522+
/// - Parameters:
523+
/// - name: The name of the target.
524+
/// - path: The path to the binary artifact. Can point directly to a `XCFramework` or a zip containing the
525+
/// `XCFramework`.
526+
@available(_PackageDescription, introduced: 5.2)
527+
public static func _binaryTarget(
528+
name: String,
529+
path: String
530+
) -> Target {
531+
return Target(
532+
name: name,
533+
dependencies: [],
534+
path: path,
535+
exclude: [],
536+
sources: nil,
537+
publicHeadersPath: nil,
538+
type: ._binary)
539+
}
435540
#endif
436541
}
437542

438543
extension Target: Encodable {
439544
private enum CodingKeys: CodingKey {
440545
case name
441546
case path
547+
case url
442548
case sources
443549
case resources
444550
case exclude
@@ -451,13 +557,15 @@ extension Target: Encodable {
451557
case cxxSettings
452558
case swiftSettings
453559
case linkerSettings
560+
case checksum
454561
}
455562

456563
public func encode(to encoder: Encoder) throws {
457564
var container = encoder.container(keyedBy: CodingKeys.self)
458565

459566
try container.encode(name, forKey: .name)
460567
try container.encode(path, forKey: .path)
568+
try container.encode(_url, forKey: .url)
461569
try container.encode(sources, forKey: .sources)
462570
try container.encode(_resources, forKey: .resources)
463571
try container.encode(exclude, forKey: .exclude)
@@ -466,6 +574,7 @@ extension Target: Encodable {
466574
try container.encode(type, forKey: .type)
467575
try container.encode(pkgConfig, forKey: .pkgConfig)
468576
try container.encode(providers, forKey: .providers)
577+
try container.encode(_checksum, forKey: .checksum)
469578

470579
if let cSettings = self._cSettings {
471580
try container.encode(cSettings, forKey: .cSettings)

Sources/PackageLoading/Diagnostics.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ extension Diagnostic.Message {
6969
static func brokenSymlink(_ path: AbsolutePath) -> Diagnostic.Message {
7070
.warning("ignoring broken symlink \(path)")
7171
}
72+
73+
static func missingBinaryTargetArtifact(target: String) -> Diagnostic.Message {
74+
.error("missing artifact for binary target '\(target)'")
75+
}
7276
}
7377

7478
public struct ManifestLoadingDiagnostic: DiagnosticData {

Sources/PackageLoading/ManifestLoader.swift

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import TSCBasic
1212
import PackageModel
1313
import TSCUtility
1414
import SPMLLBuild
15-
import class Foundation.ProcessInfo
15+
import Foundation
1616
public typealias FileSystem = TSCBasic.FileSystem
1717

1818
public enum ManifestParseError: Swift.Error {
@@ -28,6 +28,9 @@ public enum ManifestParseError: Swift.Error {
2828
/// The manifest contains a product that references a target that does not exist.
2929
case productTargetNotFound(productName: String, targetName: String)
3030

31+
/// The manifest contains a binary-only product of the wrong type.
32+
case invalidBinaryProductType(productName: String)
33+
3134
/// The manifest contains dependencies with the same URL.
3235
case duplicateDependencyURLs([[PackageDependencyDescription]])
3336

@@ -39,6 +42,15 @@ public enum ManifestParseError: Swift.Error {
3942

4043
/// The manifest contains target product dependencies that reference an unknown package.
4144
case unknownTargetDependencyPackage(targetName: String, packageName: String)
45+
46+
/// The manifest contains a binary target with an invalid artifact path/url.
47+
case invalidBinaryLocation(targetName: String)
48+
49+
/// The manifest contains a binary target with an invalid arfiact url scheme.
50+
case invalidBinaryURLScheme(targetName: String, validSchemes: [String])
51+
52+
/// The manifest contains a binary target with an invalid artifact extension.
53+
case invalidBinaryLocationExtension(targetName: String, validExtensions: [String])
4254
}
4355

4456
/// Resources required for manifest loading.
@@ -313,6 +325,7 @@ public final class ManifestLoader: ManifestLoaderProtocol {
313325
if toolsVersion >= .v5_2 {
314326
try validateDependencyNames(manifest)
315327
try validateTargetDependencyReferences(manifest)
328+
try validateBinaryTargets(manifest)
316329
}
317330
}
318331

@@ -329,6 +342,12 @@ public final class ManifestLoader: ManifestLoaderProtocol {
329342
throw ManifestParseError.productTargetNotFound(productName: product.name, targetName: target)
330343
}
331344
}
345+
346+
// Check that products that reference only binary targets don't define a type.
347+
let areTargetsBinary = product.targets.allSatisfy({ manifest.targetMap[$0]!.type == .binary })
348+
if areTargetsBinary && product.type != .library(.automatic) {
349+
throw ManifestParseError.invalidBinaryProductType(productName: product.name)
350+
}
332351
}
333352
}
334353

@@ -355,6 +374,32 @@ public final class ManifestLoader: ManifestLoaderProtocol {
355374
}
356375
}
357376

377+
private func validateBinaryTargets(_ manifest: Manifest) throws {
378+
// Check that binary targets point to the right file type.
379+
for target in manifest.targets where target.type == .binary {
380+
guard let location = URL(string: target.url ?? target.path!) else {
381+
throw ManifestParseError.invalidBinaryLocation(targetName: target.name)
382+
}
383+
384+
let isRemote = target.url != nil
385+
let validSchemes = ["https"]
386+
guard !isRemote || (location.scheme.map({ validSchemes.contains($0) }) ?? false) else {
387+
throw ManifestParseError.invalidBinaryURLScheme(
388+
targetName: target.name,
389+
validSchemes: validSchemes
390+
)
391+
}
392+
393+
let validExtensions = isRemote ? ["zip"] : ["zip", "xcframework"]
394+
guard validExtensions.contains(location.pathExtension) else {
395+
throw ManifestParseError.invalidBinaryLocationExtension(
396+
targetName: target.name,
397+
validExtensions: validExtensions
398+
)
399+
}
400+
}
401+
}
402+
358403
/// Validates that product target dependencies reference an existing package.
359404
private func validateTargetDependencyReferences(_ manifest: Manifest) throws {
360405
for target in manifest.targets {

Sources/PackageLoading/PackageDescription4Loader.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,16 @@ extension ManifestBuilder {
9999
name: try json.get("name"),
100100
dependencies: dependencies,
101101
path: json.get("path"),
102+
url: json.get("url"),
102103
exclude: try json.get("exclude"),
103104
sources: try? json.get("sources"),
104105
resources: try parseResources(json),
105106
publicHeadersPath: json.get("publicHeadersPath"),
106107
type: try .init(v4: json.get("type")),
107108
pkgConfig: json.get("pkgConfig"),
108109
providers: providers,
109-
settings: try parseBuildSettings(json)
110+
settings: try parseBuildSettings(json),
111+
checksum: json.get("checksum")
110112
)
111113
}
112114

@@ -318,6 +320,8 @@ extension TargetDescription.TargetType {
318320
self = .test
319321
case "system":
320322
self = .system
323+
case "binary":
324+
self = .binary
321325
default:
322326
fatalError()
323327
}

0 commit comments

Comments
 (0)