【问题标题】:Determine that Swift code is being run under Terminal确定 Swift 代码正在终端下运行
【发布时间】:2020-07-21 15:48:23
【问题描述】:

我正在实施一个带有自适应记录器的 swift 包,该记录器可以确定用于输出目的的环境。该记录器支持所有平台(macOS、iOS、tvOS 等),也可以从测试或 Swift 命令行应用程序中使用。

我的问题:当运行swift test 时,如何确定 Swift 代码正在终端应用程序下运行?

当您使用终端应用程序时,我发现 ProcessInfo 的环境属性有一个带有“/usr/bin/swift”或“/Users/.../TestApp”值的 "_" 字段,但我不确定这是正确的做法。

var isTerminal : Bool {
    return ProcessInfo.processInfo.environment["_"] != nil
}

还有其他方法可以检查吗?

【问题讨论】:

  • 这听起来有点像XY problem;最有可能的是,您不需要检测您是否在 Terminal.app 中,而是您的程序是否在交互式上下文中运行,或者颜色输出等特定功能是否可用。为什么您需要知道您使用的是哪种终端?
  • @NobodyNada 是的,颜色输出是原因之一

标签: swift macos terminal


【解决方案1】:

与其尝试检测您是否在使用 Terminal.app(例如,如果您使用不同的终端程序或在 Linux 上运行,这可能容易出错),您应该查询终端以了解它是否支持特定功能。这可以通过ncurses 库轻松完成。

1。将CCurses 目标添加到您的 Swift 包中

我们需要创建一个系统库目标,以便 Swift 包管理器知道如何查找和链接 ncurses 库将目标添加到您的Package.swift

let package = Package(
    // ...
    targets: [
        // ...
        .systemLibrary(name: "CCurses"),
    ],
    // ...
)

并在Sources/CCurses 创建一个新目录,其中包含以下文件:

curses.h

#include <curses.h>
#include <term.h>

module.modulemap

module CCurses [system] {
  header "curses.h"
  link "curses"
  export *
}

2。检查终端是否支持颜色输出

在设置时,查询 terminfo 数据库以检查颜色支持:

import CCurses

// ...

var erret: Int32 = 0
if setupterm(nil, 1, &erret) != ERR {
    useColor = has_colors()
} else {
    useColor = false
}

基于https://stackoverflow.com/a/7426284

如果您需要检查其他功能是否存在,几乎可以肯定有一个 ncurses 功能 - 只需检查 the man page、在线搜索或在此处询问。


ncurses dylib 在所有平台上都可用,但由于某种原因,标头仅存在于 macOS 上。无论如何,您在 iOS、watchOS 或 tvOS 上都不需要它,因为这些平台没有 终端。有两种方法可以排除在 iOS 上构建 ncurses 等:1)您可以在标头中使用#ifdef,或者 2)在 Swift 5.3 中,您可以声明一个 conditional target dependency,这会稍微简单和干净。

方法一:#ifdef 标头

改用以下Sources/CCurses/curses.h 文件:

#ifdef __APPLE__
    #include <TargetConditionals.h>
#endif

#if !(defined TARGET_OS_IPHONE || defined TARGET_OS_WATCH || defined TARGET_OS_TV)
    #include <curses.h>
    #include <term.h>
#endif

然后,每当你在 Swift 代码中使用来自 curses 的函数时,用构建条件包围它:

#if os(iOS) || os(watchOS) || os(tvOS)
    useColor = false
#else
    var erret: Int32 = 0
    if setupterm(nil, 1, &erret) != ERR {
        useColor = has_colors()
    } else {
        useColor = false
    }
#endif

方法 2:条件目标依赖(需要 Swift 5.3)

CCurses 目标无需更改;你只需要在你的Package.swift 中修改你对CCurses 的依赖:

.target(
    name: "MyLib",
    dependencies: [
        .target(name: "CCurses", condition: .when(platforms: [.macOS, .linux]))
]),

并在您 import CCurses 或使用 curses 函数时使用构建条件:

#if canImport(CCurses)
    import CCurses
#endif

// ...

#if canImport(CCurses)
    var erret: Int32 = 0
    if setupterm(nil, 1, &erret) != ERR {
        useColor = has_colors()
    } else {
        useColor = false
    }
#else
    useColor = false
#endif

【讨论】:

  • 你的答案很好,但不幸的是它只能在 Mac/Mac Catalyst 上工作,因为在其他平台上编译器找不到系统头文件 curses.h 和 term.h。
  • @iUrii 查看我的编辑。显然,iOS 等 SDK 中缺少标头,即使该库仍然存在。我假设您不需要在非桌面平台上使用终端功能,因此在为这些平台构建时完全禁用 ncurses 非常简单。
  • 有效!唯一需要注意的是,TARGET_OS_IPHONETARGET_OS_WATCH 等始终是为所有平台定义的,您应该检查它们的值 #if !(TARGET_OS_IPHONE || TARGET_OS_WATCH ...
猜你喜欢
  • 1970-01-01
  • 2016-10-22
  • 1970-01-01
  • 2016-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-19
相关资源
最近更新 更多