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

Add Linux support #9

Merged
merged 9 commits into from
Jan 12, 2024
Merged
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
27 changes: 23 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: macos-13
steps:
- name: Checkout Repo
uses: actions/checkout@v3
uses: actions/checkout@v4
Copy link
Owner Author

Choose a reason for hiding this comment

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

This isn't a directly related change but I figured I'd bump while I was here.

- name: Select Xcode Version
run: sudo xcode-select --switch /Applications/Xcode_15.0.1.app/Contents/Developer
- name: Build and Test Framework
Expand All @@ -28,7 +28,7 @@ jobs:
runs-on: macos-13
steps:
- name: Checkout Repo
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Select Xcode Version
run: sudo xcode-select --switch /Applications/Xcode_15.0.1.app/Contents/Developer
- name: Build Package Integration
Expand All @@ -39,17 +39,36 @@ jobs:
runs-on: macos-13
steps:
- name: Checkout Repo
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Select Xcode Version
run: sudo xcode-select --switch /Applications/Xcode_15.0.1.app/Contents/Developer
- name: Build Project Integration
run: pushd Examples/ExampleProjectIntegration; xcrun xcodebuild build -skipPackagePluginValidation -skipMacroValidation -scheme ExampleProjectIntegration; popd

linux:
name: "Build and Test on Linux"
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup Swift Environment
uses: swift-actions/setup-swift@v1
with:
swift-version: 5.9
- name: Build and Test Framework
run: swift test -c release --enable-code-coverage -Xswiftc -enable-testing
- name: Prepare Coverage Reports
run: |
llvm-cov export -format="lcov" .build/x86_64-unknown-linux-gnu/release/SafeDIPackageTests.xctest -instr-profile .build/x86_64-unknown-linux-gnu/release/codecov/default.profdata > coverage.lcov
- name: Upload Coverage Reports
if: success()
uses: codecov/codecov-action@v3

readme-validation:
name: Check Markdown links
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Validate Markdown
uses: gaurav-nelson/github-action-markdown-link-check@v1
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,16 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftParser", package: "swift-syntax"),
.byNameItem(name: "ZippyJSON", condition: .when(platforms: [.iOS, .tvOS, .macOS])),
"SafeDICore",
"ZippyJSON",
Copy link
Owner Author

Choose a reason for hiding this comment

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

ZippyJSON does not support Linux (or technically watchOS, visionOS, and a few other platforms)

]
),
.testTarget(
name: "SafeDIToolTests",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.byNameItem(name: "ZippyJSON", condition: .when(platforms: [.iOS, .tvOS, .macOS])),
"SafeDITool",
"ZippyJSON",
]
),

Expand Down
4 changes: 2 additions & 2 deletions Sources/SafeDICore/Generators/DependencyTreeGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public final class DependencyTreeGenerator {
for try await generatedRoot in taskGroup {
generatedRoots.append(generatedRoot)
}
return generatedRoots.sorted().joined(separator: "\n\n").trimmingCharacters(in: .whitespacesAndNewlines)
Copy link
Owner Author

Choose a reason for hiding this comment

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

trimmingCharacters exists on NSString, which doesn't exist in Swift on Linux. Turns out we didn't need this call anyways though.

return generatedRoots.sorted().joined(separator: "\n\n")
}

let importsWhitespace = imports.isEmpty ? "" : "\n"
Expand All @@ -69,7 +69,7 @@ public final class DependencyTreeGenerator {

// MARK: - DependencyTreeGeneratorError

enum DependencyTreeGeneratorError: Error, CustomStringConvertible {
private enum DependencyTreeGeneratorError: Error, CustomStringConvertible {
Copy link
Owner Author

Choose a reason for hiding this comment

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

not a necessary change but noticed that I'd forgotten to make this private while I was here.


case noInstantiableFound(TypeDescription)
case unfulfillableProperties([UnfulfillableProperty])
Expand Down
15 changes: 7 additions & 8 deletions Sources/SafeDICore/Models/TypeDescription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public enum TypeDescription: Codable, Hashable, Comparable, Sendable {
case let .closure(arguments, isAsync, doesThrow, returnType):
return "(\(arguments.map { $0.asSource }.joined(separator: ", ")))\([isAsync ? " async" : "", doesThrow ? " throws" : ""].filter { !$0.isEmpty }.joined()) -> \(returnType.asSource)"
case let .unknown(text):
return text.trimmingCharacters(in: .whitespacesAndNewlines)
Copy link
Owner Author

Choose a reason for hiding this comment

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

since trimmingCharacters doesn't exist on Linux, we now initialize this type with a trimmedDescription which does effectively the same thing (and we've got a test to prove it!)

return text
}
}

Expand Down Expand Up @@ -280,9 +280,8 @@ extension TypeSyntax {
returnType: typeIdentifier.returnClause.type.typeDescription)

} else {
assertionFailure("TypeSyntax of unknown type. Defaulting to `description`.")
Copy link
Owner Author

Choose a reason for hiding this comment

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

We haven't gotten any value out of this assertionFailure in multiple years of development on this type across multiple projects. Time to remove.

// The description is a source-accurate description of this node, so it is a reasonable fallback.
return .unknown(text: description)
return .unknown(text: trimmedDescription)
}
}
}
Expand All @@ -304,7 +303,7 @@ extension ExprSyntax {
if let base = memberAccessExpr.base {
return base.typeDescription
} else {
return .unknown(text: memberAccessExpr.description)
return .unknown(text: memberAccessExpr.trimmedDescription)
}
} else {
if let base = memberAccessExpr.base {
Expand All @@ -321,7 +320,7 @@ extension ExprSyntax {
)
}
} else {
return .unknown(text: memberAccessExpr.description)
return .unknown(text: memberAccessExpr.trimmedDescription)
}
}
} else if let genericExpr = GenericSpecializationExprSyntax(self) {
Expand All @@ -339,7 +338,7 @@ extension ExprSyntax {
parentType: parentType, generics: genericTypeVisitor.genericArguments
)
case .any, .array, .attributed, .closure, .composition, .dictionary, .implicitlyUnwrappedOptional, .metatype, .optional, .some, .tuple, .unknown:
return .unknown(text: description)
return .unknown(text: trimmedDescription)
}
} else if let tupleExpr = TupleExprSyntax(self) {
let tupleTypes = tupleExpr.elements.map(\.expression.typeDescription)
Expand Down Expand Up @@ -377,7 +376,7 @@ extension ExprSyntax {
returnType: returnType.typeDescription
)
} else {
return .unknown(text: description)
return .unknown(text: trimmedDescription)
}
} else if let optionalChainingExpr = OptionalChainingExprSyntax(self) {
return .optional(optionalChainingExpr.expression.typeDescription)
Expand All @@ -398,7 +397,7 @@ extension ExprSyntax {
value: onlyElement.value.typeDescription
)
} else {
return .unknown(text: description)
return .unknown(text: trimmedDescription)
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions Sources/SafeDITool/SafeDITool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import ArgumentParser
import Foundation
import SafeDICore
import SwiftParser
#if canImport(ZippyJSON)
import ZippyJSON
#endif

@main
struct SafeDITool: AsyncParsableCommand {
Expand Down Expand Up @@ -175,7 +177,11 @@ struct SafeDITool: AsyncParsableCommand {
of: ModuleInfo.self,
returning: [ModuleInfo].self
) { taskGroup in
#if canImport(ZippyJSON)
let decoder = ZippyJSONDecoder()
#else
let decoder = JSONDecoder()
#endif
for moduleInfoURL in moduleInfoURLs {
taskGroup.addTask {
try decoder.decode(
Expand Down Expand Up @@ -234,11 +240,15 @@ struct SafeDITool: AsyncParsableCommand {

extension Data {
fileprivate func write(toPath filePath: String) throws {
#if os(Linux)
try write(to: URL(fileURLWithPath: filePath))
#else
if #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) {
try write(to: URL(filePath: filePath))
} else {
try write(to: URL(fileURLWithPath: filePath))
}
#endif
}
}

Expand All @@ -248,10 +258,14 @@ extension String {
}

fileprivate var asFileURL: URL {
#if os(Linux)
URL(fileURLWithPath: self)
#else
if #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) {
URL(filePath: self)
} else {
URL(fileURLWithPath: self)
}
#endif
}
}
4 changes: 2 additions & 2 deletions Tests/SafeDICoreTests/TypeDescriptionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,8 @@ final class TypeDescriptionTests: XCTestCase {
}

func test_asSource_whenDescribingAnUnknownCase_returnsTheProvidedStringWithWhitespaceStripped() {
let sut = TypeDescription.unknown(text: " SomeTypeThatIsFormattedOddly ")
XCTAssertEqual(sut.asSource, "SomeTypeThatIsFormattedOddly")
let typeDescription = TypeSyntax(stringLiteral: " SomeTypeThatIsFormattedOddly ").typeDescription
XCTAssertEqual(typeDescription.asSource, "SomeTypeThatIsFormattedOddly")
}

// MARK: - Visitors
Expand Down
6 changes: 2 additions & 4 deletions Tests/SafeDIToolTests/SafeDIToolTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2732,13 +2732,11 @@ final class SafeDIToolTests: XCTestCase {
line: UInt = #line,
block: () async throws -> ReturnType
) async {
var didThrow = false
do {
_ = try await block()
XCTFail("Did not throw error!", line: line)
} catch {
didThrow = true
XCTAssertEqual((error as CustomStringConvertible).description, errorDescription, line: line)
XCTAssertEqual("\(error)", errorDescription, line: line)
Copy link
Owner Author

Choose a reason for hiding this comment

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

This change was required to get tests working on Linux. (error as CustomStringConvertible).description was not finding our var description: String code on Linux for unknown reasons.

}
XCTAssertTrue(didThrow, "Did not throw error!", line: line)
}
}