Xcode and Curses.h with Error opening terminal

末鹿安然 提交于 2019-11-30 14:21:50

I had the same problem with ncurses debugging in Xcode. Finally I've found a good way for me to manage debugging with Terminal.app that allows to debug ncurses.

As we know, to initialise and use ncurses we need to run our application in terminal. But Xcode doesn't open terminal when we press run button. So, if we request environment variable TERM in from code, we'll get NULL. This is why application crashes on initscr().

But Xcode allows us to setup Launch option for Run scheme (Product > Scheme > Edit scheme... > Run) to "Wait for executable to be launched" instead of default "Automatically":

Now we can press Run in Xcode and after launch our application manually in Terminal. So debugger will be attached to the application. There are 2 problems in that:

  1. If say honestly, debugger will not be attached self without additional actions and it will skip all breakpoints. But we can manage it by calling getchar() at the beginning of our program. I solved this problem by introducing command line argument that indicates we are debugging in terminal:

    for (int argi = 1; argi < argc; argi++)
    {
        if (strcmp(argv[argi], "--debug-in-terminal") == 0)
        {
            printf("Debugging in terminal enabled\n");
            getchar(); // Without this call debugging will be skipped
            break;
        }
    }
    

    So, we can turn on getchar() call only when we want to debug inside Terminal.app

  2. We need to open Terminal and launch the application manually each time when we press Run in Xcode. This is really annoying. So, I decided to automate it through Pre-actions in Run scheme settings. The idea is to open Terminal and run our application in it. But this pre-action should launch the application AFTER Xcode will execute Run action to let it "Wait for executable to be launched" first. So we need a background non-blocking task with a delay. This can be achieved by following shell command (& in the end needed to run in the background):

    osascript -e 'tell application "Terminal"' -e 'delay 0.5' -e "set currentTab to do script (\"$TARGET_BUILD_DIR/$PRODUCT_NAME --debug-in-terminal\")" -e 'end tell' &
    

    Don't forget to select your application in "Provide build settings from" list to make accessible important environment variables $TARGET_BUILD_DIR and $PRODUCT_NAME:

    So, now when we press Run, Xcode will wait for executable to attach, Terminal will be opened and our application will be executed with command line option --debug-in-terminal and all breakpoints will be triggered.

    Pretty nice, but it will be even better to close this Terminal window in the end, because it will generate new one for each debug session. Let's do it in Post-actions for Run scheme:

    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'
    

    This command just closes active terminal window, so maybe this Post-action is not needed if you plan to use terminal for some other purposes during debug session, because you can close some important window accidentally.

And finally, an idea of safe ncurses code, that checks if we can use ncurses or no:

#include <stdlib.h>
#include <string.h>
#include <ncurses.h>


bool IsTerminalAvailable = false; // Check this global variable before ncurses calls


int main(int argc, const char *argv[])
{
    for (int argi = 1; argi < argc; argi++)
    {
        if (strcmp(argv[argi], "--debug-in-terminal") == 0)
        {
            printf("Debugging in terminal enabled\n");
            getchar(); // Without this call debugging will be skipped
            break;
        }
    }

    char *term = getenv("TERM");

    IsTerminalAvailable = (term != NULL);

    if (IsTerminalAvailable)
        IsTerminalAvailable = (initscr() != NULL);

    // Do some code here....

    if (IsTerminalAvailable)
    {
        printw("Press any key to exit...");
        refresh();

        getch();

        endwin();
    }

    return 0;
}

There is no terminal to attach to in the Xcode IDE. Instead run the program from a shell (via Terminal application)

./build/Debug/myprogram

If you want to use the IDE debugger (which is just gdb) you can attach to the process. Fist get the process id,

gdb> attach mypid

For a more convenient method, I'll quote Step into Xcode: Mac OS X development

Open the Executables group in the Groups & Files list, select the application, open an Info window, and in the Debugging tab, uncheck Start executable after starting debugger. When you're ready to debug, start the debugger, and then then launch the target application in its friendly environment. In the debugger's gdb console, type attach myprogram, and your debugging sesssion is under way.

In XCode 8 you can choose to run within the terminal, from the Edit Scheme... Options page.

Though in my quick testing it doesn't seem to work all that well; it sometimes (not always) seems to 'lose' the debuggee, or the debuggee never starts, and thinks it's still running. If you try to exit, Xcode gets stuck. I have found that if you find and then kill a process called lldb-rpc-server you can avoid having to force-quit.

In more detail (in case this helps anyone) whenever the debuggee fails to start, I open a Terminal and type

ps x | grep lldb

then

kill 12345

where 12345 is the process ID that ps gives me.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!