Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Begin including SPI documentation in local builds, and fix all newly-revealed DocC issues #295

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Documentation/Releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ git checkout -b release/x.y.z
The package manifest files (Package.swift _and_ [email protected]) must
be updated so that the release can be used as a package dependency:

1. Delete any unsafe flags from `var packageSettings` as well as elsewhere in
the package manifest files.
1. Change the `isBuildingForDistribution` constant from `false` to `true`.
1. Open the "Documentation/Testing.docc/TemporaryGettingStarted.md" file and
update the line:

Expand Down
37 changes: 26 additions & 11 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,7 @@ let package = Package(
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
],
swiftSettings: .packageSettings + [
// The only target which needs the ability to import this macro
// implementation target's module is its unit test target. Users of the
// macros this target implements use them via their declarations in the
// Testing module. This target's module is never distributed to users,
// but as an additional guard against accidental misuse, this specifies
// the unit test target as the only allowable client.
.unsafeFlags(["-Xfrontend", "-allowable-client", "-Xfrontend", "TestingMacrosTests"]),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though I still like the idea of this setting, in practice it is not giving us any benefit or protection since currently it must be commented-out for distribution builds (which are the usage scenario it was meant for). Having it here causes us an additional manual step each time we cut a release, and there's little practical value in conditionalizing it to only non-distribution builds IMO.

]
swiftSettings: .packageSettings
),

// "Support" targets: These contain C family code and are used exclusively
Expand Down Expand Up @@ -110,12 +102,20 @@ package.targets.append(contentsOf: [
])
#endif

/// Whether this package is building for distribution.
///
/// This can be used to conditionalize certain settings which are not intended
/// for use when building the package for distribution to end users.
/// Non-distribution (i.e. development-only) builds may enable things
/// like stricter compiler features which are unsupported or inappropriate for
/// client builds.
let isBuildingForDistribution = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if instead we checked for an environment variable and built SPI when it's set?

let buildSPIDocumentation: Bool = {
  if let boolValue = Context.environment["SWT_SPI_DOCUMENTATION_ENABLED"].flatMap(Bool.init) {
    return boolValue
  } else if let intValue = Context.environment["SWT_SPI_DOCUMENTATION_ENABLED"].flatMap(UInt64.init) {
    return intValue != 0
  }
  return false // disabled by default
}()

(That's a slightly simplified version of what our own Environment type does.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do this, although a workflow I commonly use for viewing docs is the "Product > Build Documentation" command in recent versions of Xcode, and I was hoping for that to get this flag too. I don't know of a way to apply an env var in that scenario (when using the GUI Xcode app, not its xcodebuild CLI).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constant I added here would still be needed, or at least it would still be helpful, even if we conditionalized the SPI docs flag based on an env var, because the constant is what (now) decides whether all the unsafeFlags get included

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly we can also just drop the unsafe flags outright.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

although a workflow I commonly use for viewing docs is the "Product > Build Documentation" command in recent versions of Xcode

We can ask the DocC folks if they can help with this.

Copy link
Contributor

@Kyle-Ye Kyle-Ye Jul 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do this, although a workflow I commonly use for viewing docs is the "Product > Build Documentation" command in recent versions of Xcode, and I was hoping for that to get this flag too. I don't know of a way to apply an env var in that scenario (when using the GUI Xcode app, not its xcodebuild CLI).

+1 for some similar scenario I experienced. And I believe it is a Xcode/SwiftPM issue instead of a DocC issue.

Xcode GUI only gives us the ability to set env value for executable target when they are running. I could not find a easy solution to setup the env value I use in my Package.swift file.

Currently I just use a small macOS app I wrote to trigger export XX=YY && open ./ -a Xocde for me in Finder.

Otherwise we'll have to quit Xcode and manually export the env value in shell and then open Xcode in the shell to make Package.swift access the env we set.

image

As a person who maintains some Swift packages and likes to add environment variable condition config in package, it is really troublesome at present if we do not use any other 3rd party tools.


extension Array where Element == PackageDescription.SwiftSetting {
/// Settings intended to be applied to every Swift target in this package.
/// Analogous to project-level build settings in an Xcode project.
static var packageSettings: Self {
availabilityMacroSettings + [
.unsafeFlags(["-require-explicit-sendable"]),
var settings = availabilityMacroSettings + [
.enableExperimentalFeature("StrictConcurrency"),
.enableUpcomingFeature("ExistentialAny"),

Expand All @@ -124,6 +124,12 @@ extension Array where Element == PackageDescription.SwiftSetting {

.define("SWT_TARGET_OS_APPLE", .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS])),
]

if !isBuildingForDistribution {
settings.append(contentsOf: developmentOnlySettings)
}

return settings
}

/// Settings which define commonly-used OS availability macros.
Expand All @@ -142,6 +148,15 @@ extension Array where Element == PackageDescription.SwiftSetting {
.enableExperimentalFeature("AvailabilityMacro=_distantFuture:macOS 99.0, iOS 99.0, watchOS 99.0, tvOS 99.0"),
]
}

/// Settings which are useful during development, but should not be included
/// when building for distribution.
private static var developmentOnlySettings: Self {
[
.unsafeFlags(["-require-explicit-sendable"]),
.unsafeFlags(["-include-spi-symbols"]),
]
}
}

extension Array where Element == PackageDescription.CXXSetting {
Expand Down
37 changes: 26 additions & 11 deletions [email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,7 @@ let package = Package(
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
],
swiftSettings: .packageSettings + [
// The only target which needs the ability to import this macro
// implementation target's module is its unit test target. Users of the
// macros this target implements use them via their declarations in the
// Testing module. This target's module is never distributed to users,
// but as an additional guard against accidental misuse, this specifies
// the unit test target as the only allowable client.
.unsafeFlags(["-Xfrontend", "-allowable-client", "-Xfrontend", "TestingMacrosTests"]),
]
swiftSettings: .packageSettings
),

// "Support" targets: These contain C family code and are used exclusively
Expand Down Expand Up @@ -110,12 +102,20 @@ package.targets.append(contentsOf: [
])
#endif

/// Whether this package is building for distribution.
///
/// This can be used to conditionalize certain settings which are not intended
/// for use when building the package for distribution to end users.
/// Non-distribution (i.e. development-only) builds may enable things
/// like stricter compiler features which are unsupported or inappropriate for
/// client builds.
let isBuildingForDistribution = false

extension Array where Element == PackageDescription.SwiftSetting {
/// Settings intended to be applied to every Swift target in this package.
/// Analogous to project-level build settings in an Xcode project.
static var packageSettings: Self {
availabilityMacroSettings + [
.unsafeFlags(["-require-explicit-sendable"]),
var settings = availabilityMacroSettings + [
.enableExperimentalFeature("StrictConcurrency"),
.enableUpcomingFeature("ExistentialAny"),

Expand All @@ -124,6 +124,12 @@ extension Array where Element == PackageDescription.SwiftSetting {

.define("SWT_TARGET_OS_APPLE", .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS])),
]

if !isBuildingForDistribution {
settings.append(contentsOf: developmentOnlySettings)
}

return settings
}

/// Settings which define commonly-used OS availability macros.
Expand All @@ -142,6 +148,15 @@ extension Array where Element == PackageDescription.SwiftSetting {
.enableExperimentalFeature("AvailabilityMacro=_distantFuture:macOS 99.0, iOS 99.0, watchOS 99.0, tvOS 99.0"),
]
}

/// Settings which are useful during development, but should not be included
/// when building for distribution.
private static var developmentOnlySettings: Self {
[
.unsafeFlags(["-require-explicit-sendable"]),
.unsafeFlags(["-include-spi-symbols"]),
]
}
}

extension Array where Element == PackageDescription.CXXSetting {
Expand Down
11 changes: 7 additions & 4 deletions Sources/Testing/Events/Event.swift
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ extension Event {
// MARK: - Snapshotting

extension Event {
/// A serializable event that occurred during testing.
/// A serializable snapshot of an ``Event`` instance.
public struct Snapshot: Sendable, Codable {

/// The kind of event.
Expand Down Expand Up @@ -312,7 +312,7 @@ extension Event {
}

extension Event.Kind {
/// A serializable enumeration describing the various kinds of event that can be observed.
/// A serializable snapshot of an ``Event/Kind-swift.enum`` instance.
public enum Snapshot: Sendable, Codable {
/// A test run started.
///
Expand Down Expand Up @@ -412,8 +412,11 @@ extension Event.Kind {
/// This is the last event posted before ``Runner/run()`` returns.
case runEnded

/// Snapshots an ``Event.Kind``.
/// - Parameter kind: The original ``Event.Kind`` to snapshot.
/// Initialize an instance of this type by snapshotting the specified event
/// kind.
///
/// - Parameters:
/// - kind: The original event kind to snapshot.
public init(snapshotting kind: Event.Kind) {
switch kind {
case .runStarted:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ extension Event {

/// Initialize a new human-readable event recorder.
///
/// Output from the testing library is converted to "messages" using the
/// ``Event/HumanReadableOutputRecorder/record(_:)`` function. The format of
/// those messages is, as the type's name suggests, not meant to be
/// machine-readable and is subject to change.
/// Output from the testing library is converted to "messages" using
/// ``Event/HumanReadableOutputRecorder/record(_:in:verbosely:)``. The
/// format of those messages is, as the type's name suggests, not meant to
/// be machine-readable and is subject to change.
public init() {}
}
}
Expand Down
9 changes: 6 additions & 3 deletions Sources/Testing/Expectations/Expectation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public struct ExpectationFailedError: Error {
// MARK: - Snapshotting

extension Expectation {
/// A serializable type describing an expectation that has been evaluated.
/// A serializable snapshot of an ``Expectation`` instance.
@_spi(ForToolsIntegrationOnly)
public struct Snapshot: Sendable, Codable {
/// The expression evaluated by this expectation.
Expand Down Expand Up @@ -89,8 +89,11 @@ extension Expectation {
/// The source location where this expectation was evaluated.
public var sourceLocation: SourceLocation

/// Creates a snapshot expectation from a real ``Expectation``.
/// - Parameter expectation: The real expectation.
/// Initialize an instance of this type by snapshotting the specified
/// expectation.
///
/// - Parameters:
/// - expectation: The original expectation to snapshot.
public init(snapshotting expectation: Expectation) {
self.evaluatedExpression = expectation.evaluatedExpression
self.mismatchedErrorDescription = expectation.mismatchedErrorDescription
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
/// - expressionWithCapturedRuntimeValues: The expression, corresponding to
/// `condition` and with runtime values captured, that is being evaluated
/// (if available at compile time.)
/// - mismatchedErrorDescription: A description of the error mismatch that
/// occurred, if any.
/// - difference: The difference between the operands in `condition`, if
/// available. Most callers should pass `nil`.
/// - comments: An array of comments describing the expectation. This array
Expand Down
31 changes: 15 additions & 16 deletions Sources/Testing/Issues/Issue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,10 @@ public struct Issue: Sendable {
///
/// - Parameters:
/// - timeLimitComponents: The time limit reached by the test.
///
/// @Comment {
/// - Bug: The associated value of this enumeration case should be an
/// instance of `Duration`, but the testing library's deployment target
/// predates the introduction of that type.
/// }
//
// - Bug: The associated value of this enumeration case should be an
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I confirmed that the rendered DocC still shows the documentation block comments above, despite having 2-slash // comments in between.

// instance of `Duration`, but the testing library's deployment target
// predates the introduction of that type.
indirect case timeLimitExceeded(timeLimitComponents: (seconds: Int64, attoseconds: Int64))

/// A known issue was expected, but was not recorded.
Expand Down Expand Up @@ -179,7 +177,7 @@ extension Issue.Kind: CustomStringConvertible {
// MARK: - Snapshotting

extension Issue {
/// A serializable type describing a failure or warning which occurred during a test.
/// A serializable snapshot of an ``Issue`` instance.
@_spi(ForToolsIntegrationOnly)
public struct Snapshot: Sendable, Codable {
/// The kind of issue this value represents.
Expand Down Expand Up @@ -221,7 +219,7 @@ extension Issue {
}

extension Issue.Kind {
/// Serializable kinds of issues which may be recorded.
/// A serializable snapshot of an ``Issue/Kind-swift.enum`` instance.
@_spi(ForToolsIntegrationOnly)
public enum Snapshot: Sendable, Codable {
/// An issue which occurred unconditionally, for example by using
Expand Down Expand Up @@ -262,12 +260,10 @@ extension Issue.Kind {
///
/// - Parameters:
/// - timeLimitComponents: The time limit reached by the test.
///
/// @Comment {
/// - Bug: The associated value of this enumeration case should be an
/// instance of `Duration`, but the testing library's deployment target
/// predates the introduction of that type.
/// }
//
// - Bug: The associated value of this enumeration case should be an
// instance of `Duration`, but the testing library's deployment target
// predates the introduction of that type.
indirect case timeLimitExceeded(timeLimitComponents: (seconds: Int64, attoseconds: Int64))

/// A known issue was expected, but was not recorded.
Expand All @@ -280,8 +276,11 @@ extension Issue.Kind {
/// within the tests being run.
case system

/// Snapshots an ``Issue.Kind``.
/// - Parameter kind: The original ``Issue.Kind`` to snapshot.
/// Initialize an instance of this type by snapshotting the specified issue
/// kind.
///
/// - Parameters:
/// - kind: The original issue kind to snapshot.
public init(snapshotting kind: Issue.Kind) {
self = switch kind {
case .unconditional:
Expand Down
8 changes: 3 additions & 5 deletions Sources/Testing/Parameterization/Test.Case.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,9 @@ extension Test {
/// The value of this property represents the type of the parameter, but
/// arguments passed to this parameter may be of different types. For
/// example, an argument may be a subclass or conforming type of the
/// declared parameter type.
///
/// For information about runtime type of an argument to a parameterized
/// test, use ``TypeInfo/init(describingTypeOf:)``, passing the argument
/// value obtained by calling ``Test/Case/Argument/value``.
/// declared parameter type. Information about the type of an argument can
/// be accessed by querying the type of an argument's
/// ``Test/Case/Argument/value`` property.
@_spi(ForToolsIntegrationOnly)
public var typeInfo: TypeInfo

Expand Down
7 changes: 4 additions & 3 deletions Sources/Testing/Running/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ public struct Configuration: Sendable {
/// The conditions under which test iterations should continue.
///
/// If the value of this property is `nil`, a test plan will be run
/// ``count`` times regardless of whether or not issues are encountered
/// while running.
/// ``maximumIterationCount`` times regardless of whether or not issues are
/// encountered while running.
public var continuationCondition: ContinuationCondition?

/// The maximum number of times the test run should iterate.
Expand All @@ -79,7 +79,8 @@ public struct Configuration: Sendable {
/// - continuationCondition: The conditions under which test iterations
/// should continue. If `nil`, the iterations should continue
/// unconditionally `count` times.
/// - count: The maximum number of times the test run should iterate.
/// - maximumIterationCount: The maximum number of times the test run
/// should iterate.
public static func repeating(_ continuationCondition: ContinuationCondition? = nil, maximumIterationCount: Int) -> Self {
Self(continuationCondition: continuationCondition, maximumIterationCount: maximumIterationCount)
}
Expand Down
3 changes: 1 addition & 2 deletions Sources/Testing/Running/Runner.Plan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -402,8 +402,7 @@ extension Runner.Plan.Step {
}

extension Runner.Plan.Action {
/// A serializable snapshot of a ``Runner/Plan-swift.struct/Step/Action``
/// instance.
/// A serializable snapshot of a ``Runner/Plan-swift.struct/Action`` instance.
@_spi(ForToolsIntegrationOnly)
public enum Snapshot: Sendable, Codable {
/// The test should be run.
Expand Down
Loading