| author | |
| committer | |
| log | 5e33175517807c6e50fe66f8d6c65fc094c8e11a |
| tree | 004fc843be6c452f0c925915d080b8ab09432723 |
| parent | 832454f38b3ef635ee03fb54c382d8be02ac7c9e |
7 files changed, 98 insertions(+), 4 deletions(-)
src/all_types.hpp+1| ... | @@ -1088,6 +1088,7 @@ enum BuiltinFnId { | ... | @@ -1088,6 +1088,7 @@ enum BuiltinFnId { |
| 1088 | BuiltinFnIdCImport, | 1088 | BuiltinFnIdCImport, |
| 1089 | BuiltinFnIdErrName, | 1089 | BuiltinFnIdErrName, |
| 1090 | BuiltinFnIdBreakpoint, | 1090 | BuiltinFnIdBreakpoint, |
| 1091 | BuiltinFnIdEmbedFile, | ||
| 1091 | }; | 1092 | }; |
| 1092 | 1093 | ||
| 1093 | struct BuiltinFnEntry { | 1094 | struct BuiltinFnEntry { |
src/analyze.cpp+41| ... | @@ -4101,6 +4101,45 @@ static TypeTableEntry *analyze_err_name(CodeGen *g, ImportTableEntry *import, | ... | @@ -4101,6 +4101,45 @@ static TypeTableEntry *analyze_err_name(CodeGen *g, ImportTableEntry *import, |
| 4101 | return str_type; | 4101 | return str_type; |
| 4102 | } | 4102 | } |
| 4103 | 4103 | ||
| 4104 | static TypeTableEntry *analyze_embed_file(CodeGen *g, ImportTableEntry *import, | ||
| 4105 | BlockContext *context, AstNode *node) | ||
| 4106 | { | ||
| 4107 | assert(node->type == NodeTypeFnCallExpr); | ||
| 4108 | |||
| 4109 | AstNode **first_param_node = &node->data.fn_call_expr.params.at(0); | ||
| 4110 | Buf *rel_file_path = resolve_const_expr_str(g, import, context, first_param_node); | ||
| 4111 | if (!rel_file_path) { | ||
| 4112 | return g->builtin_types.entry_invalid; | ||
| 4113 | } | ||
| 4114 | |||
| 4115 | // figure out absolute path to resource | ||
| 4116 | Buf source_dir_path = BUF_INIT; | ||
| 4117 | os_path_dirname(import->path, &source_dir_path); | ||
| 4118 | |||
| 4119 | Buf file_path = BUF_INIT; | ||
| 4120 | os_path_resolve(&source_dir_path, rel_file_path, &file_path); | ||
| 4121 | |||
| 4122 | // load from file system into const expr | ||
| 4123 | Buf file_contents = BUF_INIT; | ||
| 4124 | int err; | ||
| 4125 | if ((err = os_fetch_file_path(&file_path, &file_contents))) { | ||
| 4126 | if (err == ErrorFileNotFound) { | ||
| 4127 | add_node_error(g, node, | ||
| 4128 | buf_sprintf("unable to find '%s'", buf_ptr(&file_path))); | ||
| 4129 | return g->builtin_types.entry_invalid; | ||
| 4130 | } else { | ||
| 4131 | add_node_error(g, node, | ||
| 4132 | buf_sprintf("unable to open '%s': %s", buf_ptr(&file_path), err_str(err))); | ||
| 4133 | return g->builtin_types.entry_invalid; | ||
| 4134 | } | ||
| 4135 | } | ||
| 4136 | |||
| 4137 | // TODO add dependency on the file we embedded so that we know if it changes | ||
| 4138 | // we'll have to invalidate the cache | ||
| 4139 | |||
| 4140 | return resolve_expr_const_val_as_string_lit(g, node, &file_contents); | ||
| 4141 | } | ||
| 4142 | |||
| 4104 | static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 4143 | static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 4105 | TypeTableEntry *expected_type, AstNode *node) | 4144 | TypeTableEntry *expected_type, AstNode *node) |
| 4106 | { | 4145 | { |
| ... | @@ -4434,6 +4473,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry | ... | @@ -4434,6 +4473,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 4434 | case BuiltinFnIdBreakpoint: | 4473 | case BuiltinFnIdBreakpoint: |
| 4435 | mark_impure_fn(context); | 4474 | mark_impure_fn(context); |
| 4436 | return g->builtin_types.entry_void; | 4475 | return g->builtin_types.entry_void; |
| 4476 | case BuiltinFnIdEmbedFile: | ||
| 4477 | return analyze_embed_file(g, import, context, node); | ||
| 4437 | } | 4478 | } |
| 4438 | zig_unreachable(); | 4479 | zig_unreachable(); |
| 4439 | } | 4480 | } |
src/codegen.cpp+2| ... | @@ -507,6 +507,7 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { | ... | @@ -507,6 +507,7 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { |
| 507 | case BuiltinFnIdMaxValue: | 507 | case BuiltinFnIdMaxValue: |
| 508 | case BuiltinFnIdMemberCount: | 508 | case BuiltinFnIdMemberCount: |
| 509 | case BuiltinFnIdConstEval: | 509 | case BuiltinFnIdConstEval: |
| 510 | case BuiltinFnIdEmbedFile: | ||
| 510 | // caught by constant expression eval codegen | 511 | // caught by constant expression eval codegen |
| 511 | zig_unreachable(); | 512 | zig_unreachable(); |
| 512 | case BuiltinFnIdCompileVar: | 513 | case BuiltinFnIdCompileVar: |
| ... | @@ -3896,6 +3897,7 @@ static void define_builtin_fns(CodeGen *g) { | ... | @@ -3896,6 +3897,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 3896 | create_builtin_fn_with_arg_count(g, BuiltinFnIdImport, "import", 1); | 3897 | create_builtin_fn_with_arg_count(g, BuiltinFnIdImport, "import", 1); |
| 3897 | create_builtin_fn_with_arg_count(g, BuiltinFnIdCImport, "c_import", 1); | 3898 | create_builtin_fn_with_arg_count(g, BuiltinFnIdCImport, "c_import", 1); |
| 3898 | create_builtin_fn_with_arg_count(g, BuiltinFnIdErrName, "err_name", 1); | 3899 | create_builtin_fn_with_arg_count(g, BuiltinFnIdErrName, "err_name", 1); |
| 3900 | create_builtin_fn_with_arg_count(g, BuiltinFnIdEmbedFile, "embed_file", 1); | ||
| 3899 | } | 3901 | } |
| 3900 | 3902 | ||
| 3901 | static void init(CodeGen *g, Buf *source_path) { | 3903 | static void init(CodeGen *g, Buf *source_path) { |
src/eval.cpp+1| ... | @@ -696,6 +696,7 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_ | ... | @@ -696,6 +696,7 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_ |
| 696 | case BuiltinFnIdImport: | 696 | case BuiltinFnIdImport: |
| 697 | case BuiltinFnIdCImport: | 697 | case BuiltinFnIdCImport: |
| 698 | case BuiltinFnIdErrName: | 698 | case BuiltinFnIdErrName: |
| 699 | case BuiltinFnIdEmbedFile: | ||
| 699 | zig_panic("TODO"); | 700 | zig_panic("TODO"); |
| 700 | case BuiltinFnIdBreakpoint: | 701 | case BuiltinFnIdBreakpoint: |
| 701 | case BuiltinFnIdInvalid: | 702 | case BuiltinFnIdInvalid: |
src/os.cpp+32-4| ... | @@ -91,6 +91,10 @@ void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_ | ... | @@ -91,6 +91,10 @@ void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_ |
| 91 | #endif | 91 | #endif |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | void os_path_dirname(Buf *full_path, Buf *out_dirname) { | ||
| 95 | return os_path_split(full_path, out_dirname, nullptr); | ||
| 96 | } | ||
| 97 | |||
| 94 | void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) { | 98 | void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) { |
| 95 | int last_index = buf_len(full_path) - 1; | 99 | int last_index = buf_len(full_path) - 1; |
| 96 | if (last_index >= 0 && buf_ptr(full_path)[last_index] == '/') { | 100 | if (last_index >= 0 && buf_ptr(full_path)[last_index] == '/') { |
| ... | @@ -99,13 +103,17 @@ void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) { | ... | @@ -99,13 +103,17 @@ void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) { |
| 99 | for (int i = last_index; i >= 0; i -= 1) { | 103 | for (int i = last_index; i >= 0; i -= 1) { |
| 100 | uint8_t c = buf_ptr(full_path)[i]; | 104 | uint8_t c = buf_ptr(full_path)[i]; |
| 101 | if (c == '/') { | 105 | if (c == '/') { |
| 102 | buf_init_from_mem(out_dirname, buf_ptr(full_path), i); | 106 | if (out_dirname) { |
| 103 | buf_init_from_mem(out_basename, buf_ptr(full_path) + i + 1, buf_len(full_path) - (i + 1)); | 107 | buf_init_from_mem(out_dirname, buf_ptr(full_path), i); |
| 108 | } | ||
| 109 | if (out_basename) { | ||
| 110 | buf_init_from_mem(out_basename, buf_ptr(full_path) + i + 1, buf_len(full_path) - (i + 1)); | ||
| 111 | } | ||
| 104 | return; | 112 | return; |
| 105 | } | 113 | } |
| 106 | } | 114 | } |
| 107 | buf_init_from_mem(out_dirname, ".", 1); | 115 | if (out_dirname) buf_init_from_mem(out_dirname, ".", 1); |
| 108 | buf_init_from_buf(out_basename, full_path); | 116 | if (out_basename) buf_init_from_buf(out_basename, full_path); |
| 109 | } | 117 | } |
| 110 | 118 | ||
| 111 | void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { | 119 | void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { |
| ... | @@ -146,6 +154,26 @@ int os_path_real(Buf *rel_path, Buf *out_abs_path) { | ... | @@ -146,6 +154,26 @@ int os_path_real(Buf *rel_path, Buf *out_abs_path) { |
| 146 | #endif | 154 | #endif |
| 147 | } | 155 | } |
| 148 | 156 | ||
| 157 | bool os_path_is_absolute(Buf *path) { | ||
| 158 | #if defined(ZIG_OS_WINDOWS) | ||
| 159 | #error "missing os_path_is_absolute implementation" | ||
| 160 | #elif defined(ZIG_OS_POSIX) | ||
| 161 | return buf_ptr(path)[0] == '/'; | ||
| 162 | #else | ||
| 163 | #error "missing os_path_is_absolute implementation" | ||
| 164 | #endif | ||
| 165 | } | ||
| 166 | |||
| 167 | void os_path_resolve(Buf *ref_path, Buf *target_path, Buf *out_abs_path) { | ||
| 168 | if (os_path_is_absolute(target_path)) { | ||
| 169 | buf_init_from_buf(out_abs_path, target_path); | ||
| 170 | return; | ||
| 171 | } | ||
| 172 | |||
| 173 | os_path_join(ref_path, target_path, out_abs_path); | ||
| 174 | return; | ||
| 175 | } | ||
| 176 | |||
| 149 | int os_fetch_file(FILE *f, Buf *out_buf) { | 177 | int os_fetch_file(FILE *f, Buf *out_buf) { |
| 150 | static const ssize_t buf_size = 0x2000; | 178 | static const ssize_t buf_size = 0x2000; |
| 151 | buf_resize(out_buf, buf_size); | 179 | buf_resize(out_buf, buf_size); |
src/os.hpp+3| ... | @@ -18,9 +18,12 @@ void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_ | ... | @@ -18,9 +18,12 @@ void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_ |
| 18 | int os_exec_process(const char *exe, ZigList<const char *> &args, | 18 | int os_exec_process(const char *exe, ZigList<const char *> &args, |
| 19 | int *return_code, Buf *out_stderr, Buf *out_stdout); | 19 | int *return_code, Buf *out_stderr, Buf *out_stdout); |
| 20 | 20 | ||
| 21 | void os_path_dirname(Buf *full_path, Buf *out_dirname); | ||
| 21 | void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename); | 22 | void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename); |
| 22 | void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path); | 23 | void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path); |
| 23 | int os_path_real(Buf *rel_path, Buf *out_abs_path); | 24 | int os_path_real(Buf *rel_path, Buf *out_abs_path); |
| 25 | void os_path_resolve(Buf *ref_path, Buf *target_path, Buf *out_abs_path); | ||
| 26 | bool os_path_is_absolute(Buf *path); | ||
| 24 | 27 | ||
| 25 | void os_write_file(Buf *full_path, Buf *contents); | 28 | void os_write_file(Buf *full_path, Buf *contents); |
| 26 | 29 |
test/run_tests.cpp+18| ... | @@ -600,6 +600,20 @@ fn do_test() -> %void { | ... | @@ -600,6 +600,20 @@ fn do_test() -> %void { |
| 600 | } | 600 | } |
| 601 | fn its_gonna_pass() -> %void { } | 601 | fn its_gonna_pass() -> %void { } |
| 602 | )SOURCE", "before\nafter\ndefer3\ndefer1\n"); | 602 | )SOURCE", "before\nafter\ndefer3\ndefer1\n"); |
| 603 | |||
| 604 | |||
| 605 | { | ||
| 606 | TestCase *tc = add_simple_case("@embed_file", R"SOURCE( | ||
| 607 | const foo_txt = @embed_file("foo.txt"); | ||
| 608 | const io = @import("std").io; | ||
| 609 | |||
| 610 | pub fn main(args: [][]u8) -> %void { | ||
| 611 | %%io.stdout.printf(foo_txt); | ||
| 612 | } | ||
| 613 | )SOURCE", "1234\nabcd\n"); | ||
| 614 | |||
| 615 | add_source_file(tc, "foo.txt", "1234\nabcd\n"); | ||
| 616 | } | ||
| 603 | } | 617 | } |
| 604 | 618 | ||
| 605 | 619 | ||
| ... | @@ -1173,6 +1187,10 @@ fn fibbonaci(x: i32) -> i32 { | ... | @@ -1173,6 +1187,10 @@ fn fibbonaci(x: i32) -> i32 { |
| 1173 | ".tmp_source.zig:3:1: error: function evaluation exceeded 1000 branches", | 1187 | ".tmp_source.zig:3:1: error: function evaluation exceeded 1000 branches", |
| 1174 | ".tmp_source.zig:2:37: note: called from here", | 1188 | ".tmp_source.zig:2:37: note: called from here", |
| 1175 | ".tmp_source.zig:4:40: note: quota exceeded here"); | 1189 | ".tmp_source.zig:4:40: note: quota exceeded here"); |
| 1190 | |||
| 1191 | add_compile_fail_case("@embed_file with bogus file", R"SOURCE( | ||
| 1192 | const resource = @embed_file("bogus.txt"); | ||
| 1193 | )SOURCE", 1, ".tmp_source.zig:2:18: error: unable to find './bogus.txt'"); | ||
| 1176 | } | 1194 | } |
| 1177 | 1195 | ||
| 1178 | ////////////////////////////////////////////////////////////////////////////// | 1196 | ////////////////////////////////////////////////////////////////////////////// |