| 1 | module c |
| 2 | |
| 3 | import os |
| 4 | import v.pref |
| 5 | import v.util |
| 6 | |
| 7 | fn live_runtime_quoted_path(target_os pref.OS, path string) string { |
| 8 | return match target_os { |
| 9 | .windows { '"${path.replace('"', '\\"')}"' } |
| 10 | else { "'" + path.replace("'", "'\\''") + "'" } |
| 11 | } |
| 12 | } |
| 13 | |
| 14 | fn live_runtime_quoted_path_for_c_string(target_os pref.OS, path string) string { |
| 15 | return live_runtime_quoted_path(target_os, path).replace('\\', '\\\\').replace('"', '\\"') |
| 16 | } |
| 17 | |
| 18 | fn (mut g Gen) generate_hotcode_reloading_declarations() { |
| 19 | if g.pref.os == .windows { |
| 20 | g.hotcode_definitions.writeln('HANDLE live_fn_mutex = 0;') |
| 21 | g.hotcode_definitions.writeln(' |
| 22 | HANDLE* v_live_fn_mutex_ptr(void) { |
| 23 | if (g_live_reload_info) { |
| 24 | v__live__LiveReloadInfo* live_info = (v__live__LiveReloadInfo*)g_live_reload_info; |
| 25 | if (live_info->live_fn_mutex) { |
| 26 | return (HANDLE*)live_info->live_fn_mutex; |
| 27 | } |
| 28 | } |
| 29 | if (!live_fn_mutex) { |
| 30 | live_fn_mutex = CreateMutexA(0, 0, 0); |
| 31 | } |
| 32 | return &live_fn_mutex; |
| 33 | } |
| 34 | void pthread_mutex_lock(HANDLE *m) { |
| 35 | WaitForSingleObject(*m, INFINITE); |
| 36 | } |
| 37 | void pthread_mutex_unlock(HANDLE *m) { |
| 38 | ReleaseMutex(*m); |
| 39 | } |
| 40 | ') |
| 41 | } else { |
| 42 | g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex;') |
| 43 | g.hotcode_definitions.writeln('pthread_once_t live_fn_mutex_once = PTHREAD_ONCE_INIT;') |
| 44 | g.hotcode_definitions.writeln(' |
| 45 | void v_init_live_mutex(void) { |
| 46 | pthread_mutexattr_t live_fn_mutex_attr; |
| 47 | pthread_mutexattr_init(&live_fn_mutex_attr); |
| 48 | pthread_mutexattr_settype(&live_fn_mutex_attr, PTHREAD_MUTEX_RECURSIVE); |
| 49 | pthread_mutex_init(&live_fn_mutex, &live_fn_mutex_attr); |
| 50 | pthread_mutexattr_destroy(&live_fn_mutex_attr); |
| 51 | } |
| 52 | |
| 53 | pthread_mutex_t* v_live_fn_mutex_ptr(void) { |
| 54 | if (g_live_reload_info) { |
| 55 | v__live__LiveReloadInfo* live_info = (v__live__LiveReloadInfo*)g_live_reload_info; |
| 56 | if (live_info->live_fn_mutex) { |
| 57 | return (pthread_mutex_t*)live_info->live_fn_mutex; |
| 58 | } |
| 59 | } |
| 60 | pthread_once(&live_fn_mutex_once, v_init_live_mutex); |
| 61 | return &live_fn_mutex; |
| 62 | } |
| 63 | ') |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | fn (mut g Gen) generate_hotcode_reloader_code() { |
| 68 | if g.pref.is_liveshared { |
| 69 | g.hotcode_definitions.writeln('') |
| 70 | return |
| 71 | } |
| 72 | // Hot code reloading |
| 73 | if g.pref.is_livemain { |
| 74 | mut phd := '' |
| 75 | mut load_code := []string{} |
| 76 | if g.pref.os != .windows { |
| 77 | for so_fn in g.hotcode_fn_names { |
| 78 | load_code << '\tvoid* live_sym_${so_fn} = dlsym(live_lib, "impl_live_${so_fn}");' |
| 79 | load_code << '\tif (live_sym_${so_fn}) {' |
| 80 | load_code << '\t\timpl_live_${so_fn} = live_sym_${so_fn};' |
| 81 | load_code << '\t} else {' |
| 82 | load_code << '\t\timpl_live_${so_fn} = no_impl_${so_fn};' |
| 83 | load_code << '\t}' |
| 84 | } |
| 85 | load_code << 'void (* fn_set_live_reload_pointer)(void *) = (void *)dlsym(live_lib, "set_live_reload_pointer");' |
| 86 | phd = posix_hotcode_definitions_1 |
| 87 | } else { |
| 88 | for so_fn in g.hotcode_fn_names { |
| 89 | load_code << '\tvoid* live_sym_${so_fn} = (void *)GetProcAddress(live_lib, "impl_live_${so_fn}");' |
| 90 | load_code << '\tif (live_sym_${so_fn}) {' |
| 91 | load_code << '\t\timpl_live_${so_fn} = live_sym_${so_fn};' |
| 92 | load_code << '\t} else {' |
| 93 | load_code << '\t\timpl_live_${so_fn} = no_impl_${so_fn};' |
| 94 | load_code << '\t}' |
| 95 | } |
| 96 | load_code << 'void (* fn_set_live_reload_pointer)(void *) = (void *)GetProcAddress(live_lib, "set_live_reload_pointer");' |
| 97 | phd = windows_hotcode_definitions_1 |
| 98 | } |
| 99 | // Ensure that g_live_reload_info from the executable is passed to the DLL . |
| 100 | // See also vlib/v/live/sharedlib/live_sharedlib.v . |
| 101 | load_code << 'if(fn_set_live_reload_pointer){ fn_set_live_reload_pointer( g_live_reload_info ); }' |
| 102 | |
| 103 | g.hotcode_definitions.writeln(phd.replace('@LOAD_FNS@', load_code.join('\n'))) |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | const posix_hotcode_definitions_1 = ' |
| 108 | void v_bind_live_symbols(void* live_lib){ |
| 109 | @LOAD_FNS@ |
| 110 | } |
| 111 | ' |
| 112 | |
| 113 | const windows_hotcode_definitions_1 = ' |
| 114 | void v_bind_live_symbols(void* live_lib){ |
| 115 | @LOAD_FNS@ |
| 116 | } |
| 117 | ' |
| 118 | |
| 119 | fn (mut g Gen) generate_hotcode_reloading_main_caller() { |
| 120 | if !g.pref.is_livemain { |
| 121 | return |
| 122 | } |
| 123 | g.writeln('') |
| 124 | // We are in live code reload mode, so start the .so loader in the background |
| 125 | g.writeln2('\t// live code initialization section:', '\t{') |
| 126 | g.writeln('\t\t// initialization of live function pointers') |
| 127 | for fname in g.hotcode_fn_names { |
| 128 | g.writeln('\t\timpl_live_${fname} = no_impl_${fname};') |
| 129 | } |
| 130 | vexe := util.cescaped_path(pref.vexe_path()) |
| 131 | file := util.cescaped_path(g.pref.path) |
| 132 | ccpath := live_runtime_quoted_path_for_c_string(g.pref.os, g.pref.ccompiler) |
| 133 | ccompiler := '-cc ${ccpath}' |
| 134 | so_debug_flag := if g.pref.is_debug { '-cg' } else { '' } |
| 135 | mut vopts := '${ccompiler} ${so_debug_flag} -sharedlive -shared' |
| 136 | if g.pref.os == .windows && g.is_cc_msvc && 'sokol' in g.table.imports { |
| 137 | mut import_lib_path := g.pref.out_name |
| 138 | ext := os.file_ext(import_lib_path) |
| 139 | if ext != '' { |
| 140 | import_lib_path = import_lib_path[..import_lib_path.len - ext.len] + '.lib' |
| 141 | } else { |
| 142 | import_lib_path += '.lib' |
| 143 | } |
| 144 | escaped_import_lib_path := util.cescaped_path(os.abs_path(import_lib_path)) |
| 145 | vopts += " -ldflags \\\"${escaped_import_lib_path}\\\"" |
| 146 | } |
| 147 | |
| 148 | g.writeln('\t\t// start background reloading thread') |
| 149 | g.writeln('\t\tvoid* live_fn_mutex_addr = v_live_fn_mutex_ptr();') |
| 150 | g.writeln('\t\tv__live__LiveReloadInfo* live_info = v__live__executable__new_live_reload_info(') |
| 151 | g.writeln('\t\t\t\t\t builtin__tos2("${file}"),') |
| 152 | g.writeln('\t\t\t\t\t builtin__tos2("${vexe}"),') |
| 153 | g.writeln('\t\t\t\t\t builtin__tos2("${vopts}"),') |
| 154 | g.writeln('\t\t\t\t\t live_fn_mutex_addr,') |
| 155 | g.writeln('\t\t\t\t\t v_bind_live_symbols') |
| 156 | g.writeln('\t\t);') |
| 157 | mut already_added := map[string]bool{} |
| 158 | for f in g.hotcode_fpaths { |
| 159 | already_added[f] = true |
| 160 | } |
| 161 | mut idx := 0 |
| 162 | for f, _ in already_added { |
| 163 | fpath := os.real_path(f) |
| 164 | g.writeln('\t\tv__live__executable__add_live_monitored_file(live_info, ${ctoslit(fpath)}); // source V file with @[live] ${ |
| 165 | idx + 1}/${already_added.len}') |
| 166 | idx++ |
| 167 | } |
| 168 | g.writeln('') |
| 169 | // g_live_reload_info gives access to the LiveReloadInfo methods, |
| 170 | // to the custom user code, through calling v_live_info() |
| 171 | g.writeln('\t\tg_live_reload_info = (void*)live_info;') |
| 172 | g.writeln('\t\tv__live__executable__start_reloader(live_info);') |
| 173 | g.writeln('\t}\t// end of live code initialization section') |
| 174 | g.writeln('') |
| 175 | } |
| 176 | |