Quellcode durchsuchen

Merge pull request #13 from tsoding/5

(#5) Introduce PIPE(...) macro draft
Alexey Kutepov vor 4 Jahren
Ursprung
Commit
38903e53bb
10 geänderte Dateien mit 297 neuen und 42 gelöschten Zeilen
  1. 6 0
      ChangeLog.md
  2. 1 1
      README.md
  3. 2 1
      examples/.gitignore
  4. 1 1
      examples/file.c
  5. 2 2
      examples/foreach.c
  6. 1 1
      examples/logging.c
  7. 13 0
      examples/pipe.c
  8. 1 1
      examples/string.c
  9. 6 1
      nobuild.c
  10. 264 34
      nobuild.h

+ 6 - 0
ChangeLog.md

@@ -4,6 +4,12 @@
 
 *Not Released Yet*
 
+- Introduced `PIPE(...)` macro.
+
+## Deprecations
+
+- `FOREACH_VARGS(...)` is deprecated. Use `FOREACH_VARGS_TYPE(...)` instead.
+
 # 0.2.0 — Removing Everything
 
 *2021-01-30*

+ 1 - 1
README.md

@@ -8,7 +8,7 @@ The idea is that you should not need anything but a C compiler to build a C proj
 
 Try it out right here:
 
-```
+```console
 $ cc ./nobuild.c -o nobuild
 $ ./nobuild
 ```

+ 2 - 1
examples/.gitignore

@@ -1,4 +1,5 @@
 string
 foreach
 logging
-file
+file
+pipe

+ 1 - 1
examples/file.c

@@ -18,7 +18,7 @@ void print_file_recursively(const char *path)
     }
 }
 
-int main(int argc, char *argv[])
+int main(void)
 {
     DEMO(IS_DIR("./nobuild.c"));
     DEMO(IS_DIR("./examples"));

+ 2 - 2
examples/foreach.c

@@ -5,7 +5,7 @@ void foreach_vargs(int ignore, ...)
 {
     va_list args;
 
-    FOREACH_VARGS(ignore, arg, args, {
+    FOREACH_VARGS_TYPE(ignore, const char*, arg, args, {
         INFO("    %s", arg);
     });
 }
@@ -34,7 +34,7 @@ void foreach_file_in_dir(const char *dir_path)
         expr;                                   \
     } while(0)
 
-int main(int argc, char *argv[])
+int main(void)
 {
     DEMO(foreach_vargs(69, "foo", "bar", "baz", NULL));
     DEMO(foreach_array());

+ 1 - 1
examples/logging.c

@@ -1,7 +1,7 @@
 #define NOBUILD_IMPLEMENTATION
 #include "../nobuild.h"
 
-int main(int argc, char *argv[])
+int main(void)
 {
     INFO("    Informational Message");
     WARN("    Warning Message");

+ 13 - 0
examples/pipe.c

@@ -0,0 +1,13 @@
+#define NOBUILD_IMPLEMENTATION
+#include "../nobuild.h"
+
+int main(void)
+{
+#if _WIN32
+    WARN("Piping is not implemented on Windows yet");
+#else
+    PIPE(IN("./nobuild.c"), CHAIN("cat"));
+#endif
+
+    return 0;
+}

+ 1 - 1
examples/string.c

@@ -7,7 +7,7 @@
 #define DEMO_D(expr)                         \
     INFO("    " #expr " == %d", expr)
 
-int main(int argc, char *argv[])
+int main(void)
 {
     DEMO_S(CONCAT("foo", "bar", "baz"));
     DEMO_S(PATH("foo", "bar", "baz"));

+ 6 - 1
nobuild.c

@@ -1,6 +1,8 @@
 #define NOBUILD_IMPLEMENTATION
 #include "./nobuild.h"
 
+#define CFLAGS "-Wall", "-Wextra", "-std=c11", "-pedantic", "-ggdb"
+
 void check_example(const char *example)
 {
     const char *example_path = PATH("examples", NOEXT(example));
@@ -11,7 +13,10 @@ void check_example(const char *example)
     CMD("cl.exe", "/Fe.\\examples\\", PATH("examples", example));
     CMD(CONCAT(example_path, ".exe"));
 #else
-    CMD("cc", "-o", example_path, PATH("examples", example));
+    const char *cc = getenv("CC");
+    if (cc == NULL) cc = "cc";
+
+    CMD(cc, CFLAGS, "-o", example_path, PATH("examples", example));
     CMD(example_path);
 #endif // _WIN32
 }

+ 264 - 34
nobuild.h

@@ -30,6 +30,10 @@
 #ifndef NOBUILD_H_
 #define NOBUILD_H_
 
+#ifndef _WIN32
+#define _POSIX_C_SOURCE 200809L
+#endif // _WIN32
+
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -100,8 +104,10 @@
 #   include <sys/wait.h>
 #   include <unistd.h>
 #   include <dirent.h>
+#   include <fcntl.h>
 #endif // _WIN32
 
+
 // TODO(#1): no way to disable echo in nobuild scripts
 // TODO(#2): no way to ignore fails
 
@@ -113,8 +119,21 @@
 
 #define PATH_SEP_LEN (sizeof(PATH_SEP) - 1)
 
+#define FOREACH_VARGS_TYPE(param, type, arg, args, body)    \
+    do {                                                    \
+        va_start(args, param);                              \
+        for (type arg = va_arg(args, type);                 \
+             arg != NULL;                                   \
+             arg = va_arg(args, type))                      \
+        {                                                   \
+            body;                                           \
+        }                                                   \
+        va_end(args);                                       \
+    } while(0)
+
 #define FOREACH_VARGS(param, arg, args, body)               \
     do {                                                    \
+        WARN("DEPRECATED: Please don't use FOREACH_VARGS(...). Please use FOREACH_VARGS_TYPE(...) instead. FOREACH_VARGS(...) will be removed on the next major release."); \
         va_start(args, param);                              \
         for (const char *arg = va_arg(args, const char *);  \
              arg != NULL;                                   \
@@ -166,6 +185,33 @@ const char *remove_ext(const char *path);
 char *shift(int *argc, char ***argv);
 void nobuild__rm(const char *path);
 
+#ifndef _WIN32
+void nobuild__posix_wait_for_pid(pid_t pid);
+#endif
+
+typedef enum {
+    PIPE_ARG_END,
+    PIPE_ARG_IN,
+    PIPE_ARG_OUT,
+    PIPE_ARG_CHAIN,
+} Pipe_Arg_Type;
+
+const char *nobuild__pipe_arg_as_cstr(Pipe_Arg_Type type);
+
+typedef struct {
+    Pipe_Arg_Type type;
+    const char** args;
+} Pipe_Arg;
+
+void nobuild__pipe(int ignore, ...);
+Pipe_Arg nobuild__make_pipe_arg(Pipe_Arg_Type type, ...);
+
+#define PIPE(...) nobuild__pipe(69, __VA_ARGS__, nobuild__make_pipe_arg(PIPE_ARG_END, NULL))
+// TODO(#17): IN and OUT are already taken by WinAPI
+#define IN(path) nobuild__make_pipe_arg(PIPE_ARG_IN, path, NULL)
+#define OUT(path) nobuild__make_pipe_arg(PIPE_ARG_OUT, path, NULL)
+#define CHAIN(...) nobuild__make_pipe_arg(PIPE_ARG_CHAIN, __VA_ARGS__, NULL)
+
 #define CONCAT(...) concat_impl(69, __VA_ARGS__, NULL)
 #define CONCAT_SEP(sep, ...) build__deprecated_concat_sep(sep, __VA_ARGS__, NULL)
 #define JOIN(sep, ...) build__join(sep, __VA_ARGS__, NULL)
@@ -277,7 +323,7 @@ const char *build__join(const char *sep, ...)
 
     va_list args;
 
-    FOREACH_VARGS(sep, arg, args, {
+    FOREACH_VARGS_TYPE(sep, const char*, arg, args, {
         length += strlen(arg);
         seps_count += 1;
     });
@@ -288,7 +334,7 @@ const char *build__join(const char *sep, ...)
     char *result = malloc(length + seps_count * sep_len + 1);
 
     length = 0;
-    FOREACH_VARGS(sep, arg, args, {
+    FOREACH_VARGS_TYPE(sep, const char*, arg, args, {
         size_t n = strlen(arg);
         memcpy(result + length, arg, n);
         length += n;
@@ -315,7 +361,7 @@ const char *build__deprecated_concat_sep(const char *sep, ...)
 
     va_list args;
 
-    FOREACH_VARGS(sep, arg, args, {
+    FOREACH_VARGS_TYPE(sep, const char*, arg, args, {
         length += strlen(arg);
         seps_count += 1;
     });
@@ -326,7 +372,7 @@ const char *build__deprecated_concat_sep(const char *sep, ...)
     char *result = malloc(length + seps_count * sep_len + 1);
 
     length = 0;
-    FOREACH_VARGS(sep, arg, args, {
+    FOREACH_VARGS_TYPE(sep, const char*, arg, args, {
         size_t n = strlen(arg);
         memcpy(result + length, arg, n);
         length += n;
@@ -353,7 +399,7 @@ const char *concat_sep_impl(const char *sep, ...)
 
     va_list args;
 
-    FOREACH_VARGS(sep, arg, args, {
+    FOREACH_VARGS_TYPE(sep, const char*, arg, args, {
         length += strlen(arg);
         seps_count += 1;
     });
@@ -364,7 +410,7 @@ const char *concat_sep_impl(const char *sep, ...)
     char *result = malloc(length + seps_count * sep_len + 1);
 
     length = 0;
-    FOREACH_VARGS(sep, arg, args, {
+    FOREACH_VARGS_TYPE(sep, const char*, arg, args, {
         size_t n = strlen(arg);
         memcpy(result + length, arg, n);
         length += n;
@@ -388,7 +434,7 @@ void mkdirs_impl(int ignore, ...)
 
     va_list args;
 
-    FOREACH_VARGS(ignore, arg, args, {
+    FOREACH_VARGS_TYPE(ignore, const char*, arg, args, {
         length += strlen(arg);
         seps_count += 1;
     });
@@ -399,7 +445,7 @@ void mkdirs_impl(int ignore, ...)
     char *result = malloc(length + seps_count * PATH_SEP_LEN + 1);
 
     length = 0;
-    FOREACH_VARGS(ignore, arg, args, {
+    FOREACH_VARGS_TYPE(ignore, const char*, arg, args, {
         size_t n = strlen(arg);
         memcpy(result + length, arg, n);
         length += n;
@@ -430,14 +476,14 @@ const char *concat_impl(int ignore, ...)
 {
     size_t length = 0;
     va_list args;
-    FOREACH_VARGS(ignore, arg, args, {
+    FOREACH_VARGS_TYPE(ignore, const char*, arg, args, {
         length += strlen(arg);
     });
 
     char *result = malloc(length + 1);
 
     length = 0;
-    FOREACH_VARGS(ignore, arg, args, {
+    FOREACH_VARGS_TYPE(ignore, const char*, arg, args, {
         size_t n = strlen(arg);
         memcpy(result + length, arg, n);
         length += n;
@@ -473,25 +519,7 @@ void nobuild_exec(const char **argv)
             exit(1);
         }
     } else {
-        for (;;) {
-            int wstatus = 0;
-            wait(&wstatus);
-
-            if (WIFEXITED(wstatus)) {
-                int exit_status = WEXITSTATUS(wstatus);
-                if (exit_status != 0) {
-                    ERRO("command exited with exit code %d", exit_status);
-                    exit(1);
-                }
-
-                break;
-            }
-
-            if (WIFSIGNALED(wstatus)) {
-                ERRO("command process was terminated by signal %d", WTERMSIG(wstatus));
-                exit(1);
-            }
-        }
+        nobuild__posix_wait_for_pid(cpid);
     }
 #endif // _WIN32
 }
@@ -501,14 +529,14 @@ void cmd_impl(int ignore, ...)
     size_t argc = 0;
 
     va_list args;
-    FOREACH_VARGS(ignore, arg, args, {
+    FOREACH_VARGS_TYPE(ignore, const char*, arg, args, {
         argc += 1;
     });
 
     const char **argv = malloc(sizeof(const char*) * (argc + 1));
 
     argc = 0;
-    FOREACH_VARGS(ignore, arg, args, {
+    FOREACH_VARGS_TYPE(ignore, const char*, arg, args, {
         argv[argc++] = arg;
     });
     argv[argc] = NULL;
@@ -540,6 +568,8 @@ const char *remove_ext(const char *path)
 {
     WARN("DEPRECATED: Please use `NOEXT(path)` instead of `remove_ext(path)`. `remove_ext(path)` will be removed in the next major release.");
     nobuild__remove_ext(path);
+
+    return NULL;
 }
 
 char *shift(int *argc, char ***argv)
@@ -616,7 +646,7 @@ int nobuild__is_dir(const char *path)
         exit(1);
     }
 
-    return (statbuf.st_mode & S_IFMT) == S_IFDIR;
+    return S_ISDIR(statbuf.st_mode);
 #endif // _WIN32
 }
 
@@ -630,7 +660,7 @@ void nobuild__rm(const char *path)
         });
 
         if (rmdir(path) < 0) {
-            if (errno = ENOENT) {
+            if (errno == ENOENT) {
                 WARN("directory %s does not exist");
             } else {
                 ERRO("could not remove directory %s: %s", path, strerror(errno));
@@ -639,7 +669,7 @@ void nobuild__rm(const char *path)
         }
     } else {
         if (unlink(path) < 0) {
-            if (errno = ENOENT) {
+            if (errno == ENOENT) {
                 WARN("file %s does not exist");
             } else {
                 ERRO("could not remove file %s: %s", path, strerror(errno));
@@ -649,4 +679,204 @@ void nobuild__rm(const char *path)
     }
 }
 
+#ifdef _WIN32
+void nobuild__pipe(int ignore, ...)
+{
+    ERRO("TODO(#14): piping is not implemented on Windows at all");
+    exit(1);
+}
+#else
+void nobuild__pipe(int ignore, ...)
+{
+    const char *input_filepath = NULL;
+    const char *output_filepath = NULL;
+    size_t cmds_count = 0;
+
+    va_list args;
+    va_start(args, ignore);
+    {
+        Pipe_Arg arg = va_arg(args, Pipe_Arg);
+        while (arg.type != PIPE_ARG_END) {
+            switch (arg.type) {
+            case PIPE_ARG_IN: {
+                if (input_filepath == NULL) {
+                    input_filepath = arg.args[0];
+                } else {
+                    // TODO(#15): PIPE does not report where exactly a syntactic error has happened
+                    ERRO("input file was already set for the pipe");
+                    exit(1);
+                }
+            } break;
+
+            case PIPE_ARG_OUT: {
+                if (output_filepath == NULL) {
+                    output_filepath = arg.args[0];
+                } else {
+                    ERRO("output file was already set for the pipe");
+                    exit(1);
+                }
+            } break;
+
+            case PIPE_ARG_CHAIN: {
+                cmds_count += 1;
+            } break;
+
+            case PIPE_ARG_END:
+            default: {
+                assert(0 && "unreachable");
+            }
+            }
+
+            arg = va_arg(args, Pipe_Arg);
+        }
+    }
+    va_end(args);
+
+    if (cmds_count == 0) {
+        ERRO("no chains provided for the pipe");
+        exit(1);
+    }
+
+    Pipe_Arg *cmds = malloc(sizeof(Pipe_Arg) * cmds_count);
+    pid_t *cpids = malloc(sizeof(pid_t) * cmds_count);
+
+    cmds_count = 0;
+    va_start(args, ignore);
+    {
+        Pipe_Arg arg = va_arg(args, Pipe_Arg);
+        while (arg.type != PIPE_ARG_END) {
+            if (arg.type == PIPE_ARG_CHAIN) {
+                cmds[cmds_count++] = arg;
+            }
+            arg = va_arg(args, Pipe_Arg);
+        }
+    }
+    va_end(args);
+
+    // TODO(#16): input/output piping is not implemented for Linux
+    for (size_t i = 0; i < cmds_count; ++i) {
+        cpids[i] = fork();
+        if (cpids[i] < 0) {
+            ERRO("could not fork a child: %s", strerror(errno));
+            exit(1);
+        }
+
+        if (cpids[i] == 0) {
+            // Chain is first and there is input file
+            if (i == 0) {
+                if (input_filepath != NULL) {
+                    int fdin = open(input_filepath, O_RDONLY);
+                    if (fdin < 0) {
+                        ERRO("could not open file %s: %s",
+                             input_filepath, strerror(errno));
+                        exit(1);
+                    }
+
+                    if (dup2(fdin, STDIN_FILENO) < 0) {
+                        ERRO("could not duplication file descriptor for %s: %s",
+                             input_filepath, strerror(errno));
+                        exit(1);
+                    }
+                }
+            } else {
+                assert(0 && "input piping is not implemented");
+            }
+
+            // Chain is last and there is output file
+            if (i == cmds_count - 1) {
+                if (output_filepath != NULL) {
+                    int fdout = open(output_filepath,
+                                     O_WRONLY | O_CREAT | O_TRUNC,
+                                     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+                    if (fdout < 0) {
+                        ERRO("could not open file %s: %s",
+                             output_filepath, strerror(errno));
+                        exit(1);
+                    }
+
+                    if (dup2(fdout, STDOUT_FILENO) < 0) {
+                        ERRO("could not duplication file descriptor for %s: %s",
+                             output_filepath, strerror(errno));
+                        exit(1);
+                    }
+                }
+            } else {
+                assert(0 && "output piping is not implemented");
+            }
+
+            if (execvp(cmds[i].args[0], (char * const*) cmds[i].args) < 0) {
+                ERRO("could not execute command: %s", strerror(errno));
+                exit(1);
+            }
+        }
+    }
+
+    for (size_t i = 0; i < cmds_count; ++i) {
+        nobuild__posix_wait_for_pid(cpids[i]);
+    }
+}
+#endif // _WIN32
+
+#ifndef _WIN32
+void nobuild__posix_wait_for_pid(pid_t pid)
+{
+    for (;;) {
+        int wstatus = 0;
+        waitpid(pid, &wstatus, 0);
+
+        if (WIFEXITED(wstatus)) {
+            int exit_status = WEXITSTATUS(wstatus);
+            if (exit_status != 0) {
+                ERRO("command exited with exit code %d", exit_status);
+                exit(1);
+            }
+
+            break;
+        }
+
+        if (WIFSIGNALED(wstatus)) {
+            ERRO("command process was terminated by %s", strsignal(WTERMSIG(wstatus)));
+            exit(1);
+        }
+    }
+}
+#endif
+
+Pipe_Arg nobuild__make_pipe_arg(Pipe_Arg_Type type, ...)
+{
+    Pipe_Arg result = {
+        .type = type
+    };
+
+    va_list args;
+
+    size_t count = 0;
+    FOREACH_VARGS_TYPE(type, const char*, arg, args, {
+        count += 1;
+    });
+
+    result.args = malloc(sizeof(const char*) * (count + 1));
+
+    count = 0;
+    FOREACH_VARGS_TYPE(type, const char*, arg, args, {
+        result.args[count++] = arg;
+    });
+    result.args[count] = NULL;
+
+    return result;
+}
+
+const char *nobuild__pipe_arg_as_cstr(Pipe_Arg_Type type)
+{
+    switch (type) {
+    case PIPE_ARG_IN: return "PIPE_ARG_IN";
+    case PIPE_ARG_OUT: return "PIPE_ARG_OUT";
+    case PIPE_ARG_CHAIN: return "PIPE_ARG_CHAIN";
+    default: {
+        assert(0 && "nobuild__pipe_arg_as_cstr: unreachable");
+        return NULL;
+    }
+    }
+}
+
 #endif // NOBUILD_IMPLEMENTATION