| author | |
| committer | |
| log | 763ce1c4852eaee22c648c6909190f73d2ca775b |
| tree | 21655b93329db0ded45e08a9e7f3296f5fb96205 |
| parent | 893e152dabd6352eaa6aaff95c521cea5cd2d9a5 |
7 files changed, 262 insertions(+), 7 deletions(-)
CMakeLists.txt+19-1| ... | ... | @@ -33,17 +33,30 @@ set(ZIG_SOURCES |
| 33 | 33 | "${CMAKE_SOURCE_DIR}/src/os.cpp" |
| 34 | 34 | ) |
| 35 | 35 | |
| 36 | set(TEST_SOURCES | |
| 37 | "${CMAKE_SOURCE_DIR}/src/buffer.cpp" | |
| 38 | "${CMAKE_SOURCE_DIR}/src/util.cpp" | |
| 39 | "${CMAKE_SOURCE_DIR}/src/os.cpp" | |
| 40 | "${CMAKE_SOURCE_DIR}/test/standalone.cpp" | |
| 41 | ) | |
| 42 | ||
| 43 | ||
| 36 | 44 | set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h") |
| 37 | 45 | configure_file ( |
| 38 | 46 | "${CMAKE_SOURCE_DIR}/src/config.h.in" |
| 39 | 47 | ${CONFIGURE_OUT_FILE} |
| 40 | 48 | ) |
| 41 | 49 | |
| 50 | include_directories( | |
| 51 | ${CMAKE_SOURCE_DIR} | |
| 52 | ${CMAKE_BINARY_DIR} | |
| 53 | "${CMAKE_SOURCE_DIR}/src" | |
| 54 | ) | |
| 55 | ||
| 42 | 56 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wno-unused-variable -Wno-unused-but-set-variable") |
| 43 | 57 | |
| 44 | 58 | set(EXE_CFLAGS "-std=c++11 -fno-exceptions -fno-rtti -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Werror -Wall -Werror=strict-prototypes -Werror=old-style-definition -Werror=missing-prototypes") |
| 45 | 59 | |
| 46 | ||
| 47 | 60 | add_executable(zig ${ZIG_SOURCES}) |
| 48 | 61 | set_target_properties(zig PROPERTIES |
| 49 | 62 | COMPILE_FLAGS ${EXE_CFLAGS}) |
| ... | ... | @@ -52,3 +65,8 @@ target_link_libraries(zig LINK_PUBLIC |
| 52 | 65 | ) |
| 53 | 66 | install(TARGETS zig DESTINATION bin) |
| 54 | 67 | |
| 68 | add_executable(run_tests ${TEST_SOURCES}) | |
| 69 | target_link_libraries(run_tests) | |
| 70 | set_target_properties(run_tests PROPERTIES | |
| 71 | COMPILE_FLAGS ${EXE_CFLAGS} | |
| 72 | ) |
README.md+11-2| ... | ... | @@ -31,7 +31,6 @@ readable, safe, optimal, and concise code to solve any computing problem. |
| 31 | 31 | |
| 32 | 32 | ## Roadmap |
| 33 | 33 | |
| 34 | * Unit tests. | |
| 35 | 34 | * C style comments. |
| 36 | 35 | * Simple .so library |
| 37 | 36 | * Multiple files |
| ... | ... | @@ -66,7 +65,7 @@ Root : many(TopLevelDecl) token(EOF) |
| 66 | 65 | |
| 67 | 66 | TopLevelDecl : FnDef | ExternBlock |
| 68 | 67 | |
| 69 | ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace) | |
| 68 | ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnDecl) token(RBrace) | |
| 70 | 69 | |
| 71 | 70 | FnProto : token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type) |
| 72 | 71 | |
| ... | ... | @@ -96,3 +95,13 @@ FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen |
| 96 | 95 | |
| 97 | 96 | Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen) |
| 98 | 97 | ``` |
| 98 | ||
| 99 | ### Building | |
| 100 | ||
| 101 | ``` | |
| 102 | mkdir build | |
| 103 | cd build | |
| 104 | cmake .. | |
| 105 | make | |
| 106 | ./run_tests | |
| 107 | ``` |
src/os.cpp+84| ... | ... | @@ -10,6 +10,11 @@ |
| 10 | 10 | |
| 11 | 11 | #include <unistd.h> |
| 12 | 12 | #include <errno.h> |
| 13 | #include <sys/types.h> | |
| 14 | #include <sys/stat.h> | |
| 15 | #include <sys/wait.h> | |
| 16 | #include <stdio.h> | |
| 17 | #include <fcntl.h> | |
| 13 | 18 | |
| 14 | 19 | void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached) { |
| 15 | 20 | pid_t pid = fork(); |
| ... | ... | @@ -32,6 +37,24 @@ void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detache |
| 32 | 37 | zig_panic("execvp failed: %s", strerror(errno)); |
| 33 | 38 | } |
| 34 | 39 | |
| 40 | static void read_all_fd(int fd, Buf *out_buf) { | |
| 41 | static const ssize_t buf_size = 8192; | |
| 42 | buf_resize(out_buf, buf_size); | |
| 43 | ssize_t actual_buf_len = 0; | |
| 44 | for (;;) { | |
| 45 | ssize_t amt_read = read(fd, buf_ptr(out_buf), buf_len(out_buf)); | |
| 46 | if (amt_read < 0) | |
| 47 | zig_panic("fd read error"); | |
| 48 | actual_buf_len += amt_read; | |
| 49 | if (amt_read == 0) { | |
| 50 | buf_resize(out_buf, actual_buf_len); | |
| 51 | return; | |
| 52 | } | |
| 53 | ||
| 54 | buf_resize(out_buf, actual_buf_len + buf_size); | |
| 55 | } | |
| 56 | } | |
| 57 | ||
| 35 | 58 | void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) { |
| 36 | 59 | int last_index = buf_len(full_path) - 1; |
| 37 | 60 | if (last_index >= 0 && buf_ptr(full_path)[last_index] == '/') { |
| ... | ... | @@ -49,3 +72,64 @@ void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) { |
| 49 | 72 | buf_init_from_buf(out_basename, full_path); |
| 50 | 73 | } |
| 51 | 74 | |
| 75 | void os_exec_process(const char *exe, ZigList<const char *> &args, | |
| 76 | int *return_code, Buf *out_stderr, Buf *out_stdout) | |
| 77 | { | |
| 78 | int stdin_pipe[2]; | |
| 79 | int stdout_pipe[2]; | |
| 80 | int stderr_pipe[2]; | |
| 81 | ||
| 82 | int err; | |
| 83 | if ((err = pipe(stdin_pipe))) | |
| 84 | zig_panic("pipe failed"); | |
| 85 | if ((err = pipe(stdout_pipe))) | |
| 86 | zig_panic("pipe failed"); | |
| 87 | if ((err = pipe(stderr_pipe))) | |
| 88 | zig_panic("pipe failed"); | |
| 89 | ||
| 90 | pid_t pid = fork(); | |
| 91 | if (pid == -1) | |
| 92 | zig_panic("fork failed"); | |
| 93 | if (pid == 0) { | |
| 94 | // child | |
| 95 | if (dup2(stdin_pipe[0], STDIN_FILENO) == -1) | |
| 96 | zig_panic("dup2 failed"); | |
| 97 | ||
| 98 | if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1) | |
| 99 | zig_panic("dup2 failed"); | |
| 100 | ||
| 101 | if (dup2(stderr_pipe[1], STDERR_FILENO) == -1) | |
| 102 | zig_panic("dup2 failed"); | |
| 103 | ||
| 104 | const char **argv = allocate<const char *>(args.length + 2); | |
| 105 | argv[0] = exe; | |
| 106 | argv[args.length + 1] = nullptr; | |
| 107 | for (int i = 0; i < args.length; i += 1) { | |
| 108 | argv[i + 1] = args.at(i); | |
| 109 | } | |
| 110 | execvp(exe, const_cast<char * const *>(argv)); | |
| 111 | zig_panic("execvp failed: %s", strerror(errno)); | |
| 112 | } else { | |
| 113 | // parent | |
| 114 | close(stdin_pipe[0]); | |
| 115 | close(stdout_pipe[1]); | |
| 116 | close(stderr_pipe[1]); | |
| 117 | ||
| 118 | waitpid(pid, return_code, 0); | |
| 119 | ||
| 120 | read_all_fd(stdout_pipe[0], out_stdout); | |
| 121 | read_all_fd(stderr_pipe[0], out_stderr); | |
| 122 | ||
| 123 | } | |
| 124 | } | |
| 125 | ||
| 126 | void os_write_file(Buf *full_path, Buf *contents) { | |
| 127 | int fd; | |
| 128 | if ((fd = open(buf_ptr(full_path), O_CREAT|O_CLOEXEC|O_WRONLY|O_TRUNC, S_IRWXU)) == -1) | |
| 129 | zig_panic("open failed"); | |
| 130 | ssize_t amt_written = write(fd, buf_ptr(contents), buf_len(contents)); | |
| 131 | if (amt_written != buf_len(contents)) | |
| 132 | zig_panic("write failed: %s", strerror(errno)); | |
| 133 | if (close(fd) == -1) | |
| 134 | zig_panic("close failed"); | |
| 135 | } |
src/os.hpp+4| ... | ... | @@ -12,8 +12,12 @@ |
| 12 | 12 | #include "buffer.hpp" |
| 13 | 13 | |
| 14 | 14 | void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached); |
| 15 | void os_exec_process(const char *exe, ZigList<const char *> &args, | |
| 16 | int *return_code, Buf *out_stderr, Buf *out_stdout); | |
| 15 | 17 | |
| 16 | 18 | void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename); |
| 17 | 19 | |
| 20 | void os_write_file(Buf *full_path, Buf *contents); | |
| 21 | ||
| 18 | 22 | |
| 19 | 23 | #endif |
test/add.h deleted-1| ... | ... | @@ -1 +0,0 @@ |
| 1 | int add(int a, int b); |
test/add.zig deleted-3| ... | ... | @@ -1,3 +0,0 @@ |
| 1 | export fn add(a: i32, b: i32) -> i32 { | |
| 2 | return a + b; | |
| 3 | } |
test/standalone.cpp created+144| ... | ... | @@ -0,0 +1,144 @@ |
| 1 | /* | |
| 2 | * Copyright (c) 2015 Andrew Kelley | |
| 3 | * | |
| 4 | * This file is part of zig, which is MIT licensed. | |
| 5 | * See http://opensource.org/licenses/MIT | |
| 6 | */ | |
| 7 | ||
| 8 | #include "list.hpp" | |
| 9 | #include "buffer.hpp" | |
| 10 | #include "os.hpp" | |
| 11 | ||
| 12 | #include <stdio.h> | |
| 13 | ||
| 14 | struct TestSourceFile { | |
| 15 | const char *relative_path; | |
| 16 | const char *text; | |
| 17 | }; | |
| 18 | ||
| 19 | struct TestCase { | |
| 20 | const char *case_name; | |
| 21 | const char *output; | |
| 22 | const char *source; | |
| 23 | ZigList<const char *> compile_errors; | |
| 24 | ZigList<const char *> compiler_args; | |
| 25 | ZigList<const char *> program_args; | |
| 26 | }; | |
| 27 | ||
| 28 | ZigList<TestCase*> test_cases = {0}; | |
| 29 | const char *tmp_source_path = ".tmp_source.zig"; | |
| 30 | const char *tmp_exe_path = "./.tmp_exe"; | |
| 31 | ||
| 32 | static void add_simple_case(const char *case_name, const char *source, const char *output) { | |
| 33 | TestCase *test_case = allocate<TestCase>(1); | |
| 34 | test_case->case_name = case_name; | |
| 35 | test_case->output = output; | |
| 36 | test_case->source = source; | |
| 37 | ||
| 38 | test_case->compiler_args.append("build"); | |
| 39 | test_case->compiler_args.append(tmp_source_path); | |
| 40 | test_case->compiler_args.append("--output"); | |
| 41 | test_case->compiler_args.append(tmp_exe_path); | |
| 42 | test_case->compiler_args.append("--release"); | |
| 43 | test_case->compiler_args.append("--strip"); | |
| 44 | ||
| 45 | test_cases.append(test_case); | |
| 46 | } | |
| 47 | ||
| 48 | static void add_all_test_cases(void) { | |
| 49 | add_simple_case("hello world with libc", R"SOURCE( | |
| 50 | #link("c") | |
| 51 | extern { | |
| 52 | fn puts(s: *mut u8) -> i32; | |
| 53 | fn exit(code: i32) -> unreachable; | |
| 54 | } | |
| 55 | ||
| 56 | fn _start() -> unreachable { | |
| 57 | puts("Hello, world!"); | |
| 58 | exit(0); | |
| 59 | } | |
| 60 | )SOURCE", "Hello, world!\n"); | |
| 61 | ||
| 62 | add_simple_case("function call", R"SOURCE( | |
| 63 | #link("c") | |
| 64 | extern { | |
| 65 | fn puts(s: *mut u8) -> i32; | |
| 66 | fn exit(code: i32) -> unreachable; | |
| 67 | } | |
| 68 | ||
| 69 | fn _start() -> unreachable { | |
| 70 | this_is_a_function(); | |
| 71 | } | |
| 72 | ||
| 73 | fn this_is_a_function() -> unreachable { | |
| 74 | puts("OK"); | |
| 75 | exit(0); | |
| 76 | } | |
| 77 | )SOURCE", "OK\n"); | |
| 78 | } | |
| 79 | ||
| 80 | static void run_test(TestCase *test_case) { | |
| 81 | os_write_file(buf_create_from_str(tmp_source_path), buf_create_from_str(test_case->source)); | |
| 82 | ||
| 83 | Buf zig_stderr = BUF_INIT; | |
| 84 | Buf zig_stdout = BUF_INIT; | |
| 85 | int return_code; | |
| 86 | os_exec_process("./zig", test_case->compiler_args, &return_code, &zig_stderr, &zig_stdout); | |
| 87 | ||
| 88 | if (return_code != 0) { | |
| 89 | printf("\nCompile failed with return code %d:\n", return_code); | |
| 90 | printf("zig"); | |
| 91 | for (int i = 0; i < test_case->compiler_args.length; i += 1) { | |
| 92 | printf(" %s", test_case->compiler_args.at(i)); | |
| 93 | } | |
| 94 | printf("\n"); | |
| 95 | printf("%s\n", buf_ptr(&zig_stderr)); | |
| 96 | exit(1); | |
| 97 | } | |
| 98 | ||
| 99 | Buf program_stderr = BUF_INIT; | |
| 100 | Buf program_stdout = BUF_INIT; | |
| 101 | os_exec_process(tmp_exe_path, test_case->program_args, &return_code, &program_stderr, &program_stdout); | |
| 102 | ||
| 103 | if (return_code != 0) { | |
| 104 | printf("\nProgram exited with return code %d:\n", return_code); | |
| 105 | printf("zig"); | |
| 106 | for (int i = 0; i < test_case->compiler_args.length; i += 1) { | |
| 107 | printf(" %s", test_case->compiler_args.at(i)); | |
| 108 | } | |
| 109 | printf("\n"); | |
| 110 | printf("%s\n", buf_ptr(&program_stderr)); | |
| 111 | exit(1); | |
| 112 | } | |
| 113 | ||
| 114 | if (!buf_eql_str(&program_stdout, test_case->output)) { | |
| 115 | printf("\n"); | |
| 116 | printf("==== Test failed. Expected output: ====\n"); | |
| 117 | printf("%s\n", test_case->output); | |
| 118 | printf("========= Actual output: ==============\n"); | |
| 119 | printf("%s\n", buf_ptr(&program_stdout)); | |
| 120 | printf("=======================================\n"); | |
| 121 | exit(1); | |
| 122 | } | |
| 123 | } | |
| 124 | ||
| 125 | static void run_all_tests(void) { | |
| 126 | for (int i = 0; i < test_cases.length; i += 1) { | |
| 127 | TestCase *test_case = test_cases.at(i); | |
| 128 | printf("Test %d/%d %s...", i + 1, test_cases.length, test_case->case_name); | |
| 129 | run_test(test_case); | |
| 130 | printf("OK\n"); | |
| 131 | } | |
| 132 | printf("%d tests passed.\n", test_cases.length); | |
| 133 | } | |
| 134 | ||
| 135 | static void cleanup(void) { | |
| 136 | remove(tmp_source_path); | |
| 137 | remove(tmp_exe_path); | |
| 138 | } | |
| 139 | ||
| 140 | int main(int argc, char **argv) { | |
| 141 | add_all_test_cases(); | |
| 142 | run_all_tests(); | |
| 143 | cleanup(); | |
| 144 | } |