Skip to content

Commit

Permalink
Revert "Remove GraphViz feature (yonaskolb#1485)"
Browse files Browse the repository at this point in the history
This reverts commit 7eb5e9b.
  • Loading branch information
asifmohd committed Oct 22, 2024
1 parent 7207c78 commit f656b8f
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
"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",
Expand Down
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ let package = Package(
.package(url: "https://github.com/tuist/XcodeProj.git", exact: "8.16.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: [
Expand All @@ -40,6 +41,7 @@ let package = Package(
"XcodeProj",
"PathKit",
"XcodeGenCore",
"GraphViz",
], resources: [
.copy("SettingPresets")
]),
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ 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:

Expand Down Expand Up @@ -137,6 +138,27 @@ 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
Expand Down
3 changes: 3 additions & 0 deletions Sources/XcodeGenCLI/Commands/DumpCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ 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 {
Expand All @@ -59,6 +61,7 @@ private enum DumpType: String, ConvertibleFromString, CaseIterable {
case parsedJSON = "parsed-json"
case parsedYaml = "parsed-yaml"
case summary
case graphviz

static var defaultValue: DumpType { .yaml }
}
66 changes: 66 additions & 0 deletions Sources/XcodeGenKit/GraphVizGenerator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
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
}
}
104 changes: 104 additions & 0 deletions Tests/XcodeGenKitTests/GraphVizGeneratorTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
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]
}
"""
}
}
}
}

0 comments on commit f656b8f

Please sign in to comment.