File: SwiftPMDependencyProject.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (116 lines) | stat: -rw-r--r-- 4,270 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Foundation
import SKSupport
import XCTest

import struct TSCBasic.AbsolutePath
import class TSCBasic.Process
import enum TSCBasic.ProcessEnv
import struct TSCBasic.ProcessResult

/// A SwiftPM package that gets written to disk and for which a Git repository is initialized with a commit tagged
/// `1.0.0`. This repository can then be used as a dependency for another package, usually a `SwiftPMTestProject`.
public class SwiftPMDependencyProject {
  /// The scratch directory created for the dependency project.
  public let scratchDirectory: URL

  /// The directory in which the repository lives.
  public var packageDirectory: URL {
    return scratchDirectory.appendingPathComponent("MyDependency")
  }

  private func runGitCommand(_ arguments: [String], workingDirectory: URL) async throws {
    enum Error: Swift.Error {
      case cannotFindGit
      case processedTerminatedWithNonZeroExitCode(ProcessResult)
    }
    guard let git = await findTool(name: "git") else {
      if ProcessEnv.block["SWIFTCI_USE_LOCAL_DEPS"] == nil {
        // Never skip the test in CI, similar to what SkipUnless does.
        throw XCTSkip("git cannot be found")
      }
      throw Error.cannotFindGit
    }
    // We can't use `workingDirectory` because Amazon Linux doesn't support working directories (or at least
    // TSCBasic.Process doesn't support working directories on Amazon Linux)
    let process = TSCBasic.Process(
      arguments: [git.path, "-C", workingDirectory.path] + arguments
    )
    try process.launch()
    let processResult = try await process.waitUntilExit()
    guard processResult.exitStatus == .terminated(code: 0) else {
      throw Error.processedTerminatedWithNonZeroExitCode(processResult)
    }
  }

  public static let defaultPackageManifest: String = """
    // swift-tools-version: 5.7

    import PackageDescription

    let package = Package(
      name: "MyDependency",
      products: [.library(name: "MyDependency", targets: ["MyDependency"])],
      targets: [.target(name: "MyDependency")]
    )
    """

  public init(
    files: [RelativeFileLocation: String],
    manifest: String = defaultPackageManifest,
    testName: String = #function
  ) async throws {
    scratchDirectory = try testScratchDir(testName: testName)

    var files = files
    files["Package.swift"] = manifest

    for (fileLocation, markedContents) in files {
      let fileURL = fileLocation.url(relativeTo: packageDirectory)
      try FileManager.default.createDirectory(
        at: fileURL.deletingLastPathComponent(),
        withIntermediateDirectories: true
      )
      try extractMarkers(markedContents).textWithoutMarkers.write(to: fileURL, atomically: true, encoding: .utf8)
    }

    try await runGitCommand(["init"], workingDirectory: packageDirectory)
    try await tag(changedFiles: files.keys.map { $0.url(relativeTo: packageDirectory) }, version: "1.0.0")
  }

  public func tag(changedFiles: [URL], version: String) async throws {
    try await runGitCommand(
      ["add"] + changedFiles.map(\.path),
      workingDirectory: packageDirectory
    )
    try await runGitCommand(
      ["-c", "user.name=Dummy", "-c", "user.email=noreply@swift.org", "commit", "-m", "Version \(version)"],
      workingDirectory: packageDirectory
    )

    try await runGitCommand(["tag", version], workingDirectory: self.packageDirectory)
  }

  deinit {
    if cleanScratchDirectories {
      try? FileManager.default.removeItem(at: scratchDirectory)
    }
  }

  /// Function that makes sure the project stays alive until this is called. Otherwise, the `SwiftPMDependencyProject`
  /// might get deinitialized, which deletes the package on disk.
  public func keepAlive() {
    withExtendedLifetime(self) { _ in }
  }
}