Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ DerivedData/

/openapi
/.tmp
/xtool
2 changes: 1 addition & 1 deletion Documentation/Contributing/Debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Some environment variables that help with debugging. Use these as e.g. `XTL_FOO=
### Disk

- `XTL_DEBUG_TMP`: Set to `1` to log details when xtool creates/destroys temporary directories.
- `XTL_TMPDIR`: Control the location where xtool stores temporary directories. We also respect `TMPDIR` and (on Linux) `XDG_CACHE_HOME`.
- `XTL_TMPDIR`: Control the location where xtool stores temporary directories. We also respect `XDG_CACHE_HOME` on Linux.

### Networking

Expand Down
8 changes: 6 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ let xtoolVersion: String? = {
let gitCommit = info.currentCommit
let gitTag = info.currentTag
let gitRef = gitTag ?? "commit \(gitCommit)"
return "\(gitRef)\(info.hasUncommittedChanges ? " (dirty)" : "")"
return gitRef
} else {
return nil
}
}()

let hasUncommittedChanges = Context.gitInformation?.hasUncommittedChanges ?? false

let cSettings: [CSetting] = [
.define("_GNU_SOURCE", .when(platforms: [.linux])),
]
Expand Down Expand Up @@ -90,7 +92,9 @@ let package = Package(
dependencies: [
.product(name: "OpenSSL", package: "xtool-core")
],
cSettings: cSettings + (
cSettings: cSettings + [
.define("XTOOL_HAS_UNCOMMITTED_CHANGES", to: hasUncommittedChanges ? "1" : "0"),
] + (
xtoolVersion
.map { [.define("XTOOL_VERSION", to: "\"\($0)\"")] }
?? []
Expand Down
3 changes: 3 additions & 0 deletions Sources/CXKit/include/version.h
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
#include <stdbool.h>

const char * _Nonnull xtl_version(void);
bool xtl_has_uncommitted_changes(void);
13 changes: 13 additions & 0 deletions Sources/CXKit/version.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "version.h"
#include <stddef.h>

const char *xtl_version(void) {
#ifdef XTOOL_VERSION
Expand All @@ -7,3 +8,15 @@ const char *xtl_version(void) {
return "unversioned";
#endif
}

bool xtl_has_uncommitted_changes(void) {
#ifndef XTOOL_VERSION
return true;
#else
#ifdef XTOOL_HAS_UNCOMMITTED_CHANGES
return XTOOL_HAS_UNCOMMITTED_CHANGES != 0;
#else
return false;
#endif
#endif
}
19 changes: 19 additions & 0 deletions Sources/XCLIUtils/CommandVersion.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
public struct CommandVersion: Sendable, Hashable {
public var version: String
public var hasUncommittedChanges: Bool

public init(version: String, hasUncommittedChanges: Bool) {
self.version = version
self.hasUncommittedChanges = hasUncommittedChanges
}

public static var unversioned: Self {
.init(version: "unversioned", hasUncommittedChanges: true)
}
}

extension CommandVersion: CustomStringConvertible {
public var description: String {
version + (hasUncommittedChanges ? "-dirty" : "")
}
}
1 change: 1 addition & 0 deletions Sources/XKit/Utilities/KeyValueStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import Foundation
import ConcurrencyExtras
import Dependencies
import XUtils

public enum KeyValueStorageError: Error {
case stringConversionFailure
Expand Down
6 changes: 5 additions & 1 deletion Sources/XToolSupport/XTool+Version.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import CXKit
import XCLIUtils

extension XTool {
public static let version = String(cString: xtl_version())
public static let version = CommandVersion(
Comment thread
coderabbitai[bot] marked this conversation as resolved.
version: String(cString: xtl_version()),
hasUncommittedChanges: xtl_has_uncommitted_changes(),
)
}
4 changes: 2 additions & 2 deletions Sources/XUtils/System+Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ extension FileDescriptor {
}
}

enum LockMode {
public enum LockMode {
case shared
case exclusive

Expand All @@ -67,7 +67,7 @@ extension FileDescriptor {
}
}

func tryLock(mode: LockMode) throws -> Bool {
public func tryLock(mode: LockMode) throws -> Bool {
if flock(rawValue, mode.raw | LOCK_NB) == 0 {
return true
}
Expand Down
19 changes: 1 addition & 18 deletions Sources/XUtils/TemporaryDirectory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,24 +71,7 @@ private struct TemporaryDirectoryRoot {
}

private init() {
let url: URL
let env = ProcessInfo.processInfo.environment
if let tmpdir = env["XTL_TMPDIR"] ?? env["TMPDIR"] {
url = URL(fileURLWithPath: tmpdir).appendingPathComponent("sh.xtool")
} else {
#if os(Linux)
// On Linux, /tmp is commonly a tmpfs mount while ~/.swiftpm lives on ext4.
// Foundation's FileManager.copyItem uses sendfile(2) internally, which returns
// EINVAL when copying between different filesystem types (e.g. tmpfs → ext4).
// This causes `swift sdk install` to fail on distros like Linux Mint (#181).
// Using a cache dir on the home filesystem avoids the cross-fs copy entirely.
let xdgCache = env["XDG_CACHE_HOME"].map { URL(fileURLWithPath: $0) }
?? FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".cache")
url = xdgCache.appendingPathComponent("xtool")
#else
url = FileManager.default.temporaryDirectory.appendingPathComponent("sh.xtool")
#endif
}
let url = XToolCache.directory

Self.pruneOrphans(in: url)

Expand Down
22 changes: 22 additions & 0 deletions Sources/XUtils/XToolCache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Foundation

package enum XToolCache {
package static var directory: URL {
let env = ProcessInfo.processInfo.environment
if let tmpdir = env["XTL_TMPDIR"], !tmpdir.isEmpty {
return URL(fileURLWithPath: tmpdir).appendingPathComponent("sh.xtool")
}
#if os(Linux)
let xdgCache: URL
if let xdgCachePath = env["XDG_CACHE_HOME"],
(xdgCachePath as NSString).isAbsolutePath {
xdgCache = URL(filePath: xdgCachePath)
} else {
xdgCache = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".cache")
Comment thread
kabiroberai marked this conversation as resolved.
}
return xdgCache.appending(component: "xtool")
#else
return URL.cachesDirectory.appending(component: "sh.xtool")
#endif
}
}
Loading