Skip to content

Make nob__go_rebuild_urself preserve the exit code; Add nob_proc_wait_exit_code to the public API #273

Description

@fartsicle

I thought it'd be more natural to present how I originally wrote it out, but that changes the public API, and per the CONTRIBUTING.md that cannot be a PR. I hope this is acceptable.

The rational lies around the expectation for nob__go_rebuild_urself to be a "transparent" kind of wrapper around your e.g. nob.exe, i.e., when it needs to rebuild itself, your new executable shouldn't really change its behavior beyond your own changes. But because of the way the API is designed around compressing many types of errors into a boolean with functions like nob_cmd_run_opt, it so naturally ended up that nob__go_rebuild_urself compressed all nonzero error codes of the rebuilt version of itself into 1.

I think the lack of preservation here breaks expectations -- I did indeed expect this in some of my own code. So this is how I went about fixing it.

However, to note as an aside first:

Aside / Preamble on why its written this way

It would be "nicest" if actually the family of functions like nob_cmd_run_opt allowed for sync (easier) and async (more work) invocations to preserve exit codes if you wanted, or something offering like this. But this would require some more thinking and work, and I'm not sure if that's what you want. So instead I sort of "hand wrote" what nob_cmd_run_opt did for nob__go_rebuild_urself but just made sure to preserve the exit code this time, and so the function nob_proc_wait_exit_code sort of "popped out" of that.

Code

Add this below nob_proc_wait's top level declaration:

#ifdef _WIN32
typedef DWORD Nob_Exit_Code;
#else
typedef int Nob_Exit_Code;
#endif // _WIN32

// Wait until the process has finished, and save its exit code to the out pointer
NOBDEF bool nob_proc_wait_exit_code(Nob_Proc proc, Nob_Exit_Code *exit_code);

Then delete all of the code inside of nob_proc_wait and put it inside the implementation for nob_proc_wait_exit_code under the now vacant implementation for nob_proc_wait, and put some new code inside nob_proc_wait, as you'll see below:

NOBDEF bool nob_proc_wait(Nob_Proc proc)
{
    return nob_proc_wait_exit_code(proc, NULL);
}

NOBDEF bool nob_proc_wait_exit_code(Nob_Proc proc, Nob_Exit_Code *exit_code)
{
    // has the code that was previously inside `nob_proc_wait` above
}

Then inside nob_proc_wait_exit_code, under this part of the _WIN32 implementation, add the the commented "NEW" code:

    DWORD exit_status;
    if (!GetExitCodeProcess(proc, &exit_status)) {
        nob_log(NOB_ERROR, "could not get process exit code: %s", nob_win32_error_message(GetLastError()));
        return false;
    }

    // NEW
    if (exit_code != NULL) {
        *exit_code = exit_status;
    }
    // ...NEW

and under the #else / UNIX part do basically the same thing:

        if (WIFEXITED(wstatus)) {
            int exit_status = WEXITSTATUS(wstatus);
            // NEW
            if (exit_code != NULL) {
                *exit_code = exit_status;
            }
            // ...NEW

            if (exit_status != 0) {
                nob_log(NOB_ERROR, "command exited with exit code %d", exit_status);
                return false;
            }

            break;
        }

Then in nob__go_rebuild_urself, at the end of the function's implementation/body, change the original snippet which is:

#endif // NOB_EXPERIMENTAL_DELETE_OLD

    nob_cmd_append(&cmd, binary_path);
    nob_da_append_many(&cmd, argv, argc);
    if (!nob_cmd_run_opt(&cmd, opt)) exit(1);
    exit(0);

to this:

#endif // NOB_EXPERIMENTAL_DELETE_OLD

    nob_cmd_append(&cmd, binary_path);
    nob_da_append_many(&cmd, argv, argc);
// NEW below:
    Nob_Exit_Code exit_code = 1;
    Nob_Proc proc = nob__cmd_start_process(cmd, NULL, NULL, NULL);
    nob_proc_wait_exit_code(proc, &exit_code);
    exit(exit_code);

and then add nob_proc_wait_exit_code and the new type to the stripped alias definitions:

        #define proc_wait nob_proc_wait
// NEW
        #define Exit_Code Nob_Exit_Code
        #define proc_wait_exit_code nob_proc_wait_exit_code
// ...NEW
        #define procs_wait nob_procs_wait

That's about it. Am running this code currently on my Windows machine and it seems to work nice.

Thank you for taking the time to look at these ideas, and I hope they brought some value.
--Fart

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions