1{
2 lib,
3 stdenv,
4 fetchurl,
5 buildPackages,
6 updateAutotoolsGnuConfigScriptsHook,
7 ncurses,
8 pkg-config,
9 abiVersion ? "6",
10 enableStatic ? stdenv.hostPlatform.isStatic,
11 withCxx ? !stdenv.hostPlatform.useAndroidPrebuilt,
12 mouseSupport ? false,
13 gpm,
14 withTermlib ? false,
15 unicodeSupport ? true,
16 testers,
17 binlore,
18}:
19
20stdenv.mkDerivation (finalAttrs: {
21 version = "6.5";
22 pname = "ncurses" + lib.optionalString (abiVersion == "5") "-abi5-compat";
23
24 src = fetchurl {
25 url = "https://invisible-island.net/archives/ncurses/ncurses-${finalAttrs.version}.tar.gz";
26 hash = "sha256-E22RvCaamleF5fnpgLx2q1dCj2BM4+WlqQzrx2eXHMY=";
27 };
28
29 outputs = [
30 "out"
31 "dev"
32 "man"
33 ];
34 setOutputFlags = false; # some aren't supported
35
36 # see other isOpenBSD clause below
37 configurePlatforms =
38 if stdenv.hostPlatform.isOpenBSD then
39 [ "build" ]
40 else
41 [
42 "build"
43 "host"
44 ];
45
46 configureFlags =
47 [
48 (lib.withFeature (!enableStatic) "shared")
49 "--without-debug"
50 "--enable-pc-files"
51 "--enable-symlinks"
52 "--with-manpage-format=normal"
53 "--disable-stripping"
54 "--with-versioned-syms"
55 ]
56 ++ lib.optional unicodeSupport "--enable-widec"
57 ++ lib.optional (!withCxx) "--without-cxx"
58 ++ lib.optional (abiVersion == "5") "--with-abi-version=5"
59 ++ lib.optional stdenv.hostPlatform.isNetBSD "--enable-rpath"
60 ++ lib.optional withTermlib "--with-termlib"
61 ++ lib.optionals stdenv.hostPlatform.isWindows [
62 "--enable-sp-funcs"
63 "--enable-term-driver"
64 ]
65 ++ lib.optionals (stdenv.hostPlatform.isUnix && enableStatic) [
66 # For static binaries, the point is to have a standalone binary with
67 # minimum dependencies. So here we make sure that binaries using this
68 # package won't depend on a terminfo database located in the Nix store.
69 "--with-terminfo-dirs=${
70 lib.concatStringsSep ":" [
71 "/etc/terminfo" # Debian, Fedora, Gentoo
72 "/lib/terminfo" # Debian
73 "/usr/share/terminfo" # upstream default, probably all FHS-based distros
74 "/run/current-system/sw/share/terminfo" # NixOS
75 ]
76 }"
77 ]
78 ++ lib.optionals (stdenv.buildPlatform != stdenv.hostPlatform) [
79 "--with-build-cc=${buildPackages.stdenv.cc}/bin/${buildPackages.stdenv.cc.targetPrefix}cc"
80 ]
81 ++ (lib.optionals (stdenv.cc.bintools.isLLVM && lib.versionAtLeast stdenv.cc.bintools.version "17")
82 [
83 # lld17+ passes `--no-undefined-version` by default and makes this a hard
84 # error; ncurses' `resulting.map` version script references symbols that
85 # aren't present.
86 #
87 # See: https://lists.gnu.org/archive/html/bug-ncurses/2024-05/msg00086.html
88 #
89 # For now we allow this with `--undefined-version`:
90 "LDFLAGS=-Wl,--undefined-version"
91 ]
92 )
93 ++ lib.optionals stdenv.hostPlatform.isOpenBSD [
94 # If you don't specify the version number in the host specification, a branch gets taken in configure
95 # which assumes that your openbsd is from the 90s, leading to a truly awful compiler/linker configuration.
96 # No, autoreconfHook doesn't work.
97 "--host=${stdenv.hostPlatform.config}${stdenv.cc.libc.version}"
98 ];
99
100 # Only the C compiler, and explicitly not C++ compiler needs this flag on solaris:
101 CFLAGS = lib.optionalString stdenv.hostPlatform.isSunOS "-D_XOPEN_SOURCE_EXTENDED";
102
103 strictDeps = true;
104
105 nativeBuildInputs =
106 [
107 updateAutotoolsGnuConfigScriptsHook
108 pkg-config
109 ]
110 ++ lib.optionals (stdenv.buildPlatform != stdenv.hostPlatform) [
111 # for `tic`, build already depends on for build `cc` so it's weird the build doesn't just build `tic`.
112 ncurses
113 ];
114
115 buildInputs = lib.optional (mouseSupport && stdenv.hostPlatform.isLinux) gpm;
116
117 preConfigure =
118 ''
119 export PKG_CONFIG_LIBDIR="$dev/lib/pkgconfig"
120 mkdir -p "$PKG_CONFIG_LIBDIR"
121 configureFlagsArray+=(
122 "--libdir=$out/lib"
123 "--includedir=$dev/include"
124 "--bindir=$dev/bin"
125 "--mandir=$man/share/man"
126 "--with-pkg-config-libdir=$PKG_CONFIG_LIBDIR"
127 )
128 ''
129 + lib.optionalString stdenv.hostPlatform.isSunOS ''
130 sed -i -e '/-D__EXTENSIONS__/ s/-D_XOPEN_SOURCE=\$cf_XOPEN_SOURCE//' \
131 -e '/CPPFLAGS="$CPPFLAGS/s/ -D_XOPEN_SOURCE_EXTENDED//' \
132 configure
133 CFLAGS=-D_XOPEN_SOURCE_EXTENDED
134 '';
135
136 enableParallelBuilding = true;
137
138 doCheck = false;
139
140 postFixup =
141 let
142 abiVersion-extension =
143 if stdenv.hostPlatform.isDarwin then "${abiVersion}.$dylibtype" else "$dylibtype.${abiVersion}";
144 in
145 ''
146 # Determine what suffixes our libraries have
147 suffix="$(awk -F': ' 'f{print $3; f=0} /default library suffix/{f=1}' config.log)"
148 ''
149 # When building a wide-character (Unicode) build, create backward
150 # compatibility links from the the "normal" libraries to the
151 # wide-character libraries (e.g. libncurses.so to libncursesw.so).
152 + lib.optionalString unicodeSupport ''
153 libs="$(ls $dev/lib/pkgconfig | tr ' ' '\n' | sed "s,\(.*\)$suffix\.pc,\1,g")"
154 suffixes="$(echo "$suffix" | awk '{for (i=1; i < length($0); i++) {x=substr($0, i+1, length($0)-i); print x}}')"
155
156 # Get the path to the config util
157 cfg=$(basename $dev/bin/ncurses*-config)
158
159 # symlink the full suffixed include directory
160 ln -svf . $dev/include/ncurses$suffix
161
162 for newsuffix in $suffixes ""; do
163 # Create a non-abi versioned config util links
164 ln -svf $cfg $dev/bin/ncurses$newsuffix-config
165
166 # Allow for end users who #include <ncurses?w/*.h>
167 ln -svf . $dev/include/ncurses$newsuffix
168
169 for library in $libs; do
170 for dylibtype in so dll dylib; do
171 if [ -e "$out/lib/lib''${library}$suffix.$dylibtype" ]; then
172 ln -svf lib''${library}$suffix.$dylibtype $out/lib/lib$library$newsuffix.$dylibtype
173 ln -svf lib''${library}$suffix.${abiVersion-extension} $out/lib/lib$library$newsuffix.${abiVersion-extension}
174 if [ "ncurses" = "$library" ]
175 then
176 # make libtinfo symlinks
177 ln -svf lib''${library}$suffix.$dylibtype $out/lib/libtinfo$newsuffix.$dylibtype
178 ln -svf lib''${library}$suffix.${abiVersion-extension} $out/lib/libtinfo$newsuffix.${abiVersion-extension}
179 fi
180 fi
181 done
182 for statictype in a dll.a la; do
183 if [ -e "$out/lib/lib''${library}$suffix.$statictype" ]; then
184 ln -svf lib''${library}$suffix.$statictype $out/lib/lib$library$newsuffix.$statictype
185 if [ "ncurses" = "$library" ]
186 then
187 # make libtinfo symlinks
188 ln -svf lib''${library}$suffix.$statictype $out/lib/libtinfo$newsuffix.$statictype
189 fi
190 fi
191 done
192 ln -svf ''${library}$suffix.pc $dev/lib/pkgconfig/$library$newsuffix.pc
193 done
194 done
195 ''
196 # Unconditional patches. Leading newline is to avoid mass rebuilds.
197 + ''
198
199 # add pkg-config aliases for libraries that are built-in to libncurses(w)
200 for library in tinfo tic; do
201 for suffix in "" ${lib.optionalString unicodeSupport "w"}; do
202 ln -svf ncurses$suffix.pc $dev/lib/pkgconfig/$library$suffix.pc
203 done
204 done
205
206 # move some utilities to $bin
207 # these programs are used at runtime and don't really belong in $dev
208 moveToOutput "bin/clear" "$out"
209 moveToOutput "bin/reset" "$out"
210 moveToOutput "bin/tabs" "$out"
211 moveToOutput "bin/tic" "$out"
212 moveToOutput "bin/tput" "$out"
213 moveToOutput "bin/tset" "$out"
214 moveToOutput "bin/captoinfo" "$out"
215 moveToOutput "bin/infotocap" "$out"
216 moveToOutput "bin/infocmp" "$out"
217 '';
218
219 preFixup = lib.optionalString (!stdenv.hostPlatform.isCygwin && !enableStatic) ''
220 rm "$out"/lib/*.a
221 '';
222
223 # I'm not very familiar with ncurses, but it looks like most of the
224 # exec here will run hard-coded executables. There's one that is
225 # dynamic, but it looks like it only comes from executing a terminfo
226 # file, so I think it isn't going to be under user control via CLI?
227 # Happy to have someone help nail this down in either direction!
228 # The "capability" is 'iprog', and I could only find 1 real example:
229 # https://invisible-island.net/ncurses/terminfo.ti.html#tic-linux-s
230 passthru.binlore.out = binlore.synthesize ncurses ''
231 execer cannot bin/{reset,tput,tset}
232 '';
233
234 meta = with lib; {
235 homepage = "https://www.gnu.org/software/ncurses/";
236 description = "Free software emulation of curses in SVR4 and more";
237 longDescription = ''
238 The Ncurses (new curses) library is a free software emulation of curses in
239 System V Release 4.0, and more. It uses Terminfo format, supports pads and
240 color and multiple highlights and forms characters and function-key
241 mapping, and has all the other SYSV-curses enhancements over BSD Curses.
242
243 The ncurses code was developed under GNU/Linux. It has been in use for
244 some time with OpenBSD as the system curses library, and on FreeBSD and
245 NetBSD as an external package. It should port easily to any
246 ANSI/POSIX-conforming UNIX. It has even been ported to OS/2 Warp!
247 '';
248 license = licenses.mit;
249 pkgConfigModules =
250 let
251 base = [
252 "form"
253 "menu"
254 "ncurses"
255 "panel"
256 ] ++ lib.optional withCxx "ncurses++";
257 in
258 base ++ lib.optionals unicodeSupport (map (p: p + "w") base);
259 platforms = platforms.all;
260 };
261
262 passthru = {
263 ldflags = "-lncurses";
264 inherit unicodeSupport abiVersion;
265 tests.pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage;
266 };
267})