Ncurses on Linux with Swift

Hi all,

Darwin.ncurses is not available in Linux, is there an alternative library for building Terminal Interfaces with?

Many thanks,

You need to create a system library package with a modulemap. Here's one I used a while ago (I think the Package.swift format may have changed since then, though):

modulemap:

module CNcurses [system] {
  header "/usr/include/ncurses.h"
  link "ncurses"
  export *

  explicit module menu {
    header "/usr/include/menu.h"
    export *
  }
  explicit module panel {
    header "/usr/include/panel.h"
    export *
  }
}

Package.swift:

import PackageDescription

let package = Package(
    name: "CNcurses",
    pkgConfig: "ncurses",
    providers: [.Apt("libncurses5-devel")]
)

I also made my own macOS package because the system's default modulemap doesn't include the "menu" or "panel" headers. Also it allows you to unify the module names:

modulemap:

module CNcurses [system] {
  use Darwin.ncurses
  link "ncurses"
  export *

  explicit module menu {
    header "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/menu.h"
    export *
  }
  explicit module panel {
    header "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/panel.h"
    export *
  }
}

Package.swift:

import PackageDescription

let package = Package(
    name: "CNcurses"
)
4 Likes

I think using a shim header should allow you to use the same module map on both platforms, and also make it compatible with ncurses isntalled in non-standard locations.

2 Likes

Also, for any Apple people - I filed a radar for the gaps in the system modulemap 2 years ago (28162304).

1 Like

Thank you all for the very useful suggestions. I have a silly, dumbass question, as I struggle with SPM. I want to make a small matrix effect for the command line using Swift and the above CNcurses, however how to set SPM so it detects that /Sources/SwiftMatrix as one product and /Sources/CNCURSES as a library I will be calling inside /Sources/SwiftMatrix/main.swift?

I am stuck with the following:

// swift-tools-version:3.1

import PackageDescription

let package = Package(
	name: "swift-matrix",
    targets: [
        .target(
            name: "swift-matrix",
            dependencies: ["CNCURSES"]
            ),
        .target(
            name: "CNCURSES",
            pkgConfig: "ncurses",
            providers: [.Apt("libncurses5-devel")],
            targets: ["CNCURSES"]
            )
        ]
)
  • What is the proper SPM syntax in this case?

I still haven't found out how to manage a library within the package directory. But naturally found the solution of working with it in a separate directory as follows:

import PackageDescription

let package = Package(
    name: "SwiftMatrix",
    dependencies: [
        .package(url: "/home/luis/Dropbox/Documentos/Coding/Swift/Libraries/CNCURSES", from: "0.0.1")
    ],
    targets: [
        .target(
            name: "SwiftMatrix"
            )
    ]
)

You mean like use the path property for targets?

Something like this should work.

.target(name: "Foo", dependencies: [], path: "Sources/Foo"),

https://github.com/apple/swift-package-manager/blob/master/Documentation/PackageDescriptionV4.md#targets

1 Like

Do I just add

explicit module form {
    header "/usr/include/form.h"
    export *
  }

To get access to form.h?

I havenโ€™t tried it (and canโ€™t right now), but it looks like that should work