File: SwiftPMDependencyProject.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (119 lines) | stat: -rw-r--r-- 4,412 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
117
118
119
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

package import Foundation
import LanguageServerProtocolExtensions
import SwiftExtensions
import TSCExtensions
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`.
package class SwiftPMDependencyProject {
  /// The scratch directory created for the dependency project.
  package let scratchDirectory: URL

  /// The directory in which the repository lives.
  package 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 processResult = try await withTimeout(defaultTimeoutDuration) {
      try await TSCBasic.Process.run(
        arguments: [try git.filePath, "-C", try workingDirectory.filePath] + arguments,
        workingDirectory: nil
      )
    }
    guard processResult.exitStatus == .terminated(code: 0) else {
      throw Error.processedTerminatedWithNonZeroExitCode(processResult)
    }
  }

  package 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")]
    )
    """

  package 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")
  }

  package func tag(changedFiles: [URL], version: String) async throws {
    try await runGitCommand(
      ["add"] + changedFiles.map { try $0.filePath },
      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.
  package func keepAlive() {
    withExtendedLifetime(self) { _ in }
  }
}