Skip to content

Commit e4dae5d

Browse files
authored
Merge pull request #1 from xcode-actions/feat/frizlab/windows
Make compilation pass on Windows
2 parents 4664361 + cd2ab5e commit e4dae5d

File tree

4 files changed

+314
-33
lines changed

4 files changed

+314
-33
lines changed

Sources/CLTLogger.swift

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import Foundation
2+
#if canImport(WinSDK)
3+
import WinSDK
4+
#endif
25

36
import Logging
47

@@ -188,8 +191,8 @@ public struct CLTLogger : LogHandler {
188191
}
189192

190193
private static func autoLogStyle(with fh: FileHandle) -> Style {
191-
if let s = getenv("CLTLOGGER_LOG_STYLE") {
192-
switch String(cString: s) {
194+
if let s = ProcessInfo.processInfo.environment["CLTLOGGER_LOG_STYLE"] {
195+
switch s {
193196
case "none": return .none
194197
case "color": return .color
195198
case "emoji": return .emoji
@@ -200,6 +203,11 @@ public struct CLTLogger : LogHandler {
200203

201204
/* * * The logging style is not defined specifically in the dedicated environment value: we try and detect a correct value depending on other environmental clues. * * */
202205

206+
if ProcessInfo.processInfo.environment["GITHUB_ACTIONS"] == "true" {
207+
/* GitHub does support colors. */
208+
return .color
209+
}
210+
#if !os(Windows)
203211
/* Is the fd on which we write a tty?
204212
* Most ttys nowadays support colors, with a notable exception: Xcode. */
205213
if isatty(fh.fileDescriptor) != 0 {
@@ -215,12 +223,12 @@ public struct CLTLogger : LogHandler {
215223
}
216224
/* If the TERM env var is not set we assume colors are not supported and return the text logging style.
217225
* In theory we should use the curses database to check for colors (ncurses has the `has_colors` function for this). */
218-
return (getenv("TERM") == nil ? .text : .color)
226+
return (ProcessInfo.processInfo.environment["TERM"] == nil ? .text : .color)
219227
}
220-
if let s = getenv("GITHUB_ACTIONS"), String(cString: s) == "true" {
221-
/* GitHub does support colors. */
222-
return .color
228+
#else
229+
if GetFileType(fh._handle) == FILE_TYPE_CHAR {
223230
}
231+
#endif
224232
/* Unknown case: we return the text logging style. */
225233
return .text
226234
}
@@ -263,41 +271,31 @@ public extension CLTLogger {
263271
}()
264272

265273
static func defaultConstantsByLogLevelForEmoji(on fh: FileHandle) -> [Logger.Level: Constants] {
266-
func addMeta(_ str: String, _ padding: String) -> Constants {
267-
var str = str
268-
if isatty(fh.fileDescriptor) != 0, tcgetpgrp(fh.fileDescriptor) == -1, errno == ENOTTY {
269-
/* We’re in Xcode (probably).
270-
* By default we do not do the emoji padding, unless explicitly asked to (`CLTLOGGER_TERMINAL_EMOJI` set to anything but “NO”). */
271-
if let s = getenv("CLTLOGGER_TERMINAL_EMOJI"), String(cString: s) != "NO" {
272-
str = str + padding
273-
}
274-
} else {
275-
/* We’re not in Xcode (probably).
276-
* By default we do the emoji padding, unless explicitly asked not to (`CLTLOGGER_TERMINAL_EMOJI` set to “NO”). */
277-
if let s = getenv("CLTLOGGER_TERMINAL_EMOJI"), String(cString: s) == "NO" {
278-
/*nop*/
279-
} else {
280-
str = str + padding
281-
}
282-
}
274+
func addMeta(_ paddedEmoji: String) -> Constants {
283275
return .init(
284-
logPrefix: str + "",
285-
multilineLogPrefix: str + " ",
276+
logPrefix: paddedEmoji + "",
277+
multilineLogPrefix: paddedEmoji + " ",
286278
metadataLinePrefix: "",
287279
metadataSeparator: " - ",
288280
logAndMetadataSeparator: " -- ",
289281
lineSeparator: "\n"
290282
)
291283
}
292-
/* The padding corrects alignment issues on the Terminal. */
284+
let envVars = ProcessInfo.processInfo.environment
285+
let outputEnvironment: OutputEnvironment = .detect(from: fh, envVars)
286+
let emojiSet = EmojiSet.default(for: outputEnvironment)
287+
/* To see all the emojis with the padding. If padding is correct, everything should be aligned. */
288+
//for emoji in Emoji.allCases {
289+
// print("\(emoji.rawValue)\(emoji.padding(for: outputEnvironment)) |")
290+
//}
293291
return [
294-
.trace: addMeta("💩", ""),
295-
.debug: addMeta("⚙️", " "),
296-
.info: addMeta("📔", ""),
297-
.notice: addMeta("🗣", " "),
298-
.warning: addMeta("⚠️", " "),
299-
.error: addMeta("❗️", ""),
300-
.critical: addMeta("‼️", " ")
292+
.trace: addMeta(emojiSet.paddedEmoji(for: .trace, in: outputEnvironment)),
293+
.debug: addMeta(emojiSet.paddedEmoji(for: .debug, in: outputEnvironment)),
294+
.info: addMeta(emojiSet.paddedEmoji(for: .info, in: outputEnvironment)),
295+
.notice: addMeta(emojiSet.paddedEmoji(for: .notice, in: outputEnvironment)),
296+
.warning: addMeta(emojiSet.paddedEmoji(for: .warning, in: outputEnvironment)),
297+
.error: addMeta(emojiSet.paddedEmoji(for: .error, in: outputEnvironment)),
298+
.critical: addMeta(emojiSet.paddedEmoji(for: .critical, in: outputEnvironment)),
301299
]
302300
}
303301

Sources/Emoji.swift

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import Foundation
2+
3+
4+
5+
internal enum Emoji : String, CaseIterable {
6+
7+
case poo = "💩"
8+
case cog = "⚙️"
9+
case notebook = "📔"
10+
case speaker = "🗣"
11+
case warning = "⚠️"
12+
case exclamationPoint = "❗️"
13+
case doubleExclamationPoint = "‼️"
14+
case eyebrow = "🤨"
15+
case redCross = ""
16+
case policeLight = "🚨"
17+
case worm = "🐛"
18+
case orangeDiamond = "🔶"
19+
20+
case ambulance = "🚑"
21+
case ladybug = "🐞"
22+
case monocle = "🧐"
23+
case greenCheck = ""
24+
case fearFace = "😱"
25+
26+
case redHeart = "❤️"
27+
case orangeHeart = "🧡"
28+
case yellowHeart = "💛"
29+
case greenHeart = "💚"
30+
case blueHeart = "💙"
31+
case purpleHeart = "💜"
32+
case blackHeart = "🖤"
33+
case greyHeart = "🩶"
34+
case brownHeart = "🤎"
35+
case whiteHeart = "🤍"
36+
case pinkHeart = "🩷"
37+
case lightBlueHeart = "🩵"
38+
39+
func padding(for environment: OutputEnvironment) -> String {
40+
guard environment != .xcode else {
41+
/* All emojis are correct on Xcode. */
42+
return ""
43+
}
44+
45+
switch self {
46+
case .poo, .notebook, .eyebrow, .redCross, .policeLight, .worm, .orangeDiamond,
47+
.orangeHeart, .yellowHeart, .greenHeart, .blueHeart, .purpleHeart,
48+
.blackHeart, .brownHeart, .whiteHeart:
49+
return ""
50+
51+
case .ambulance, .ladybug, .monocle, .greenCheck, .fearFace:
52+
return ""
53+
54+
case .cog, .warning, .doubleExclamationPoint, .redHeart:
55+
guard !environment.isVSCode, environment != .macOSTerminal
56+
else {return " "}
57+
return ""
58+
59+
case .speaker:
60+
guard !environment.isVSCode, !environment.isWindowsShell, environment != .macOSTerminal, environment != .macOSiTerm2
61+
else {return " "}
62+
return ""
63+
64+
case .exclamationPoint:
65+
/* Note: For the Windows Terminal and Console, we’re a negative 1 space…
66+
# We ignore this special case and return an empty string. */
67+
guard !environment.isWindowsShell
68+
else {return ""/*negative one space*/}
69+
return ""
70+
71+
case .greyHeart, .pinkHeart, .lightBlueHeart:
72+
guard !environment.isVSCode
73+
else {return " "}
74+
return ""
75+
}
76+
}
77+
78+
func valueWithPadding(for environment: OutputEnvironment) -> String {
79+
rawValue + padding(for: environment)
80+
}
81+
82+
}

Sources/EmojiSet.swift

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import Foundation
2+
3+
import Logging
4+
5+
6+
7+
internal enum EmojiSet : String {
8+
9+
/**
10+
The original set of emoji used in clt-logger.
11+
These work well in Terminal and Xcode (and on macOS generally, though not in VSCode). */
12+
case original = "ORIGINAL"
13+
case originalForWindowsTerminal = "ORIGINAL+WINDOWS_TERMINAL"
14+
case originalForVSCodeMacOS = "ORIGINAL+VSCODE_MACOS"
15+
case originalForVSCodeWindows = "ORIGINAL+VSCODE_WINDOWS"
16+
17+
case vaibhavsingh97EmojiLogger = "VAIBHAVSINGH97_EMOJI_LOGGER"
18+
case vaibhavsingh97EmojiLoggerForVSCodeMacOS = "VAIBHAVSINGH97_EMOJI_LOGGER+VSCODE_MACOS"
19+
20+
static func `default`(for environment: OutputEnvironment, _ envVars: [String: String] = ProcessInfo.processInfo.environment) -> EmojiSet {
21+
if let envStr = envVars["CLTLOGGER_EMOJI_SET_NAME"], let ret = EmojiSet(rawValue: envStr) {
22+
return ret
23+
}
24+
switch environment {
25+
case .xcode, .macOSTerminal, .macOSiTerm2, .macOSUnknown:
26+
return .original
27+
28+
case .macOSVSCode, .unknownVSCode, .unknown:
29+
return .originalForVSCodeMacOS
30+
31+
case .windowsTerminal, .windowsConsole, .windowsUnknown:
32+
return .originalForWindowsTerminal
33+
34+
case .windowsVSCode:
35+
return .originalForVSCodeWindows
36+
}
37+
}
38+
39+
/* Exceptions:
40+
* - ⚙️ on VSCode macOS renders as text
41+
* - ⚠️ on VSCode macOS renders as text
42+
* - ‼️ on VSCode macOS renders as text
43+
* - ❤️ on VSCode macOS renders as text
44+
* - 🗣 on VSCode Windows renders as text (I think)
45+
* - ‼️ on VSCode Windows renders as text
46+
* - ❗️ on Windows Terminal is larger than the rest (negative padding would be needed)
47+
* - ‼️ on Windows Terminal renders as text */
48+
func emoji(for logLevel: Logger.Level) -> Emoji {
49+
let original: (Logger.Level) -> Emoji = {
50+
switch $0 {
51+
case .critical: return .doubleExclamationPoint
52+
case .error: return .exclamationPoint
53+
case .warning: return .warning
54+
case .notice: return .speaker
55+
case .info: return .notebook
56+
case .debug: return .cog
57+
case .trace: return .poo
58+
}
59+
}
60+
let vaibhavsingh97: (Logger.Level) -> Emoji = {
61+
switch $0 {
62+
case .critical: return .ambulance
63+
case .error: return .fearFace
64+
case .warning: return .warning
65+
case .notice: return .greenCheck /* Called success in upstream. */
66+
case .info: return .monocle
67+
case .debug: return .ladybug
68+
case .trace: return .poo /* Does not exist in upstream. */
69+
}
70+
}
71+
72+
switch self {
73+
case .original:
74+
return original(logLevel)
75+
76+
case .originalForWindowsTerminal:
77+
switch logLevel {
78+
case .critical: return .policeLight
79+
case .error: return .redCross
80+
default: return original(logLevel)
81+
}
82+
83+
case .originalForVSCodeMacOS:
84+
switch logLevel {
85+
case .critical: return .policeLight
86+
case .warning: return .orangeDiamond
87+
case .debug: return .worm
88+
default: return original(logLevel)
89+
}
90+
91+
case .originalForVSCodeWindows:
92+
switch logLevel {
93+
case .critical: return .policeLight
94+
case .notice: return .eyebrow
95+
default: return original(logLevel)
96+
}
97+
98+
case .vaibhavsingh97EmojiLogger:
99+
return vaibhavsingh97(logLevel)
100+
101+
case .vaibhavsingh97EmojiLoggerForVSCodeMacOS:
102+
switch logLevel {
103+
case .warning: return .orangeDiamond
104+
default: return vaibhavsingh97(logLevel)
105+
}
106+
}
107+
}
108+
109+
func paddedEmoji(for logLevel: Logger.Level, in environment: OutputEnvironment) -> String {
110+
return emoji(for: logLevel).valueWithPadding(for: environment)
111+
}
112+
113+
}

Sources/OutputEnvironment.swift

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import Foundation
2+
3+
4+
5+
internal enum OutputEnvironment : String {
6+
7+
case xcode = "XCODE"
8+
9+
case macOSTerminal = "MACOS_TERMINAL"
10+
case macOSiTerm2 = "MACOS_ITERM2"
11+
case macOSVSCode = "MACOS_VSCODE"
12+
case macOSUnknown = "MACOS_UNKNOWN"
13+
14+
/* This value is never auto-detected.
15+
* We don’t know how to detect the Windows Terminal (TERM_PROGRAM is not set). */
16+
case windowsTerminal = "WINDOWS_TERMINAL"
17+
/* This value is never auto-detected.
18+
* We don’t know how to detect the Windows Console. */
19+
case windowsConsole = "WINDOWS_CONSOLE"
20+
case windowsVSCode = "WINDOWS_VSCODE"
21+
case windowsUnknown = "WINDOWS_UNKNOWN"
22+
23+
case unknownVSCode = "UNKNOWN_VSCODE"
24+
case unknown = "UNKNOWN"
25+
26+
var isVSCode: Bool {
27+
switch self {
28+
case .macOSVSCode, .windowsVSCode, .unknownVSCode: return true
29+
default: return false
30+
}
31+
}
32+
33+
var isWindowsShell: Bool {
34+
switch self {
35+
case .windowsTerminal, .windowsConsole, .windowsUnknown: return true
36+
default: return false
37+
}
38+
}
39+
40+
static func detect(from fh: FileHandle, _ envVars: [String: String] = ProcessInfo.processInfo.environment) -> OutputEnvironment {
41+
if let envStr = envVars["CLTLOGGER_OUTPUT_ENV"] {
42+
return OutputEnvironment(rawValue: envStr) ?? .unknown
43+
}
44+
45+
#if !os(Windows)
46+
/* Let’s detect Xcode. */
47+
if isatty(fh.fileDescriptor) != 0 && tcgetpgrp(fh.fileDescriptor) == -1 && errno == ENOTTY {
48+
return .xcode
49+
}
50+
#endif
51+
switch envVars["TERM_PROGRAM"] {
52+
case "Apple_Terminal":
53+
#if os(macOS)
54+
return .macOSTerminal
55+
#else
56+
return .unknown
57+
#endif
58+
59+
case "iTerm.app":
60+
#if os(macOS)
61+
return .macOSiTerm2
62+
#else
63+
return .unknown
64+
#endif
65+
66+
case "vscode":
67+
#if os(macOS)
68+
return .macOSVSCode
69+
#elseif os(Windows)
70+
return .windowsVSCode
71+
#else
72+
return .unknownVSCode
73+
#endif
74+
75+
default:
76+
#if os(macOS)
77+
return .macOSUnknown
78+
#elseif os(Windows)
79+
/* We don’t know how to detect the Windows Terminal env:
80+
* anything we have not previously detected on Windows is the Terminal. */
81+
return .windowsTerminal
82+
#else
83+
return .unknown
84+
#endif
85+
}
86+
}
87+
88+
}

0 commit comments

Comments
 (0)