Swift软件包管理器无法编译通过Homebrew安装的ncurses [英] Swift package manager unable to compile ncurses installed through Homebrew

查看:219
本文介绍了Swift软件包管理器无法编译通过Homebrew安装的ncurses的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to use ncurses in a library using Swift Package Manager and I'd like to use a specific version of ncurses, not the one included in OS X. To do so I installed a more recent version (6.1) using Homebrew. This is how my Package.swift looks like:

// 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文件:

Under the Sources directory I have a subdirectory for Cncurses containing a module.modulemap and shim.h files:

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:

However, when compiling I get several errors complaining about conflicting types, apparently because ncurses is also provided by the macOS SDK:

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 */

...

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

I'm trying to compile the package using:

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

我也遵循了在软件包定义中指定pkgConfig的方法,得到了相同的结果.有人可以帮忙吗?

I also went down the route of specifying the pkgConfig on the package definition, with the same result. Can someone help?

仅供参考,值得一提的是,通过Homebrew安装ncurses时,由于OS X已经提供了ncurses,我会收到以下警告:

FYI It's worth mentioning that upon ncurses installation through Homebrew I get the following warning as ncurses is already provided by OS X:

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看起来像这样

Pkgconfig for ncurses looks like this

# 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中提供.

The main problem are conflicts of the header files, since ncurses is also supplied in /Applications/Xcode.app/.../MacOSX10.14.sdk/usr/include.

在这种情况下,常见的纯C解决方案是仅使用-I相应的-L指定自定义include和lib目录,这将起作用,请在此处查看有关C ncurses的答案:

A common pure C solution in such situations is to just specify the custom include and lib directories with -I repective -L and it would work, see my answer regarding C ncurses here: https://stackoverflow.com/a/56623033/2331445

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

This approach does not seem to work with the Swift package manager. But that doesn't mean it's not possible with a little effort.

可能的解决方案

我们需要确保macOS SDK提供的ncurses头文件被忽略.为此,我们可以为swift build命令指定-Xcc -D__NCURSES_H参数.

We need to make sure that the ncurses header files provided by the macOS SDK are ignored. We can do this by specifying the -Xcc -D__NCURSES_H parameter for the swift build command.

之所以可行,是因为在头文件中有一个典型的例子:

This works because in the header file, there is this typical:

#ifndef __NCURSES_H
#define __NCURSES_H
...
#endif

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

The problem, of course, is that our custom installation of ncurses using Brew is also affected. But we can work around it:

  • 将新的ncurses头文件复制到我们的Sources/Cncurses目录中
  • 通过其他方式替换__NCURSES_H,例如__CNCURSES_H(请注意以"C"开头)
  • 然后确保首先在我们的本地包含目录中搜索所有进一步嵌套的包含,方法是将包含的尖括号替换为引号,例如代替#include <ncursesw/unctrl.h>使用'#include"ncursesw/unctrl.h"'的形式
  • copy the new ncurses header files into our Sources/Cncurses directory
  • replace __NCURSES_H through something different, e.g. __CNCURSES_H (note leading 'C')
  • then make sure that all further nested includes are first searched in our local include directory by replace the angle brackets of the includes with quotes, so e.g. instead of #include <ncursesw/unctrl.h> the form '#include "ncursesw/unctrl.h"' is used

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

This can actually be done with the following command line commands:

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表达式,即是否执行

The last statement may require some explanation. With the help of an echo command, you can look at the generated sed expression, i.e. if you execute

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

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

As you can see, it searches and replaces only local available include files.

测试

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

For a test we need a simple ncurses example program. It should be built and we should make sure that the correct version of the library is used.

module.modulemap

我的头文件称为cncurses.h. module.modulemap看起来像这样:

My header file is called cncurses.h. The module.modulemap looks like this:

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

cncurses.h

cncurses.h是单行代码,它从我们的本地include文件夹中导入我们复制并自定义的ncurses.h文件:

cncurses.h is a one-liner, it imports our copied and customized ncurses.h file from our local include folder:

#include "include/ncurses.h"

main.swift

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

In the NcursesExample folder we have main.swift where we have a simple cncurses swift app:

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

请在此处注意systemLibrary目标中的pkgConfig: "ncurses":

Please note here the pkgConfig: "ncurses" in the systemLibrary targets:

// 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正常工作,我们必须首先调用以下命令:

For pkg-config to do its job properly, we must first call the following:

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

最后,我们使用以下命令启动构建:

Finally we initiate the build with:

swift build -Xcc -D__NCURSES_H 

因此,首先我们应该测试是否使用了正确的ncurses lib.我们可以这样:

So first we should test if the correct ncurses lib was used. We can do that with:

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

在其他行中,输出包含以下内容:

Among other lines, the output contains this:

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

看起来很有希望.

最后调用二进制文件:

Xcode项目

如果要生成Xcode项目,请使用以下命令:

If you want to generate a Xcode project, use the following command:

swift package generate-xcodeproj

然后在Xcode中加载项目并

Then load the project in Xcode and

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

这篇关于Swift软件包管理器无法编译通过Homebrew安装的ncurses的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆