Диспетчер пакетов Swift не может скомпилировать ncurses, установленные через Homebrew - PullRequest
8 голосов
/ 22 мая 2019

Я пытаюсь использовать ncurses в библиотеке с помощью Swift Package Manager, и я хотел бы использовать конкретную версию ncurses, а не ту, которая включена в OS X. Для этого я установил более свежую версию (6.1)используя Homebrew.Вот как выглядит мой 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.modulemap и shim.h:

.modulemap

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

shim.h

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

Однако при компиляции я получаю несколько ошибок с жалобами на конфликтующие типы, по-видимому, потому что ncurses такжепредоставляемый 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 */

...

Я пытаюсь скомпилировать пакет, используя:

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

Я также пошел по пути указания pkgConfig на определение пакета, с тем же результатом.Может кто-нибудь помочь?

К вашему сведению Стоит отметить, что после установки ncurses через Homebrew я получаю следующее предупреждение, поскольку ncurses уже предоставляется 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"

Pkgconfig для ncurses выглядит следующим образом

# 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}

Ответы [ 3 ]

1 голос
/ 24 июня 2019

Проблема

Основная проблема - конфликты заголовочных файлов, поскольку ncurses также предоставляется в /Applications/Xcode.app/.../MacOSX10.14.sdk/usr/include.

Обычное решение на чистом C в таких ситуациях состоит в том, чтобы просто указать пользовательские каталоги include и lib с ключом -I reverse -L, и это сработает, см. мой ответ относительно C ncurses здесь: https://stackoverflow.com/a/56623033/2331445

Этот подход не работает с менеджером пакетов Swift.Но это не значит, что это невозможно без особых усилий.

Возможное решение

Нам нужно убедиться, что заголовочные файлы ncurses, предоставляемые macOS SDK,игнорируются.Мы можем сделать это, указав параметр -Xcc -D__NCURSES_H для команды быстрой сборки.

Это работает, потому что в заголовочном файле есть такая типичная проблема:

#ifndef __NCURSES_H
#define __NCURSES_H
...
#endif

ПроблемаКонечно, это влияет на нашу пользовательскую установку ncurses с использованием Brew.Но мы можем обойти это:

  • скопировать новые заголовочные файлы 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

Как видите,он ищет и заменяет только локальные доступные включаемые файлы.

Test

Для теста нам понадобится простая программа-пример 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 "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 и

  • выберите узел проекта
  • в настройках сборки введите Preprocessor в поле поиска в правом верхнем углу
  • под Apple Clang - Макросы предварительной обработки / предварительной обработки добавляют __NCURSES_H = 1 для отладки и выпуска
0 голосов
/ 21 июня 2019

я бы поменял

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

до

#include <ncurses.h>

потому что у вас уже есть

-I/usr/local/Cellar/ncurses/6.1/include/

Читая формулу , вы должны иметь

/usr/local/Cellar/ncurses/6.1/include/ncursesw/ncurses_dll.h

(и некоторые другие вещи), но использование заключенного в кавычки может помешать пути поиска, который установлен параметром -I (см., Например, this ).

0 голосов
/ 29 мая 2019

Я не слишком разбираюсь в этой теме, но, похоже, вы захотите добавить в свой проект скрипт сборки для этого.В Xcode, под выбранным проектом, перейдите в Фазы сборки> Фаза нового сценария запуска

enter image description here

В этом сценарии добавьте свои флаги:

export LDFLAGS="-L/usr/local/opt/ncurses/lib"
export CPPFLAGS="-I/usr/local/opt/ncurses/include"

Скрипт сборки должен запускаться при установке.Я не уверен, если вам нужен флаг pkg-config здесь.Надеюсь, это поможет вам направить вас куда-то в правильном направлении.

...