Build a Small ncurses Program with ncurses6-config
Linking against ncurses by hand means guessing library names and include paths that differ by distribution. ncurses6-config hands you the exact compiler and linker flags instead, and this walks through checking the installation, using those flags in a real build, and reading its metadata options when a build still does not match what you expected. Checked against Debian's libncurses-dev:amd64 version 6.4+20240113-1ubuntu2.2, whose helper reports ncurses 6.4.20240113.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow fifteen minutes: a shell, a C compiler and the development package. Everything here writes only to a temporary file under /tmp; nothing installs a package, touches a system library or needs elevated privileges.
1. Resolve the helper you are actually about to use
Start with an ordinary, read-only check of the command and its package:
$ command -v ncurses6-config
/usr/bin/ncurses6-config
$ dpkg-query -W -f='${Package} ${Version}\n' libncurses-dev:amd64
libncurses-dev:amd64 6.4+20240113-1ubuntu2.2
$ /usr/bin/ncurses6-config --version
6.4.20240113
Use the absolute path for the rest of this guide once you are diagnosing a build: a different executable sitting earlier in PATH can report different flags for a different ncurses release entirely. On this machine an unrelated Homebrew install also happens to provide a command with the same name, so trusting an unexamined PATH here would be a genuine source of confusion, not a hypothetical one.
Checkpoint
If command -v does not show the helper you expect, stop and check command -v gcc, echo "$PATH" and any environment-module or package-manager setup before you compile anything.
2. Read off the compiler and linker flags
The two options that matter for a normal C build are --cflags and --libs:
$ /usr/bin/ncurses6-config --cflags
-D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600
$ /usr/bin/ncurses6-config --libs
-lncurses -ltinfo
--cflags prints compiler flags, --libs prints the libraries needed at link time. Keep them where they belong: compiler flags and source files before the output option, library flags at the end. This output is configuration data, not something to execute on its own.
The helper also reports paths and version details that are handy when a development installation looks mismatched:
$ /usr/bin/ncurses6-config --prefix
/usr
$ /usr/bin/ncurses6-config --includedir
$ /usr/bin/ncurses6-config --libdir
$ /usr/bin/ncurses6-config --abi-version
6
$ /usr/bin/ncurses6-config --mouse-version
2
Blank --includedir and --libdir lines are valid output here, not an error. They mean no extra directory flag is needed for this installation, not that the package is missing headers or libraries. Do not paper over a blank line with a guessed -I or -L.
3. Write a harmless test program
A small program that starts ncurses, prints one line, waits for a keypress, then restores the terminal before exiting:
$ test_dir=$(mktemp -d /tmp/ncurses6-config.XXXXXX)
$ cat > "$test_dir/hello.c" <<'EOF'
#include <ncurses.h>
int main(void)
{
initscr();
printw("ncurses build works");
refresh();
getch();
endwin();
return 0;
}
EOF
This only touches the freshly created temporary directory, never anything with real files in it. To clean up, check the variable first with printf '%s\n' "$test_dir" so a failed assignment cannot turn rm -rf into something much broader, then remove it with rm -rf -- "$test_dir".
4. Compile and link using the reported values
Capture both outputs as shell variables so the configuration lives in one obvious place, then pass them to the compiler:
$ cflags=$(/usr/bin/ncurses6-config --cflags)
$ libs=$(/usr/bin/ncurses6-config --libs)
$ gcc $cflags "$test_dir/hello.c" -o "$test_dir/hello" $libs
$ printf 'exit status: %s\n' "$?"
exit status: 0
No sudo needed anywhere here. A compile failure saying ncurses.h: No such file or directory means check that the development package is installed and that the helper and compiler belong to the same environment. An unresolved symbol at link time means check the exact --libs output and make sure those libraries stay after the source and object files, not before.
Word splitting on these variables is intentional: they are compiler arguments from a trusted local helper. Do not reuse this pattern with untrusted text. For a real build system, either record the exact command or use the project's supported pkg-config integration if it has one.
5. Run it in a real terminal
Run the binary from an interactive terminal, not a redirected log or a background service:
$ "$test_dir/hello"
ncurses build works
Press a key when the message appears: getch() returns, endwin() restores the terminal, and the program exits. The screen may get cleared or redrawn along the way, so treat this as a smoke test, not a measure of terminal capability.
Checkpoint
Check the exit status straight after the binary returns:
$ printf 'program status: %s\n' "$?"
program status: 0
If a test gets interrupted before endwin() runs, the terminal can end up stuck in an odd mode. Type reset and press Enter to recover its display settings; that only affects the terminal session, not ncurses or the installed package.
6. Check metadata when a build does not add up
The helper also reports the directories this ncurses installation actually uses, via --bindir, --datadir, --mandir, --terminfo, and the supplemental search path in --terminfo-dirs:
$ /usr/bin/ncurses6-config --terminfo
/etc/terminfo
$ /usr/bin/ncurses6-config --terminfo-dirs
/etc/terminfo:/lib/terminfo:/usr/share/terminfo
$ /usr/bin/ncurses6-config --help
Usage: ncurses6-config [options]
These options describe the build and its data locations: they will not fix a missing terminal database entry or switch the active terminal type. If a program complains about an unknown terminal, check printf '%s\n' "$TERM" and the terminfo installation separately. Random compiler or linker flags will not solve a runtime data problem.
The manpage notes that --help and --version take precedence over other options. In scripts, use one option per invocation so the intent stays obvious, and capture its exit status if a failed configuration query should actually stop the build.
Done means
- Version confirmed.
/usr/bin/ncurses6-config --versionreports the ncurses release you intended. - Flags captured correctly.
--cflagsand--libssupplied the values the compiler actually used. - Test program works end to end. It compiles, links, runs, and exits with status 0 after restoring the terminal.
- One installation, not two. You confirmed the helper's path and the compiler's environment refer to the same install.