mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2024-10-05 16:57:30 +03:00
Compare commits
4 Commits
1415e57c87
...
85dad74d19
Author | SHA1 | Date | |
---|---|---|---|
|
85dad74d19 | ||
|
f8842228c5 | ||
|
7eb5e9bd06 | ||
|
9034be374c |
@ -2,6 +2,8 @@
|
||||
|
||||
## Next Version
|
||||
|
||||
- Removed `xcodegen dump --type graphviz` feature. @giginet
|
||||
|
||||
## 2.41.0
|
||||
|
||||
### Added
|
||||
|
@ -1064,6 +1064,7 @@ A multiline script can be written using the various YAML multiline methods, for
|
||||
- [ ] **captureScreenshotsAutomatically**: **Bool** - indicates whether screenshots should be captured automatically while UI Testing. This defaults to true.
|
||||
- [ ] **deleteScreenshotsWhenEachTestSucceeds**: **Bool** - whether successful UI tests should cause automatically-captured screenshots to be deleted. If `captureScreenshotsAutomatically` is false, this value is ignored. This defaults to true.
|
||||
- [ ] **testPlans**: **[[Test Plan](#test-plan)]** - List of test plan locations that will be referenced in the scheme.
|
||||
- [ ] **preferredScreenCaptureFormat**: **String** - automatic screen capture format to use while UI Testing. Possible values are `screenshots`, `screenRecording`. Default is `screenRecording`.
|
||||
|
||||
#### Test Target
|
||||
A target can be one of a 2 types:
|
||||
|
@ -18,15 +18,6 @@
|
||||
"version" : "0.0.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "graphviz",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/SwiftDocOrg/GraphViz.git",
|
||||
"state" : {
|
||||
"revision" : "70bebcf4597b9ce33e19816d6bbd4ba9b7bdf038",
|
||||
"version" : "0.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "jsonutilities",
|
||||
"kind" : "remoteSourceControl",
|
||||
@ -86,8 +77,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/tuist/XcodeProj.git",
|
||||
"state" : {
|
||||
"revision" : "6e60fb55271c80f83a186c9b1b4982fd991cfc0a",
|
||||
"version" : "8.13.0"
|
||||
"revision" : "3797181813ee963fe305d939232bc576d23ddbb0",
|
||||
"version" : "8.15.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -16,10 +16,9 @@ let package = Package(
|
||||
.package(url: "https://github.com/yonaskolb/JSONUtilities.git", from: "4.2.0"),
|
||||
.package(url: "https://github.com/kylef/Spectre.git", from: "0.9.2"),
|
||||
.package(url: "https://github.com/onevcat/Rainbow.git", from: "4.0.0"),
|
||||
.package(url: "https://github.com/tuist/XcodeProj.git", exact: "8.13.0"),
|
||||
.package(url: "https://github.com/tuist/XcodeProj.git", exact: "8.15.0"),
|
||||
.package(url: "https://github.com/jakeheis/SwiftCLI.git", from: "6.0.3"),
|
||||
.package(url: "https://github.com/mxcl/Version", from: "2.0.0"),
|
||||
.package(url: "https://github.com/SwiftDocOrg/GraphViz.git", exact: "0.2.0"),
|
||||
.package(url: "https://github.com/freddi-kit/ArtifactBundleGen", exact: "0.0.6")
|
||||
],
|
||||
targets: [
|
||||
@ -41,7 +40,6 @@ let package = Package(
|
||||
"XcodeProj",
|
||||
"PathKit",
|
||||
"XcodeGenCore",
|
||||
"GraphViz",
|
||||
], resources: [
|
||||
.copy("SettingPresets")
|
||||
]),
|
||||
|
22
README.md
22
README.md
@ -35,7 +35,6 @@ The project spec is a YAML or JSON file that defines your targets, configuration
|
||||
- ✅ Distribute your spec amongst multiple files for easy **sharing** and overriding
|
||||
- ✅ Easily create **multi-platform** frameworks
|
||||
- ✅ Integrate **Carthage** frameworks without any work
|
||||
- ✅ Export **Dependency Diagrams** to view in [Graphviz](https://www.graphviz.org)
|
||||
|
||||
Given an example project spec:
|
||||
|
||||
@ -138,27 +137,6 @@ Options:
|
||||
|
||||
There are other commands as well such as `xcodegen dump` which lets one output the resolved spec in many different formats, or write it to a file. Use `xcodegen help` to see more detailed usage information.
|
||||
|
||||
## Dependency Diagrams
|
||||
<details>
|
||||
<summary>Click to expand!</summary>
|
||||
|
||||
#### How to export dependency diagrams:
|
||||
|
||||
To stdout:
|
||||
|
||||
```
|
||||
xcodegen dump --type graphviz
|
||||
```
|
||||
|
||||
To a file:
|
||||
|
||||
```
|
||||
xcodegen dump --type graphviz --file Graph.viz
|
||||
```
|
||||
|
||||
During implementation, `graphviz` formatting was validated using [GraphvizOnline](https://dreampuf.github.io/GraphvizOnline/), [WebGraphviz](http://www.webgraphviz.com), and [Graphviz on MacOS](https://graphviz.org).
|
||||
</details>
|
||||
|
||||
## Editing
|
||||
```shell
|
||||
git clone https://github.com/yonaskolb/XcodeGen.git
|
||||
|
@ -203,6 +203,7 @@ public struct Scheme: Equatable {
|
||||
public static let debugEnabledDefault = true
|
||||
public static let captureScreenshotsAutomaticallyDefault = true
|
||||
public static let deleteScreenshotsWhenEachTestSucceedsDefault = true
|
||||
public static let preferredScreenCaptureFormatDefault = XCScheme.TestAction.ScreenCaptureFormat.screenRecording
|
||||
|
||||
public var config: String?
|
||||
public var gatherCoverageData: Bool
|
||||
@ -220,6 +221,7 @@ public struct Scheme: Equatable {
|
||||
public var captureScreenshotsAutomatically: Bool
|
||||
public var deleteScreenshotsWhenEachTestSucceeds: Bool
|
||||
public var testPlans: [TestPlan]
|
||||
public var preferredScreenCaptureFormat: XCScheme.TestAction.ScreenCaptureFormat
|
||||
|
||||
public struct TestTarget: Equatable, ExpressibleByStringLiteral {
|
||||
|
||||
@ -286,7 +288,8 @@ public struct Scheme: Equatable {
|
||||
debugEnabled: Bool = debugEnabledDefault,
|
||||
customLLDBInit: String? = nil,
|
||||
captureScreenshotsAutomatically: Bool = captureScreenshotsAutomaticallyDefault,
|
||||
deleteScreenshotsWhenEachTestSucceeds: Bool = deleteScreenshotsWhenEachTestSucceedsDefault
|
||||
deleteScreenshotsWhenEachTestSucceeds: Bool = deleteScreenshotsWhenEachTestSucceedsDefault,
|
||||
preferredScreenCaptureFormat: XCScheme.TestAction.ScreenCaptureFormat = preferredScreenCaptureFormatDefault
|
||||
) {
|
||||
self.config = config
|
||||
self.gatherCoverageData = gatherCoverageData
|
||||
@ -304,6 +307,7 @@ public struct Scheme: Equatable {
|
||||
self.customLLDBInit = customLLDBInit
|
||||
self.captureScreenshotsAutomatically = captureScreenshotsAutomatically
|
||||
self.deleteScreenshotsWhenEachTestSucceeds = deleteScreenshotsWhenEachTestSucceeds
|
||||
self.preferredScreenCaptureFormat = preferredScreenCaptureFormat
|
||||
}
|
||||
|
||||
public var shouldUseLaunchSchemeArgsEnv: Bool {
|
||||
@ -620,6 +624,7 @@ extension Scheme.Test: JSONObjectConvertible {
|
||||
customLLDBInit = jsonDictionary.json(atKeyPath: "customLLDBInit")
|
||||
captureScreenshotsAutomatically = jsonDictionary.json(atKeyPath: "captureScreenshotsAutomatically") ?? Scheme.Test.captureScreenshotsAutomaticallyDefault
|
||||
deleteScreenshotsWhenEachTestSucceeds = jsonDictionary.json(atKeyPath: "deleteScreenshotsWhenEachTestSucceeds") ?? Scheme.Test.deleteScreenshotsWhenEachTestSucceedsDefault
|
||||
preferredScreenCaptureFormat = jsonDictionary.json(atKeyPath: "preferredScreenCaptureFormat") ?? Scheme.Test.preferredScreenCaptureFormatDefault
|
||||
}
|
||||
}
|
||||
|
||||
@ -662,6 +667,10 @@ extension Scheme.Test: JSONEncodable {
|
||||
dict["deleteScreenshotsWhenEachTestSucceeds"] = deleteScreenshotsWhenEachTestSucceeds
|
||||
}
|
||||
|
||||
if preferredScreenCaptureFormat != Scheme.Test.preferredScreenCaptureFormatDefault {
|
||||
dict["preferredScreenCaptureFormat"] = preferredScreenCaptureFormat.toJSONValue()
|
||||
}
|
||||
|
||||
return dict
|
||||
}
|
||||
}
|
||||
@ -1013,3 +1022,9 @@ extension XCScheme.LaunchAction.GPUValidationMode: JSONEncodable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension XCScheme.TestAction.ScreenCaptureFormat: JSONEncodable {
|
||||
public func toJSONValue() -> Any {
|
||||
rawValue
|
||||
}
|
||||
}
|
||||
|
@ -41,8 +41,6 @@ class DumpCommand: ProjectCommand {
|
||||
output = try Yams.dump(object: project.toJSONDictionary())
|
||||
case .summary:
|
||||
output = project.debugDescription
|
||||
case .graphviz:
|
||||
output = GraphVizGenerator().generateModuleGraphViz(targets: project.targets)
|
||||
}
|
||||
|
||||
if let file = file {
|
||||
@ -61,7 +59,6 @@ private enum DumpType: String, ConvertibleFromString, CaseIterable {
|
||||
case parsedJSON = "parsed-json"
|
||||
case parsedYaml = "parsed-yaml"
|
||||
case summary
|
||||
case graphviz
|
||||
|
||||
static var defaultValue: DumpType { .yaml }
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
import DOT
|
||||
import Foundation
|
||||
import GraphViz
|
||||
import ProjectSpec
|
||||
|
||||
extension Dependency {
|
||||
var graphVizName: String {
|
||||
switch self.type {
|
||||
case .bundle, .package, .sdk, .framework, .carthage:
|
||||
return "[\(self.type)]\\n\(reference)"
|
||||
case .target:
|
||||
return reference
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Dependency.DependencyType: CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .bundle: return "bundle"
|
||||
case .package: return "package"
|
||||
case .framework: return "framework"
|
||||
case .carthage: return "carthage"
|
||||
case .sdk: return "sdk"
|
||||
case .target: return "target"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Node {
|
||||
init(target: Target) {
|
||||
self.init(target.name)
|
||||
self.shape = .box
|
||||
}
|
||||
|
||||
init(dependency: Dependency) {
|
||||
self.init(dependency.reference)
|
||||
self.shape = .box
|
||||
self.label = dependency.graphVizName
|
||||
}
|
||||
}
|
||||
|
||||
public class GraphVizGenerator {
|
||||
|
||||
public init() {}
|
||||
|
||||
public func generateModuleGraphViz(targets: [Target]) -> String {
|
||||
return DOTEncoder().encode(generateGraph(targets: targets))
|
||||
}
|
||||
|
||||
func generateGraph(targets: [Target]) -> Graph {
|
||||
var graph = Graph(directed: true)
|
||||
targets.forEach { target in
|
||||
target.dependencies.forEach { dependency in
|
||||
let from = Node(target: target)
|
||||
graph.append(from)
|
||||
let to = Node(dependency: dependency)
|
||||
graph.append(to)
|
||||
var edge = Edge(from: from, to: to)
|
||||
edge.style = .dashed
|
||||
graph.append(edge)
|
||||
}
|
||||
}
|
||||
return graph
|
||||
}
|
||||
}
|
@ -286,10 +286,12 @@ public class SchemeGenerator {
|
||||
let testPlans = scheme.test?.testPlans.enumerated().map { index, testPlan in
|
||||
XCScheme.TestPlanReference(reference: "container:\(testPlan.path)", default: defaultTestPlanIndex == index)
|
||||
} ?? []
|
||||
let testBuildableEntries = buildActionEntries.filter({ $0.buildFor.contains(.testing) }) + testBuildTargetEntries
|
||||
let testMacroExpansionBuildableRef = testBuildableEntries.map(\.buildableReference).contains(buildableReference) ? buildableReference : testBuildableEntries.first?.buildableReference
|
||||
|
||||
let testAction = XCScheme.TestAction(
|
||||
buildConfiguration: scheme.test?.config ?? defaultDebugConfig.name,
|
||||
macroExpansion: buildableReference,
|
||||
macroExpansion: testMacroExpansionBuildableRef,
|
||||
testables: testables,
|
||||
testPlans: testPlans.isEmpty ? nil : testPlans,
|
||||
preActions: scheme.test?.preActions.map(getExecutionAction) ?? [],
|
||||
@ -306,6 +308,7 @@ public class SchemeGenerator {
|
||||
language: scheme.test?.language,
|
||||
region: scheme.test?.region,
|
||||
systemAttachmentLifetime: scheme.test?.systemAttachmentLifetime,
|
||||
preferredScreenCaptureFormat: scheme.test?.preferredScreenCaptureFormat,
|
||||
customLLDBInitFile: scheme.test?.customLLDBInit
|
||||
)
|
||||
|
||||
|
@ -1,104 +0,0 @@
|
||||
import ProjectSpec
|
||||
import Spectre
|
||||
@testable import XcodeGenKit
|
||||
import XCTest
|
||||
|
||||
private let app = Target(
|
||||
name: "MyApp",
|
||||
type: .application,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_1": "VALUE"]),
|
||||
dependencies: [
|
||||
Dependency(type: .target, reference: "MyInternalFramework"),
|
||||
Dependency(type: .bundle, reference: "Resources"),
|
||||
Dependency(type: .carthage(findFrameworks: true, linkType: .static), reference: "MyStaticFramework"),
|
||||
Dependency(type: .carthage(findFrameworks: true, linkType: .dynamic), reference: "MyDynamicFramework"),
|
||||
Dependency(type: .framework, reference: "MyExternalFramework"),
|
||||
Dependency(type: .package(products: ["MyPackage"]), reference: "MyPackage"),
|
||||
Dependency(type: .sdk(root: "MySDK"), reference: "MySDK"),
|
||||
]
|
||||
)
|
||||
|
||||
private let framework = Target(
|
||||
name: "MyFramework",
|
||||
type: .framework,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_2": "VALUE"])
|
||||
)
|
||||
|
||||
private let uiTest = Target(
|
||||
name: "MyAppUITests",
|
||||
type: .uiTestBundle,
|
||||
platform: .iOS,
|
||||
settings: Settings(buildSettings: ["SETTING_3": "VALUE"]),
|
||||
dependencies: [Dependency(type: .target, reference: "MyApp")]
|
||||
)
|
||||
|
||||
private let targets = [app, framework, uiTest]
|
||||
|
||||
class GraphVizGeneratorTests: XCTestCase {
|
||||
|
||||
func testGraphOutput() throws {
|
||||
describe {
|
||||
let graph = GraphVizGenerator().generateGraph(targets: targets)
|
||||
$0.it("generates the expected number of nodes") {
|
||||
try expect(graph.nodes.count) == 16
|
||||
}
|
||||
$0.it("generates box nodes") {
|
||||
try expect(graph.nodes.filter { $0.shape == .box }.count) == 16
|
||||
}
|
||||
$0.it("generates the expected carthage nodes") {
|
||||
try expect(graph.nodes.filter { $0.label?.contains("[carthage]") ?? false }.count) == 2
|
||||
}
|
||||
$0.it("generates the expected sdk nodes") {
|
||||
try expect(graph.nodes.filter { $0.label?.contains("[sdk]") ?? false }.count) == 1
|
||||
}
|
||||
$0.it("generates the expected Framework nodes") {
|
||||
try expect(graph.nodes.filter { $0.label?.contains("[framework]") ?? false }.count) == 1
|
||||
}
|
||||
$0.it("generates the expected package nodes") {
|
||||
try expect(graph.nodes.filter { $0.label?.contains("[package]") ?? false }.count) == 1
|
||||
}
|
||||
$0.it("generates the expected bundle nodes") {
|
||||
try expect(graph.nodes.filter { $0.label?.contains("[bundle]") ?? false }.count) == 1
|
||||
}
|
||||
$0.it("generates the expected edges") {
|
||||
try expect(graph.edges.count) == 8
|
||||
}
|
||||
$0.it("generates dashed edges") {
|
||||
try expect(graph.edges.filter { $0.style == .dashed }.count) == 8
|
||||
}
|
||||
$0.it("generates the expected output") {
|
||||
let output = GraphVizGenerator().generateModuleGraphViz(targets: targets)
|
||||
try expect(output) == """
|
||||
digraph {
|
||||
MyApp [shape=box]
|
||||
MyInternalFramework [label=MyInternalFramework shape=box]
|
||||
MyApp [shape=box]
|
||||
Resources [label="[bundle]\\nResources" shape=box]
|
||||
MyApp [shape=box]
|
||||
MyStaticFramework [label="[carthage]\\nMyStaticFramework" shape=box]
|
||||
MyApp [shape=box]
|
||||
MyDynamicFramework [label="[carthage]\\nMyDynamicFramework" shape=box]
|
||||
MyApp [shape=box]
|
||||
MyExternalFramework [label="[framework]\\nMyExternalFramework" shape=box]
|
||||
MyApp [shape=box]
|
||||
MyPackage [label="[package]\\nMyPackage" shape=box]
|
||||
MyApp [shape=box]
|
||||
MySDK [label="[sdk]\\nMySDK" shape=box]
|
||||
MyAppUITests [shape=box]
|
||||
MyApp [label=MyApp shape=box]
|
||||
MyApp -> MyInternalFramework [style=dashed]
|
||||
MyApp -> Resources [style=dashed]
|
||||
MyApp -> MyStaticFramework [style=dashed]
|
||||
MyApp -> MyDynamicFramework [style=dashed]
|
||||
MyApp -> MyExternalFramework [style=dashed]
|
||||
MyApp -> MyPackage [style=dashed]
|
||||
MyApp -> MySDK [style=dashed]
|
||||
MyAppUITests -> MyApp [style=dashed]
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -479,6 +479,46 @@ class SchemeGeneratorTests: XCTestCase {
|
||||
try expect(xcscheme.launchAction?.macroExpansion?.buildableName) == "MyApp.app"
|
||||
}
|
||||
|
||||
$0.it("generates scheme with macroExpansion from tests when the main target is not part of the scheme") {
|
||||
let app = Target(
|
||||
name: "MyApp",
|
||||
type: .application,
|
||||
platform: .iOS,
|
||||
dependencies: []
|
||||
)
|
||||
|
||||
let mockApp = Target(
|
||||
name: "MockApp",
|
||||
type: .application,
|
||||
platform: .iOS,
|
||||
dependencies: []
|
||||
)
|
||||
|
||||
let testBundle = Target(
|
||||
name: "TestBundle",
|
||||
type: .unitTestBundle,
|
||||
platform: .iOS
|
||||
)
|
||||
let appTarget = Scheme.BuildTarget(target: .local(app.name), buildTypes: [.running])
|
||||
let mockAppTarget = Scheme.BuildTarget(target: .local(mockApp.name), buildTypes: [.testing])
|
||||
let testBundleTarget = Scheme.BuildTarget(target: .local(testBundle.name), buildTypes: [.testing])
|
||||
|
||||
let scheme = Scheme(
|
||||
name: "TestScheme",
|
||||
build: Scheme.Build(targets: [appTarget, mockAppTarget, testBundleTarget]),
|
||||
run: Scheme.Run(config: "Debug", macroExpansion: "MyApp")
|
||||
)
|
||||
let project = Project(
|
||||
name: "test",
|
||||
targets: [app, mockApp, testBundle],
|
||||
schemes: [scheme]
|
||||
)
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
|
||||
let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first)
|
||||
try expect(xcscheme.testAction?.macroExpansion?.buildableName) == "MockApp.app"
|
||||
}
|
||||
|
||||
$0.it("generates scheme with test target of local swift package") {
|
||||
let targetScheme = TargetScheme(
|
||||
testTargets: [Scheme.Test.TestTarget(targetReference: TestableTargetReference(name: "XcodeGenKitTests", location: .package("XcodeGen")))])
|
||||
@ -568,6 +608,42 @@ class SchemeGeneratorTests: XCTestCase {
|
||||
.init(reference: "container:\(testPlanPath2)", default: true),
|
||||
]
|
||||
}
|
||||
|
||||
$0.it("generates scheme with screenshots as preferred screen capture format") {
|
||||
let scheme = Scheme(
|
||||
name: "MyScheme",
|
||||
build: Scheme.Build(targets: [buildTarget]),
|
||||
run: Scheme.Run(config: "Debug"),
|
||||
test: Scheme.Test(config: "Debug", preferredScreenCaptureFormat: .screenshots)
|
||||
)
|
||||
let project = Project(
|
||||
name: "test",
|
||||
targets: [app, framework],
|
||||
schemes: [scheme]
|
||||
)
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
|
||||
let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first)
|
||||
try expect(xcscheme.testAction?.preferredScreenCaptureFormat) == .screenshots
|
||||
}
|
||||
|
||||
$0.it("generates scheme with screen recording as preferred screen capture format") {
|
||||
let scheme = Scheme(
|
||||
name: "MyScheme",
|
||||
build: Scheme.Build(targets: [buildTarget]),
|
||||
run: Scheme.Run(config: "Debug"),
|
||||
test: Scheme.Test(config: "Debug", preferredScreenCaptureFormat: .screenRecording)
|
||||
)
|
||||
let project = Project(
|
||||
name: "test",
|
||||
targets: [app, framework],
|
||||
schemes: [scheme]
|
||||
)
|
||||
let xcodeProject = try project.generateXcodeProject()
|
||||
|
||||
let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first)
|
||||
try expect(xcscheme.testAction?.preferredScreenCaptureFormat) == .screenRecording
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user