我在 Xcode 中调试 ncurses 时遇到了同样的问题。最后,我找到了一种使用 Terminal.app 管理调试的好方法,它允许调试 ncurses。
众所周知,要初始化和使用 ncurses,我们需要在终端中运行我们的应用程序。但是当我们按下运行按钮时,Xcode 并没有打开终端。所以,如果我们TERM
从代码中请求环境变量,我们会得到NULL
. 这就是应用程序在initscr()
.
因此,需要启动 Terminal.app,在那里执行我们的进程并将调试器附加到它。可以通过Scheme setup来实现。我在 Xcode 11.4 中做到了。我创建了一个新的 macOS 命令行工具项目,基于Language:
C++
. 我libncurses.tbd
还在Frameworks and Libraries
.
转到Product > Scheme > Edit scheme...
,选择Run
方案和Run
操作并导航到Info
选项卡。您会看到Launch
设置为Automatically
. 将其更改为Wait for the executable to be launched
.
Pre-actions
在方案中选择Run
并添加New Run Script Action
. 更改Provide build settings from
为None
您的构建目标。在此处添加以下代码:
osascript -e 'tell application "Terminal"' -e 'delay 0.5' -e 'activate' -e "do script (\"$TARGET_BUILD_DIR/$PRODUCT_NAME\")" -e 'end tell' &
要在调试会话结束时选择关闭终端,请Post-actions
在Run
方案中选择并添加New Run Script Action
. 添加以下代码:
osascript -e 'activate application "Terminal"' -e 'delay 0.5' -e 'tell application "System Events"' -e 'tell process "Terminal"' -e 'keystroke "w" using {command down}' -e 'end tell' -e 'end tell'
实际上 osascript 将始终创建至少两个终端窗口,但如果您将第一个窗口保持打开状态,它将通过 Pre 和 Post 操作自动创建并销毁第二个终端窗口。
您可能还会遇到调试器附加问题。我不知道 Xcode 在外部执行进程时不想附加调试器的确切原因,但我发现了这个问题。此外,我stdin
在调试会话开始期间也发现流处于奇怪的状态。所以,我写了一个基于pselect
调用的解决方法。我要求stdin
任何数据,直到它不返回成功。我发现经过这些操作后,调试器会感觉正常,stdin
请求也会正常。这是我的代码示例:
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>
#include <sys/select.h>
#include <unistd.h>
#include <errno.h>
bool g_has_terminal = false; // Check this global variable before ncurses calls
bool ensure_debugger_attached_woraround(int timeout_ms)
{
fd_set fd_stdin;
FD_ZERO(&fd_stdin);
FD_SET(STDIN_FILENO, &fd_stdin);
struct timespec timeout = { timeout_ms / 1000, (timeout_ms % 1000) * 1000000 };
do
{
errno = 0;
}
while (pselect(STDIN_FILENO + 1, &fd_stdin, NULL, NULL, &timeout, NULL) < 0 && errno == EINTR);
if (errno != 0)
{
fprintf(stderr, "Unexpected error %d", errno);
return false;
}
return true;
}
int main(int argc, const char *argv[])
{
if (!ensure_debugger_attached_woraround(700))
return 1;
char *term = getenv("TERM");
g_has_terminal = (term != NULL);
if (g_has_terminal)
g_has_terminal = (initscr() != NULL);
// Some ncurses code. Maybe you should terminate if g_has_terminal is not set
if (g_has_terminal)
{
printw("Press any key to exit...");
refresh();
getch();
endwin();
}
return 0;
}
ensure_debugger_attached_woraround
调用超时 700 毫秒。我尝试了不同的值,发现 500 ms 是不可跳过的最小时间pselect
。也许这个超时取决于机器,我不知道。您可以通过或通过其他一些检查来包装此调用#ifdef ... #endif
,也许通过特殊的命令行参数检查来排除发布模式下的一点等待开销。