Swift 包管理器无法编译通过 Homebrew 安装的 ncurses

标签 swift ncurses swift-package-manager

我正在尝试使用 Swift 包管理器在库中使用 ncurses,我想使用特定版本的 ncurses,而不是 OS X 中包含的版本。 为此,我使用 Homebrew 安装了更新版本 (6.1)。 这就是我的 Package.swift 的样子:

// swift-tools-version:5.0
import PackageDescription

let package = Package(
    name: "NcursesExample",
    products: [
        .executable(name: "NcursesExample", targets: ["NcursesExample"]),
    ],
    dependencies: [
    ],
    targets: [
        .systemLibrary(name: "Cncurses"),
        .target(name: "NcursesExample", dependencies: ["Cncurses"]),
    ]
)

在 Sources 目录下,我有一个 Cncurses 的子目录,其中包含一个 module.modulemapshim.h 文件:

module.modulemap

module Cncurses {
  header "shim.h"
  link "ncurses"
  export *
}

shim.h

#include "/usr/local/Cellar/ncurses/6.1/include/ncurses.h"

但是,在编译时我遇到了几个提示类型冲突的错误,显然是因为 macOS SDK 也提供了 ncurses:

shim.h:1:10: note: in file included from shim.h:1:
#include "/usr/local/Cellar/ncurses/6.1/include/ncurses.h"
         ^
/usr/local/Cellar/ncurses/6.1/include/ncurses.h:60:10: error: 'ncursesw/ncurses_dll.h' file not found with <angled> include; use "quotes" instead
#include <ncursesw/ncurses_dll.h>
         ^
<module-includes>:1:9: note: in file included from <module-includes>:1:
#import "shim.h"
        ^
shim.h:1:10: note: in file included from shim.h:1:
#include "/usr/local/Cellar/ncurses/6.1/include/ncurses.h"
         ^
/usr/local/Cellar/ncurses/6.1/include/ncurses.h:674:45: error: conflicting types for 'keyname'
extern NCURSES_EXPORT(NCURSES_CONST char *) keyname (int);              /* implemented */
                                            ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/include/curses.h:598:45: note: previous declaration is here
extern NCURSES_EXPORT(NCURSES_CONST char *) keyname (int);              /* implemented */

...

我正在尝试使用以下方法编译包:

swift build -Xcc -I/usr/local/Cellar/ncurses/6.1/include/-Xlinker -L/usr/local/Cellar/ncurses/6.1/lib

我也采用了在包定义中指定 pkgConfig 的方法,结果相同。有人可以帮忙吗?

仅供引用值得一提的是,在通过 Homebrew 安装 ncurses 时,我收到以下警告,因为 OS X 已经提供了 ncurses:

ncurses is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.

If you need to have ncurses first in your PATH run:
  echo 'export PATH="/usr/local/opt/ncurses/bin:$PATH"' >> ~/.zshrc

For compilers to find ncurses you may need to set:
  export LDFLAGS="-L/usr/local/opt/ncurses/lib"
  export CPPFLAGS="-I/usr/local/opt/ncurses/include"

For pkg-config to find ncurses you may need to set:
  export PKG_CONFIG_PATH="/usr/local/opt/ncurses/lib/pkgconfig"

ncurses 的 Pkgconfig 看起来像这样

# pkg-config file generated by gen-pkgconfig
# vile:makemode

prefix=/usr/local/Cellar/ncurses/6.1
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include/ncursesw
abi_version=6
major_version=6
version=6.1.20180127

Name: ncursesw
Description: ncurses 6.1 library
Version: ${version}
URL: https://invisible-island.net/ncurses
Requires.private:
Libs:  -L${libdir} -lncursesw
Libs.private:
Cflags:  -D_DARWIN_C_SOURCE -I/usr/local/Cellar/ncurses/6.1/include -I${includedir}

最佳答案

问题

主要问题是头文件的冲突,因为 ncurses 也在/Applications/Xcode.app/.../MacOSX10.14.sdk/usr/include 中提供。

在这种情况下,一种常见的纯 C 解决方案是仅使用 -I 和 -L 指定自定义包含和 lib 目录,它会起作用,请在此处查看我关于 C ncurses 的回答:https://stackoverflow.com/a/56623033/2331445

这种方法似乎不适用于 Swift 包管理器。但这并不意味着不费吹灰之力就不可能。

可能的解决方案

我们需要确保忽略 macOS SDK 提供的 ncurses 头文件。我们可以通过为 swift build 命令指定 -Xcc -D__NCURSES_H 参数来做到这一点。

这是有效的,因为在头文件中,有这样的典型:

#ifndef __NCURSES_H
#define __NCURSES_H
...
#endif

当然,问题在于我们使用 Brew 自定义安装的 ncurses 也会受到影响。但我们可以解决它:

  • 将新的 ncurses 头文件复制到我们的 Sources/Cncurses 目录
  • 用不同的东西替换 __NCURSES_H,例如__CNCURSES_H(注意前导“C”)
  • 然后确保首先在我们的本地包含目录中搜索所有进一步嵌套的包含,方法是用引号替换包含的尖括号,例如而不是 #include <ncursesw/unctrl.h>使用的形式是“#include "ncursesw/unctrl.h"”

这实际上可以通过以下命令行命令完成:

cd Sources/Cncurses
cp -r /usr/local/Cellar/ncurses/6.1/include include
find . -name '*.h' -exec sed -i ''  's/__NCURSES_H/__CNCURSES_H/g' {} \;
find . -name '*.h' -exec sed -i '' -E -e "s/<(.*(`find . -name '*.h' -exec basename {} \; |  paste -sd "|" -`))>/\"\1\"/g"  {} \;

最后的陈述可能需要一些解释。在 echo 命令的帮助下,您可以查看生成的 sed 表达式,即如果您执行

echo "s/<(.*(`find . -name '*.h' -exec basename {} \; |  paste -sd "|" -`))>/\"\1\"/g" 

你得到以下输出:

s/<(.*(termcap.h|form.h|term.h|panel.h|ncurses.h|termcap.h|cursesp.h|cursesf.h|etip.h|form.h|cursesw.h|nc_tparm.h|unctrl.h|cursesapp.h|term.h|cursslk.h|panel.h|ncurses.h|tic.h|eti.h|ncurses_dll.h|term_entry.h|menu.h|cursesm.h|curses.h|curses.h|cncurses.h))>/"\1"/g

如您所见,它仅搜索和替换本地可用的包含文件。

测试

为了进行测试,我们需要一个简单的 ncurses 示例程序。它应该被构建,我们应该确保使用正确版本的库。

module.modulemap

我的头文件叫做 cncurses.h。 module.modulemap 看起来像这样:

module cncurses [system] 
{
    umbrella header "cncurses.h"
    link "ncurses"
    export *
}

cncurses.h

cncurses.h 是单行代码,它从本地包含文件夹导入我们复制和定制的 ncurses.h 文件:

#include "include/ncurses.h"

main.swift

在 NcursesExample 文件夹中,我们有 main.swift,我们有一个简单的 cncurses swift 应用程序:

import cncurses

initscr()
curs_set(0) 
move(5, 10)
addstr("NCURSES")
move(10, 10)
addstr("Hello World!")
refresh() 

select(0, nil, nil, nil, nil)

Package.swift

请注意此处的 pkgConfig: "ncurses"在系统库目标中:

// swift-tools-version:5.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "NcursesExample",
    dependencies: [
    ],
    targets: [
        .systemLibrary(name: "cncurses", pkgConfig: "ncurses"),
        .target(name: "NcursesExample", dependencies: ["cncurses"]),
        .testTarget(
            name: "NcursesExampleTests",
            dependencies: ["NcursesExample"]),

    ]
)

构建

要使 pkg-config 正常工作,我们必须首先调用以下命令:

export PKG_CONFIG_PATH="/usr/local/opt/ncurses/lib/pkgconfig"

最后我们开始构建:

swift build -Xcc -D__NCURSES_H 

所以首先我们应该测试是否使用了正确的 ncurses 库。我们可以这样做:

otool -L .build/x86_64-apple-macosx/debug/NcursesExample

除其他行外,输出包含以下内容:

/usr/local/opt/ncurses/lib/libncursesw.6.dylib (compatibility version 6.0.0, current version 6.0.0)

看起来很有希望。

最后调用二进制文件:

ncurses demo

Xcode 项目

如果你想生成一个Xcode工程,使用下面的命令:

swift package generate-xcodeproj

然后在Xcode中加载项目

  • 选择项目节点
  • 在build设置中,在右上角的搜索字段中输入预处理器
  • 在 Apple Clang - 预处理/预处理宏下添加 __NCURSES_H=1 用于调试和发布

关于Swift 包管理器无法编译通过 Homebrew 安装的 ncurses,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56251835/

相关文章:

swift - 核心 WLAN MCS 索引?

ios - 数据将 TableView 单元格传递给 Swift 中的选项卡栏 View Controller

swift - 为什么 Swift 结构中的私有(private)变量在该结构的#if'd init 中不可用?

具有独立输入区和输出区的 Linux 终端仿真器?

ncurses - 如何在 NCurses 中添加窗口填充?

ios - 运行后 `swift build` dependencies are downloaded by class is not found

像 CocoaPods 一样带有子模块的 Swift 包

swift - 如何防止 Spacer 使 VStack 贪婪地增长超出必要范围?

ios - 映射到 Realm 结果到 NSDictionary 错误