compiler: add back support for -live
parent
d6c1ee0202
commit
7038f59ca5
|
@ -2,8 +2,10 @@ module main
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import dl
|
||||||
|
|
||||||
const (
|
const (
|
||||||
os_used = os.MAX_PATH
|
os_used = os.MAX_PATH
|
||||||
time_used = time.now()
|
time_used = time.now()
|
||||||
|
dl_used = dl.version
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,7 +20,6 @@ const (
|
||||||
'vlib/clipboard/clipboard_test.v',
|
'vlib/clipboard/clipboard_test.v',
|
||||||
'vlib/sqlite/sqlite_test.v',
|
'vlib/sqlite/sqlite_test.v',
|
||||||
|
|
||||||
'vlib/v/tests/live_test.v', // Linux & Solaris only; since live does not actually work for now with v2, just skip
|
|
||||||
'vlib/v/tests/asm_test.v', // skip everywhere for now, works on linux with cc != tcc
|
'vlib/v/tests/asm_test.v', // skip everywhere for now, works on linux with cc != tcc
|
||||||
]
|
]
|
||||||
skip_on_linux = []string{}
|
skip_on_linux = []string{}
|
||||||
|
|
|
@ -123,14 +123,14 @@ fn parse_args(args []string) (&pref.Preferences, string) {
|
||||||
'-cg' {
|
'-cg' {
|
||||||
res.is_debug = true
|
res.is_debug = true
|
||||||
}
|
}
|
||||||
'-live' {
|
|
||||||
res.is_live = true
|
|
||||||
}
|
|
||||||
'-repl' {
|
'-repl' {
|
||||||
res.is_repl = true
|
res.is_repl = true
|
||||||
}
|
}
|
||||||
|
'-live' {
|
||||||
|
res.is_livemain = true
|
||||||
|
}
|
||||||
'-sharedlive' {
|
'-sharedlive' {
|
||||||
res.is_live = true
|
res.is_liveshared = true
|
||||||
res.is_shared = true
|
res.is_shared = true
|
||||||
}
|
}
|
||||||
'-shared' {
|
'-shared' {
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
module dl
|
||||||
|
|
||||||
|
pub const (
|
||||||
|
version = 1
|
||||||
|
)
|
|
@ -95,6 +95,7 @@ fn (mut v Builder) cc() {
|
||||||
'-Wno-unused-parameter', '-Wno-unused-result', '-Wno-unused-function', '-Wno-missing-braces',
|
'-Wno-unused-parameter', '-Wno-unused-result', '-Wno-unused-function', '-Wno-missing-braces',
|
||||||
'-Wno-unused-label'
|
'-Wno-unused-label'
|
||||||
]
|
]
|
||||||
|
mut linker_flags := []string{}
|
||||||
// TCC on Linux by default, unless -cc was provided
|
// TCC on Linux by default, unless -cc was provided
|
||||||
// TODO if -cc = cc, TCC is still used, default compiler should be
|
// TODO if -cc = cc, TCC is still used, default compiler should be
|
||||||
// used instead.
|
// used instead.
|
||||||
|
@ -131,11 +132,15 @@ fn (mut v Builder) cc() {
|
||||||
// linux_host := os.user_os() == 'linux'
|
// linux_host := os.user_os() == 'linux'
|
||||||
v.log('cc() isprod=$v.pref.is_prod outname=$v.pref.out_name')
|
v.log('cc() isprod=$v.pref.is_prod outname=$v.pref.out_name')
|
||||||
if v.pref.is_shared {
|
if v.pref.is_shared {
|
||||||
a << '-shared -fPIC ' // -Wl,-z,defs'
|
linker_flags << '-shared'
|
||||||
|
a << '-fPIC' // -Wl,-z,defs'
|
||||||
v.pref.out_name += '.so'
|
v.pref.out_name += '.so'
|
||||||
}
|
}
|
||||||
if v.pref.is_bare {
|
if v.pref.is_bare {
|
||||||
a << '-fno-stack-protector -static -ffreestanding -nostdlib'
|
a << '-fno-stack-protector'
|
||||||
|
a << '-ffreestanding'
|
||||||
|
linker_flags << '-static'
|
||||||
|
linker_flags << '-nostdlib'
|
||||||
}
|
}
|
||||||
if v.pref.build_mode == .build_module {
|
if v.pref.build_mode == .build_module {
|
||||||
// Create the modules & out directory if it's not there.
|
// Create the modules & out directory if it's not there.
|
||||||
|
@ -199,14 +204,14 @@ fn (mut v Builder) cc() {
|
||||||
a << optimization_options
|
a << optimization_options
|
||||||
}
|
}
|
||||||
if debug_mode && os.user_os() != 'windows' {
|
if debug_mode && os.user_os() != 'windows' {
|
||||||
a << ' -rdynamic ' // needed for nicer symbolic backtraces
|
linker_flags << ' -rdynamic ' // needed for nicer symbolic backtraces
|
||||||
}
|
}
|
||||||
if v.pref.ccompiler != 'msvc' && v.pref.os != .freebsd {
|
if v.pref.ccompiler != 'msvc' && v.pref.os != .freebsd {
|
||||||
a << '-Werror=implicit-function-declaration'
|
a << '-Werror=implicit-function-declaration'
|
||||||
}
|
}
|
||||||
if v.pref.is_shared || v.pref.is_live {
|
if v.pref.is_liveshared || v.pref.is_livemain {
|
||||||
if v.pref.os == .linux || os.user_os() == 'linux' {
|
if v.pref.os == .linux || os.user_os() == 'linux' {
|
||||||
a << '-rdynamic'
|
linker_flags << '-rdynamic'
|
||||||
}
|
}
|
||||||
if v.pref.os == .mac || os.user_os() == 'mac' {
|
if v.pref.os == .mac || os.user_os() == 'mac' {
|
||||||
a << '-flat_namespace'
|
a << '-flat_namespace'
|
||||||
|
@ -320,7 +325,8 @@ fn (mut v Builder) cc() {
|
||||||
}
|
}
|
||||||
if !is_cc_tcc {
|
if !is_cc_tcc {
|
||||||
$if linux {
|
$if linux {
|
||||||
a << '-Xlinker -z -Xlinker muldefs'
|
linker_flags << '-Xlinker -z'
|
||||||
|
linker_flags << '-Xlinker muldefs'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,20 +334,21 @@ fn (mut v Builder) cc() {
|
||||||
// || os.user_os() == 'linux'
|
// || os.user_os() == 'linux'
|
||||||
if !v.pref.is_bare && v.pref.build_mode != .build_module && v.pref.os in [ .linux, .freebsd,
|
if !v.pref.is_bare && v.pref.build_mode != .build_module && v.pref.os in [ .linux, .freebsd,
|
||||||
.openbsd, .netbsd, .dragonfly, .solaris, .haiku] {
|
.openbsd, .netbsd, .dragonfly, .solaris, .haiku] {
|
||||||
a << '-lm -lpthread '
|
linker_flags << '-lm'
|
||||||
|
linker_flags << '-lpthread'
|
||||||
// -ldl is a Linux only thing. BSDs have it in libc.
|
// -ldl is a Linux only thing. BSDs have it in libc.
|
||||||
if v.pref.os == .linux {
|
if v.pref.os == .linux {
|
||||||
a << ' -ldl '
|
linker_flags << '-ldl'
|
||||||
}
|
}
|
||||||
if v.pref.os == .freebsd {
|
if v.pref.os == .freebsd {
|
||||||
// FreeBSD: backtrace needs execinfo library while linking
|
// FreeBSD: backtrace needs execinfo library while linking
|
||||||
a << ' -lexecinfo '
|
linker_flags << '-lexecinfo'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !v.pref.is_bare && v.pref.os == .js && os.user_os() == 'linux' {
|
if !v.pref.is_bare && v.pref.os == .js && os.user_os() == 'linux' {
|
||||||
a << '-lm'
|
linker_flags << '-lm'
|
||||||
}
|
}
|
||||||
args := a.join(' ')
|
args := a.join(' ') + linker_flags.join(' ')
|
||||||
start:
|
start:
|
||||||
todo()
|
todo()
|
||||||
// TODO remove
|
// TODO remove
|
||||||
|
|
|
@ -151,10 +151,10 @@ pub fn (v Builder) get_user_files() []string {
|
||||||
// See cmd/tools/preludes/README.md for more info about what preludes are
|
// See cmd/tools/preludes/README.md for more info about what preludes are
|
||||||
vroot := os.dir(pref.vexe_path())
|
vroot := os.dir(pref.vexe_path())
|
||||||
preludes_path := os.join_path(vroot, 'cmd', 'tools', 'preludes')
|
preludes_path := os.join_path(vroot, 'cmd', 'tools', 'preludes')
|
||||||
if v.pref.is_live {
|
if v.pref.is_livemain {
|
||||||
user_files << os.join_path(preludes_path, 'live_main.v')
|
user_files << os.join_path(preludes_path, 'live_main.v')
|
||||||
}
|
}
|
||||||
if v.pref.is_live && v.pref.is_shared {
|
if v.pref.is_liveshared {
|
||||||
user_files << os.join_path(preludes_path, 'live_shared.v')
|
user_files << os.join_path(preludes_path, 'live_shared.v')
|
||||||
}
|
}
|
||||||
if v.pref.is_test {
|
if v.pref.is_test {
|
||||||
|
|
|
@ -1,242 +0,0 @@
|
||||||
module builder
|
|
||||||
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
fn (v &Builder) generate_hotcode_reloading_declarations() {
|
|
||||||
/*
|
|
||||||
QTODO
|
|
||||||
mut cgen := v.cgen
|
|
||||||
if v.pref.os != .windows {
|
|
||||||
if v.pref.is_so {
|
|
||||||
cgen.genln('pthread_mutex_t live_fn_mutex;')
|
|
||||||
}
|
|
||||||
if v.pref.is_live {
|
|
||||||
cgen.genln('pthread_mutex_t live_fn_mutex = PTHREAD_MUTEX_INITIALIZER;')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if v.pref.is_so {
|
|
||||||
cgen.genln('HANDLE live_fn_mutex;')
|
|
||||||
cgen.genln('
|
|
||||||
void pthread_mutex_lock(HANDLE *m) {
|
|
||||||
WaitForSingleObject(*m, INFINITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pthread_mutex_unlock(HANDLE *m) {
|
|
||||||
ReleaseMutex(*m);
|
|
||||||
}')
|
|
||||||
}
|
|
||||||
if v.pref.is_live {
|
|
||||||
cgen.genln('HANDLE live_fn_mutex = 0;')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (v &Builder) generate_hotcode_reloading_main_caller() {
|
|
||||||
// QTODO
|
|
||||||
/*
|
|
||||||
if !v.pref.is_live {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// We are in live code reload mode, so start the .so loader in the background
|
|
||||||
mut cgen := v.cgen
|
|
||||||
cgen.genln('')
|
|
||||||
file_base := os.file_name(v.pref.path).replace('.v', '')
|
|
||||||
if v.pref.os != .windows {
|
|
||||||
// unix:
|
|
||||||
so_name := file_base + '.so'
|
|
||||||
cgen.genln(' char *live_library_name = "$so_name";')
|
|
||||||
cgen.genln(' load_so(live_library_name);')
|
|
||||||
cgen.genln(' pthread_t _thread_so;')
|
|
||||||
cgen.genln(' pthread_create(&_thread_so , NULL, (void *)&reload_so, live_library_name);')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// windows:
|
|
||||||
so_name := file_base + if v.pref.ccompiler == 'msvc' { '.dll' } else { '.so' }
|
|
||||||
cgen.genln(' char *live_library_name = "$so_name";')
|
|
||||||
cgen.genln(' live_fn_mutex = CreateMutexA(0, 0, 0);')
|
|
||||||
cgen.genln(' load_so(live_library_name);')
|
|
||||||
cgen.genln(' unsigned long _thread_so;')
|
|
||||||
cgen.genln(' _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);')
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (v &Builder) generate_hot_reload_code() {
|
|
||||||
/*
|
|
||||||
QTODO
|
|
||||||
mut cgen := v.cgen
|
|
||||||
// Hot code reloading
|
|
||||||
if v.pref.is_live {
|
|
||||||
mut file := os.real_path(v.pref.path)
|
|
||||||
file_base := os.file_name(file).replace('.v', '')
|
|
||||||
so_name := file_base + '.so'
|
|
||||||
// Need to build .so file before building the live application
|
|
||||||
// The live app needs to load this .so file on initialization.
|
|
||||||
mut vexe := os.args[0]
|
|
||||||
if os.user_os() == 'windows' {
|
|
||||||
vexe = cescaped_path(vexe)
|
|
||||||
file = cescaped_path(file)
|
|
||||||
}
|
|
||||||
mut msvc := ''
|
|
||||||
if v.pref.ccompiler == 'msvc' {
|
|
||||||
msvc = '-cc msvc'
|
|
||||||
}
|
|
||||||
so_debug_flag := if v.pref.is_debug { '-g' } else { '' }
|
|
||||||
cmd_compile_shared_library := '$vexe $msvc $so_debug_flag -o $file_base -solive -shared $file'
|
|
||||||
if v.pref.verbosity.is_higher_or_equal(.level_one) {
|
|
||||||
println(cmd_compile_shared_library)
|
|
||||||
}
|
|
||||||
ticks := time.ticks()
|
|
||||||
so_compilation_result := os.system(cmd_compile_shared_library)
|
|
||||||
if v.pref.verbosity.is_higher_or_equal(.level_two) {
|
|
||||||
diff := time.ticks() - ticks
|
|
||||||
println('compiling shared library took $diff ms')
|
|
||||||
println('=========\n')
|
|
||||||
}
|
|
||||||
if so_compilation_result != 0 {
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
cgen.genln('
|
|
||||||
|
|
||||||
void lfnmutex_print(char *s){
|
|
||||||
#if 0
|
|
||||||
fflush(stderr);
|
|
||||||
fprintf(stderr,">> live_fn_mutex: %p | %s\\n", &live_fn_mutex, s);
|
|
||||||
fflush(stderr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
')
|
|
||||||
if v.pref.os != .windows {
|
|
||||||
cgen.genln('
|
|
||||||
#define _POSIX_C_SOURCE 1
|
|
||||||
#include <limits.h>
|
|
||||||
#ifndef PATH_MAX
|
|
||||||
#define PATH_MAX 1024
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void* live_lib = 0;
|
|
||||||
|
|
||||||
int load_so(byteptr path) {
|
|
||||||
char cpath[PATH_MAX] = {0};
|
|
||||||
int res = snprintf(cpath, sizeof (cpath), "./%s", path);
|
|
||||||
if (res >= sizeof (cpath)) {
|
|
||||||
fprintf (stderr, "path is too long");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
//printf("load_so %s\\n", cpath);
|
|
||||||
if (live_lib) dlclose(live_lib);
|
|
||||||
live_lib = dlopen(cpath, RTLD_LAZY);
|
|
||||||
if (!live_lib) {
|
|
||||||
fprintf(stderr, "open failed");
|
|
||||||
exit(1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
')
|
|
||||||
for so_fn in cgen.so_fns {
|
|
||||||
cgen.genln('$so_fn = dlsym(live_lib, "$so_fn"); ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cgen.genln('
|
|
||||||
|
|
||||||
#ifndef PATH_MAX
|
|
||||||
#define PATH_MAX 1024
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void pthread_mutex_lock(HANDLE *m) {
|
|
||||||
WaitForSingleObject(*m, INFINITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pthread_mutex_unlock(HANDLE *m) {
|
|
||||||
ReleaseMutex(*m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* live_lib = NULL;
|
|
||||||
int load_so(byteptr path) {
|
|
||||||
char cpath[PATH_MAX];
|
|
||||||
int res = snprintf(cpath, sizeof (cpath), "./%s", path);
|
|
||||||
if (res >= sizeof(cpath)) {
|
|
||||||
puts("path is too long\\n");
|
|
||||||
exit(1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (live_lib) FreeLibrary(live_lib);
|
|
||||||
live_lib = LoadLibraryA(cpath);
|
|
||||||
if (!live_lib) {
|
|
||||||
puts("open failed\\n");
|
|
||||||
exit(1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
')
|
|
||||||
for so_fn in cgen.so_fns {
|
|
||||||
cgen.genln('$so_fn = (void *)GetProcAddress(live_lib, "$so_fn"); ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cgen.genln('return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _live_reloads = 0;
|
|
||||||
void reload_so() {
|
|
||||||
char new_so_base[PATH_MAX] = {0};
|
|
||||||
char new_so_name[PATH_MAX] = {0};
|
|
||||||
char compile_cmd[PATH_MAX] = {0};
|
|
||||||
int last = os__file_last_mod_unix(tos2("$file"));
|
|
||||||
while (1) {
|
|
||||||
// TODO use inotify
|
|
||||||
int now = os__file_last_mod_unix(tos2("$file"));
|
|
||||||
if (now != last) {
|
|
||||||
last = now;
|
|
||||||
_live_reloads++;
|
|
||||||
|
|
||||||
//v -o bounce -shared bounce.v
|
|
||||||
snprintf(new_so_base, sizeof (new_so_base), ".tmp.%d.${file_base}", _live_reloads);
|
|
||||||
#ifdef _WIN32
|
|
||||||
// We have to make this directory becuase windows WILL NOT
|
|
||||||
// do it for us
|
|
||||||
os__mkdir(string_all_before_last(tos2(new_so_base), tos2("/")));
|
|
||||||
#endif
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
snprintf(new_so_name, sizeof (new_so_name), "%s.dll", new_so_base);
|
|
||||||
#else
|
|
||||||
snprintf(new_so_name, sizeof (new_so_name), "%s.so", new_so_base);
|
|
||||||
#endif
|
|
||||||
snprintf(compile_cmd, sizeof (compile_cmd), "$vexe $msvc -o %s -solive -shared $file", new_so_base);
|
|
||||||
os__system(tos2(compile_cmd));
|
|
||||||
|
|
||||||
if( !os__exists(tos2(new_so_name)) ) {
|
|
||||||
puts("Errors while compiling $file\\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lfnmutex_print("reload_so locking...");
|
|
||||||
pthread_mutex_lock(&live_fn_mutex);
|
|
||||||
lfnmutex_print("reload_so locked");
|
|
||||||
|
|
||||||
load_so(new_so_name);
|
|
||||||
#ifndef _WIN32
|
|
||||||
unlink(new_so_name); // removing the .so file from the filesystem after dlopen-ing it is safe, since it will still be mapped in memory.
|
|
||||||
#else
|
|
||||||
_unlink(new_so_name);
|
|
||||||
#endif
|
|
||||||
//if(0 == rename(new_so_name, "${so_name}")){
|
|
||||||
// load_so("${so_name}");
|
|
||||||
//}
|
|
||||||
|
|
||||||
lfnmutex_print("reload_so unlocking...");
|
|
||||||
pthread_mutex_unlock(&live_fn_mutex);
|
|
||||||
lfnmutex_print("reload_so unlocked");
|
|
||||||
|
|
||||||
}
|
|
||||||
time__sleep_ms(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
')
|
|
||||||
}
|
|
||||||
if v.pref.is_so {
|
|
||||||
cgen.genln(' int load_so(byteptr path) { return 0; }')
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
|
@ -56,6 +56,7 @@ struct Gen {
|
||||||
auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs
|
auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs
|
||||||
comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI
|
comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI
|
||||||
pcs_declarations strings.Builder // -prof profile counter declarations for each function
|
pcs_declarations strings.Builder // -prof profile counter declarations for each function
|
||||||
|
hotcode_definitions strings.Builder // -live declarations & functions
|
||||||
table &table.Table
|
table &table.Table
|
||||||
pref &pref.Preferences
|
pref &pref.Preferences
|
||||||
module_built string
|
module_built string
|
||||||
|
@ -89,6 +90,9 @@ mut:
|
||||||
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
|
pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
|
||||||
attr string
|
attr string
|
||||||
is_builtin_mod bool
|
is_builtin_mod bool
|
||||||
|
hotcode_fn_names []string
|
||||||
|
//
|
||||||
|
fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -120,9 +124,11 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
|
||||||
comptime_defines: strings.new_builder(100)
|
comptime_defines: strings.new_builder(100)
|
||||||
inits: strings.new_builder(100)
|
inits: strings.new_builder(100)
|
||||||
pcs_declarations: strings.new_builder(100)
|
pcs_declarations: strings.new_builder(100)
|
||||||
|
hotcode_definitions: strings.new_builder(100)
|
||||||
table: table
|
table: table
|
||||||
pref: pref
|
pref: pref
|
||||||
fn_decl: 0
|
fn_decl: 0
|
||||||
|
fn_main: 0
|
||||||
autofree: true
|
autofree: true
|
||||||
indent: -1
|
indent: -1
|
||||||
module_built: pref.path.after('vlib/')
|
module_built: pref.path.after('vlib/')
|
||||||
|
@ -182,6 +188,8 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
|
||||||
b.writeln(g.interface_table())
|
b.writeln(g.interface_table())
|
||||||
b.writeln('\n// V gowrappers:')
|
b.writeln('\n// V gowrappers:')
|
||||||
b.writeln(g.gowrappers.str())
|
b.writeln(g.gowrappers.str())
|
||||||
|
b.writeln('\n// V hotcode definitions:')
|
||||||
|
b.writeln(g.hotcode_definitions.str())
|
||||||
b.writeln('\n// V stringliterals:')
|
b.writeln('\n// V stringliterals:')
|
||||||
b.writeln(g.stringliterals.str())
|
b.writeln(g.stringliterals.str())
|
||||||
b.writeln('\n// V auto str functions:')
|
b.writeln('\n// V auto str functions:')
|
||||||
|
@ -233,6 +241,9 @@ pub fn (mut g Gen) init() {
|
||||||
if g.pref.is_debug || 'debug' in g.pref.compile_defines {
|
if g.pref.is_debug || 'debug' in g.pref.compile_defines {
|
||||||
g.comptime_defines.writeln('#define _VDEBUG (1)')
|
g.comptime_defines.writeln('#define _VDEBUG (1)')
|
||||||
}
|
}
|
||||||
|
if g.pref.is_livemain || g.pref.is_liveshared {
|
||||||
|
g.generate_hotcode_reloading_declarations()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) finish() {
|
pub fn (mut g Gen) finish() {
|
||||||
|
@ -244,6 +255,14 @@ pub fn (mut g Gen) finish() {
|
||||||
if g.pref.is_prof {
|
if g.pref.is_prof {
|
||||||
g.gen_vprint_profile_stats()
|
g.gen_vprint_profile_stats()
|
||||||
}
|
}
|
||||||
|
if g.pref.is_livemain || g.pref.is_liveshared {
|
||||||
|
g.generate_hotcode_reloader_code()
|
||||||
|
}
|
||||||
|
if g.fn_main != 0 {
|
||||||
|
g.out.writeln('')
|
||||||
|
g.fn_decl = g.fn_main
|
||||||
|
g.gen_fn_decl( g.fn_main )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) write_typeof_functions() {
|
pub fn (mut g Gen) write_typeof_functions() {
|
||||||
|
@ -449,11 +468,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
ast.Attr {
|
ast.Attr {
|
||||||
g.attr = it.name
|
g.attr = it.name
|
||||||
if it.name == 'inline' {
|
g.writeln('// Attr: [$it.name]')
|
||||||
// g.writeln(it.name)
|
|
||||||
} else {
|
|
||||||
g.writeln('//[$it.name]')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ast.Block {
|
ast.Block {
|
||||||
g.writeln('{')
|
g.writeln('{')
|
||||||
|
@ -530,16 +545,20 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
println('build module `$g.module_built` fn `$it.name`')
|
println('build module `$g.module_built` fn `$it.name`')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn_start_pos := g.out.len
|
|
||||||
g.fn_decl = it // &it
|
g.fn_decl = it // &it
|
||||||
g.gen_fn_decl(it)
|
if it.name == 'main' {
|
||||||
if g.pref.printfn_list.len > 0 && g.last_fn_c_name in g.pref.printfn_list {
|
// just remember `it`; main code will be generated in finish()
|
||||||
println(g.out.after(fn_start_pos))
|
g.fn_main = it
|
||||||
|
} else {
|
||||||
|
g.gen_fn_decl(it)
|
||||||
}
|
}
|
||||||
|
g.fn_decl = 0
|
||||||
if skip {
|
if skip {
|
||||||
g.out.go_back_to(pos)
|
g.out.go_back_to(pos)
|
||||||
}
|
}
|
||||||
g.writeln('')
|
g.writeln('')
|
||||||
|
// g.attr has to be reset after each function
|
||||||
|
g.attr = ''
|
||||||
}
|
}
|
||||||
ast.ForCStmt {
|
ast.ForCStmt {
|
||||||
g.write('for (')
|
g.write('for (')
|
||||||
|
@ -2123,6 +2142,9 @@ fn verror(s string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) write_init_function() {
|
fn (mut g Gen) write_init_function() {
|
||||||
|
if g.pref.is_liveshared {
|
||||||
|
return
|
||||||
|
}
|
||||||
g.writeln('void _vinit() {')
|
g.writeln('void _vinit() {')
|
||||||
g.writeln('\tbuiltin_init();')
|
g.writeln('\tbuiltin_init();')
|
||||||
g.writeln('\tvinit_string_literals();')
|
g.writeln('\tvinit_string_literals();')
|
||||||
|
|
135
vlib/v/gen/fn.v
135
vlib/v/gen/fn.v
|
@ -12,25 +12,40 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
|
||||||
// || it.no_body {
|
// || it.no_body {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
is_main := it.name == 'main'
|
||||||
|
//
|
||||||
|
if is_main && g.pref.is_liveshared {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//
|
||||||
|
fn_start_pos := g.out.len
|
||||||
if g.attr == 'inline' {
|
if g.attr == 'inline' {
|
||||||
g.write('inline ')
|
g.write('inline ')
|
||||||
g.attr = ''
|
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
is_livefn := g.attr == 'live'
|
||||||
|
is_livemain := g.pref.is_livemain && is_livefn
|
||||||
|
is_liveshared := g.pref.is_liveshared && is_livefn
|
||||||
|
is_livemode := g.pref.is_livemain || g.pref.is_liveshared
|
||||||
|
is_live_wrap := is_livefn && is_livemode
|
||||||
|
if is_livefn && !is_livemode {
|
||||||
|
eprintln('INFO: compile with `v -live $g.pref.path `, if you want to use the [live] function ${it.name} .')
|
||||||
|
}
|
||||||
|
//
|
||||||
g.reset_tmp_count()
|
g.reset_tmp_count()
|
||||||
is_main := it.name == 'main'
|
|
||||||
if is_main {
|
if is_main {
|
||||||
if g.pref.os == .windows {
|
if g.pref.os == .windows {
|
||||||
if g.is_gui_app() {
|
if g.is_gui_app() {
|
||||||
// GUI application
|
// GUI application
|
||||||
g.writeln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd')
|
g.writeln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd){')
|
||||||
g.last_fn_c_name = 'wWinMain'
|
g.last_fn_c_name = 'wWinMain'
|
||||||
} else {
|
} else {
|
||||||
// Console application
|
// Console application
|
||||||
g.writeln('int wmain(int ___argc, wchar_t* ___argv[], wchar_t* ___envp[]')
|
g.writeln('int wmain(int ___argc, wchar_t* ___argv[], wchar_t* ___envp[]){')
|
||||||
g.last_fn_c_name = 'wmain'
|
g.last_fn_c_name = 'wmain'
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
g.write('int ${it.name}(int ___argc, char** ___argv')
|
g.writeln('int main(int ___argc, char** ___argv){')
|
||||||
g.last_fn_c_name = it.name
|
g.last_fn_c_name = it.name
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -51,40 +66,67 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
|
||||||
// println(name)
|
// println(name)
|
||||||
// }
|
// }
|
||||||
// type_name := g.table.Type_to_str(it.return_type)
|
// type_name := g.table.Type_to_str(it.return_type)
|
||||||
type_name := g.typ(it.return_type)
|
|
||||||
g.write('$type_name ${name}(')
|
// Live functions are protected by a mutex, because otherwise they
|
||||||
g.last_fn_c_name = name
|
// can be changed by the live reload thread, *while* they are
|
||||||
g.definitions.write('$type_name ${name}(')
|
// running, with unpredictable results (usually just crashing).
|
||||||
}
|
// For this purpose, the actual body of the live function,
|
||||||
// Receiver is the first argument
|
// is put under a non publicly accessible function, that is prefixed
|
||||||
/*
|
// with 'impl_live_' .
|
||||||
if it.is_method {
|
if is_livemain {
|
||||||
mut styp := g.typ(it.receiver.typ)
|
g.hotcode_fn_names << name
|
||||||
// if it.receiver.typ.nr_muls() > 0 {
|
}
|
||||||
// if it.rec_mut {
|
mut impl_fn_name := if is_live_wrap { 'impl_live_${name}' } else { name }
|
||||||
// styp += '*'
|
g.last_fn_c_name = impl_fn_name
|
||||||
// }
|
type_name := g.typ(it.return_type)
|
||||||
g.write('$styp $it.receiver.name ')
|
//
|
||||||
// TODO mut
|
if is_live_wrap {
|
||||||
g.definitions.write('$styp $it.receiver.name')
|
if is_livemain {
|
||||||
if it.args.len > 0 {
|
g.definitions.write('$type_name (* ${impl_fn_name})(')
|
||||||
g.write(', ')
|
g.write('$type_name no_impl_${name}(')
|
||||||
g.definitions.write(', ')
|
}
|
||||||
|
if is_liveshared {
|
||||||
|
g.definitions.write('$type_name ${impl_fn_name}(')
|
||||||
|
g.write('$type_name ${impl_fn_name}(')
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
g.definitions.write('$type_name ${name}(')
|
||||||
|
g.write('$type_name ${name}(')
|
||||||
|
}
|
||||||
|
fargs, fargtypes := g.fn_args(it.args, it.is_variadic)
|
||||||
|
if it.no_body || (g.pref.use_cache && it.is_builtin) {
|
||||||
|
// Just a function header. Builtin function bodies are defined in builtin.o
|
||||||
|
g.definitions.writeln(');')
|
||||||
|
g.writeln(');')
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
*/
|
|
||||||
//
|
|
||||||
g.fn_args(it.args, it.is_variadic)
|
|
||||||
if it.no_body || (g.pref.use_cache && it.is_builtin) {
|
|
||||||
// Just a function header.
|
|
||||||
// Builtin function bodies are defined in builtin.o
|
|
||||||
g.definitions.writeln(');')
|
|
||||||
g.writeln(');')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
g.writeln(') {')
|
|
||||||
if !is_main {
|
|
||||||
g.definitions.writeln(');')
|
g.definitions.writeln(');')
|
||||||
|
g.writeln(') {')
|
||||||
|
|
||||||
|
if is_livemain {
|
||||||
|
// The live function just calls its implementation dual, while ensuring
|
||||||
|
// that the call is wrapped by the mutex lock & unlock calls.
|
||||||
|
// Adding the mutex lock/unlock inside the body of the implementation
|
||||||
|
// function is not reliable, because the implementation function can do
|
||||||
|
// an early exit, which will leave the mutex locked.
|
||||||
|
mut fn_args_list := []string{}
|
||||||
|
for ia, fa in fargs {
|
||||||
|
fn_args_list << '${fargtypes[ia]} ${fa}'
|
||||||
|
}
|
||||||
|
mut live_fncall := '${impl_fn_name}(' + fargs.join(', ') + ');'
|
||||||
|
mut live_fnreturn := ''
|
||||||
|
if type_name != 'void' {
|
||||||
|
live_fncall = '${type_name} res = ${live_fncall}'
|
||||||
|
live_fnreturn = 'return res;'
|
||||||
|
}
|
||||||
|
g.definitions.writeln('$type_name ${name}('+fn_args_list.join(', ')+');')
|
||||||
|
g.hotcode_definitions.writeln('$type_name ${name}('+fn_args_list.join(', ')+'){')
|
||||||
|
g.hotcode_definitions.writeln(' pthread_mutex_lock(&live_fn_mutex);')
|
||||||
|
g.hotcode_definitions.writeln(' $live_fncall')
|
||||||
|
g.hotcode_definitions.writeln(' pthread_mutex_unlock(&live_fn_mutex);')
|
||||||
|
g.hotcode_definitions.writeln(' $live_fnreturn')
|
||||||
|
g.hotcode_definitions.writeln('}')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if is_main {
|
if is_main {
|
||||||
if g.pref.os == .windows && g.is_gui_app() {
|
if g.pref.os == .windows && g.is_gui_app() {
|
||||||
|
@ -106,6 +148,10 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if g.pref.is_livemain && is_main {
|
||||||
|
g.generate_hotcode_reloading_main_caller()
|
||||||
|
}
|
||||||
// Profiling mode? Start counting at the beginning of the function (save current time).
|
// Profiling mode? Start counting at the beginning of the function (save current time).
|
||||||
if g.pref.is_prof {
|
if g.pref.is_prof {
|
||||||
g.profile_fn(it.name, is_main)
|
g.profile_fn(it.name, is_main)
|
||||||
|
@ -130,7 +176,9 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
|
||||||
}
|
}
|
||||||
g.writeln('}')
|
g.writeln('}')
|
||||||
g.defer_stmts = []
|
g.defer_stmts = []
|
||||||
g.fn_decl = 0
|
if g.pref.printfn_list.len > 0 && g.last_fn_c_name in g.pref.printfn_list {
|
||||||
|
println(g.out.after(fn_start_pos))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) write_defer_stmts_when_needed() {
|
fn (mut g Gen) write_defer_stmts_when_needed() {
|
||||||
|
@ -145,7 +193,9 @@ fn (mut g Gen) write_defer_stmts_when_needed() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) {
|
fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) ([]string, []string) {
|
||||||
|
mut fargs := []string{}
|
||||||
|
mut fargtypes := []string{}
|
||||||
no_names := args.len > 0 && args[0].name == 'arg_1'
|
no_names := args.len > 0 && args[0].name == 'arg_1'
|
||||||
for i, arg in args {
|
for i, arg in args {
|
||||||
arg_type_sym := g.table.get_type_symbol(arg.typ)
|
arg_type_sym := g.table.get_type_symbol(arg.typ)
|
||||||
|
@ -164,6 +214,8 @@ fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) {
|
||||||
if !info.is_anon {
|
if !info.is_anon {
|
||||||
g.write(arg_type_name + ' ' + arg.name)
|
g.write(arg_type_name + ' ' + arg.name)
|
||||||
g.definitions.write(arg_type_name + ' ' + arg.name)
|
g.definitions.write(arg_type_name + ' ' + arg.name)
|
||||||
|
fargs << arg.name
|
||||||
|
fargtypes << arg_type_name
|
||||||
} else {
|
} else {
|
||||||
g.write('${g.typ(func.return_type)} (*$arg.name)(')
|
g.write('${g.typ(func.return_type)} (*$arg.name)(')
|
||||||
g.definitions.write('${g.typ(func.return_type)} (*$arg.name)(')
|
g.definitions.write('${g.typ(func.return_type)} (*$arg.name)(')
|
||||||
|
@ -174,6 +226,8 @@ fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) {
|
||||||
} else if no_names {
|
} else if no_names {
|
||||||
g.write(arg_type_name)
|
g.write(arg_type_name)
|
||||||
g.definitions.write(arg_type_name)
|
g.definitions.write(arg_type_name)
|
||||||
|
fargs << ''
|
||||||
|
fargtypes << arg_type_name
|
||||||
} else {
|
} else {
|
||||||
mut nr_muls := arg.typ.nr_muls()
|
mut nr_muls := arg.typ.nr_muls()
|
||||||
s := arg_type_name + ' ' + arg.name
|
s := arg_type_name + ' ' + arg.name
|
||||||
|
@ -186,12 +240,15 @@ fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) {
|
||||||
// }
|
// }
|
||||||
g.write(s)
|
g.write(s)
|
||||||
g.definitions.write(s)
|
g.definitions.write(s)
|
||||||
|
fargs << arg.name
|
||||||
|
fargtypes << arg_type_name
|
||||||
}
|
}
|
||||||
if i < args.len - 1 {
|
if i < args.len - 1 {
|
||||||
g.write(', ')
|
g.write(', ')
|
||||||
g.definitions.write(', ')
|
g.definitions.write(', ')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return fargs, fargtypes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) call_expr(node ast.CallExpr) {
|
fn (mut g Gen) call_expr(node ast.CallExpr) {
|
||||||
|
|
|
@ -0,0 +1,263 @@
|
||||||
|
module gen
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import v.pref
|
||||||
|
import v.util
|
||||||
|
|
||||||
|
fn (g &Gen) generate_hotcode_reloading_declarations() {
|
||||||
|
if g.pref.os != .windows {
|
||||||
|
if g.pref.is_livemain {
|
||||||
|
g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex = PTHREAD_MUTEX_INITIALIZER;')
|
||||||
|
}
|
||||||
|
if g.pref.is_liveshared {
|
||||||
|
g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex;')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if g.pref.is_livemain {
|
||||||
|
g.hotcode_definitions.writeln('HANDLE live_fn_mutex = 0;')
|
||||||
|
}
|
||||||
|
if g.pref.is_liveshared {
|
||||||
|
g.hotcode_definitions.writeln('HANDLE live_fn_mutex;')
|
||||||
|
g.hotcode_definitions.writeln('
|
||||||
|
void pthread_mutex_lock(HANDLE *m) {
|
||||||
|
WaitForSingleObject(*m, INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pthread_mutex_unlock(HANDLE *m) {
|
||||||
|
ReleaseMutex(*m);
|
||||||
|
}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g &Gen) generate_hotcode_reloader_code() {
|
||||||
|
if g.pref.is_liveshared {
|
||||||
|
g.hotcode_definitions.writeln('')
|
||||||
|
g.hotcode_definitions.writeln('int load_so(byteptr path) { return 0; }')
|
||||||
|
g.hotcode_definitions.writeln('')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Hot code reloading
|
||||||
|
if g.pref.is_livemain {
|
||||||
|
mut file := os.real_path(g.pref.path)
|
||||||
|
file_base := os.file_name(file).replace('.v', '')
|
||||||
|
// Need to build .so file before building the live application
|
||||||
|
// The live app needs to load this .so file on initialization.
|
||||||
|
mut vexe := pref.vexe_path()
|
||||||
|
mut so_dir := os.cache_dir()
|
||||||
|
if os.user_os() == 'windows' {
|
||||||
|
vexe = util.cescaped_path(vexe)
|
||||||
|
file = util.cescaped_path(file)
|
||||||
|
so_dir = util.cescaped_path(so_dir)
|
||||||
|
}
|
||||||
|
mut msvc := ''
|
||||||
|
if g.pref.ccompiler == 'msvc' {
|
||||||
|
msvc = '-cc msvc'
|
||||||
|
}
|
||||||
|
so_debug_flag := if g.pref.is_debug { '-cg' } else { '' }
|
||||||
|
cmd_compile_shared_library := '$vexe $msvc -cg -keepc $so_debug_flag -o ${so_dir}/${file_base} -sharedlive -shared $file'
|
||||||
|
if g.pref.is_verbose {
|
||||||
|
println(cmd_compile_shared_library)
|
||||||
|
}
|
||||||
|
ticks := time.ticks()
|
||||||
|
so_compilation_result := os.system(cmd_compile_shared_library)
|
||||||
|
if g.pref.is_verbose {
|
||||||
|
diff := time.ticks() - ticks
|
||||||
|
println('compiling shared library took $diff ms')
|
||||||
|
}
|
||||||
|
if so_compilation_result != 0 {
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
mut phd := ''
|
||||||
|
mut load_code := []string{}
|
||||||
|
if g.pref.os != .windows {
|
||||||
|
for so_fn in g.hotcode_fn_names {
|
||||||
|
load_code << 'impl_live_${so_fn} = dlsym(live_lib, "impl_live_${so_fn}");'
|
||||||
|
}
|
||||||
|
phd = posix_hotcode_definitions_1
|
||||||
|
} else {
|
||||||
|
for so_fn in g.hotcode_fn_names {
|
||||||
|
load_code << 'impl_live_${so_fn} = (void *)GetProcAddress(live_lib, "impl_live_${so_fn}"); '
|
||||||
|
}
|
||||||
|
phd = windows_hotcode_definitions_1
|
||||||
|
}
|
||||||
|
g.hotcode_definitions.writeln(phd.replace('@LOAD_FNS@', load_code.join('\n')))
|
||||||
|
g.hotcode_definitions.writeln('
|
||||||
|
|
||||||
|
void lfnmutex_print(char *s){
|
||||||
|
#if 0
|
||||||
|
fflush(stderr);
|
||||||
|
fprintf(stderr,">> live_fn_mutex: %p | %s\\n", &live_fn_mutex, s);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_so_file(char *s){
|
||||||
|
// removing the .so file from the filesystem after dlopen-ing it is safe, since it will still be mapped in memory.
|
||||||
|
#ifndef _WIN32
|
||||||
|
unlink(s);
|
||||||
|
#else
|
||||||
|
_unlink(s);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int _live_reloads = 0;
|
||||||
|
void reload_so() {
|
||||||
|
char new_so_base[PATH_MAX] = {0};
|
||||||
|
char new_so_name[PATH_MAX] = {0};
|
||||||
|
char compile_cmd[PATH_MAX] = {0};
|
||||||
|
int last = os__file_last_mod_unix(tos2("$file"));
|
||||||
|
while (1) {
|
||||||
|
// TODO use inotify
|
||||||
|
int now = os__file_last_mod_unix(tos2("$file"));
|
||||||
|
if (now != last) {
|
||||||
|
last = now;
|
||||||
|
_live_reloads++;
|
||||||
|
|
||||||
|
//v -o bounce -sharedlive -shared bounce.v
|
||||||
|
snprintf(new_so_base, sizeof (new_so_base), "${so_dir}/tmp.%d.${file_base}", _live_reloads);
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
snprintf(new_so_name, sizeof (new_so_name), "%s.dll", new_so_base);
|
||||||
|
#else
|
||||||
|
snprintf(new_so_name, sizeof (new_so_name), "%s.so", new_so_base);
|
||||||
|
#endif
|
||||||
|
snprintf(compile_cmd, sizeof (compile_cmd), "$vexe $msvc $so_debug_flag -cg -keepc -o %s -sharedlive -shared $file", new_so_base);
|
||||||
|
os__system(tos2(compile_cmd));
|
||||||
|
|
||||||
|
if( !os__exists(tos2(new_so_name)) ) {
|
||||||
|
puts("Errors while compiling $file\\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
lfnmutex_print("reload_so locking...");
|
||||||
|
pthread_mutex_lock(&live_fn_mutex);
|
||||||
|
lfnmutex_print("reload_so locked");
|
||||||
|
|
||||||
|
load_so(new_so_name);
|
||||||
|
remove_so_file( new_so_name );
|
||||||
|
|
||||||
|
lfnmutex_print("reload_so unlocking...");
|
||||||
|
pthread_mutex_unlock(&live_fn_mutex);
|
||||||
|
lfnmutex_print("reload_so unlocked");
|
||||||
|
}
|
||||||
|
time__sleep_ms(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const (
|
||||||
|
posix_hotcode_definitions_1 = '
|
||||||
|
#include <limits.h>
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
#define PATH_MAX 1024
|
||||||
|
#endif
|
||||||
|
void* live_lib = 0;
|
||||||
|
int load_so(byteptr path) {
|
||||||
|
char cpath[PATH_MAX] = {0};
|
||||||
|
int res = snprintf(cpath, sizeof(cpath), "%s", path);
|
||||||
|
if (res >= sizeof (cpath)) {
|
||||||
|
fprintf (stderr, "path is too long");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (live_lib) {
|
||||||
|
int closing_status = dlclose(live_lib);
|
||||||
|
//fprintf(stderr, "Closing status: %d\\n", closing_status);
|
||||||
|
live_lib = 0;
|
||||||
|
}
|
||||||
|
//fprintf (stderr, "Opening shared library at: %s\\n", cpath);
|
||||||
|
live_lib = dlopen(cpath, RTLD_LAZY);
|
||||||
|
if (!live_lib) {
|
||||||
|
fprintf(stderr, "open failed, reason: %s\\n", dlerror());
|
||||||
|
exit(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dlerror(); // clear errors
|
||||||
|
//fprintf(stderr, "live_lib: %p\\n", live_lib);
|
||||||
|
|
||||||
|
@LOAD_FNS@
|
||||||
|
|
||||||
|
char *dlsym_error = dlerror();
|
||||||
|
if (dlsym_error != NULL) {
|
||||||
|
fprintf(stderr, "dlsym failed, reason: %s\\n", dlsym_error);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
'
|
||||||
|
windows_hotcode_definitions_1 = '
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
#define PATH_MAX 1024
|
||||||
|
#endif
|
||||||
|
void pthread_mutex_lock(HANDLE *m) {
|
||||||
|
WaitForSingleObject(*m, INFINITE);
|
||||||
|
}
|
||||||
|
void pthread_mutex_unlock(HANDLE *m) {
|
||||||
|
ReleaseMutex(*m);
|
||||||
|
}
|
||||||
|
void* live_lib = NULL;
|
||||||
|
int load_so(byteptr path) {
|
||||||
|
char cpath[PATH_MAX];
|
||||||
|
int res = snprintf(cpath, sizeof(cpath), "%s", path);
|
||||||
|
if (res >= sizeof(cpath)) {
|
||||||
|
puts("path is too long\\n");
|
||||||
|
exit(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (live_lib) FreeLibrary(live_lib);
|
||||||
|
live_lib = LoadLibraryA(cpath);
|
||||||
|
if (!live_lib) {
|
||||||
|
puts("open failed\\n");
|
||||||
|
exit(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@LOAD_FNS@
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
'
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
fn (g &Gen) generate_hotcode_reloading_main_caller() {
|
||||||
|
if !g.pref.is_livemain {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.writeln('')
|
||||||
|
// We are in live code reload mode, so start the .so loader in the background
|
||||||
|
file_base := os.file_name(g.pref.path).replace('.v', '')
|
||||||
|
mut so_dir := os.cache_dir()
|
||||||
|
if os.user_os() == 'windows' {
|
||||||
|
so_dir = util.cescaped_path(so_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.writeln('\t// live code initialization section:')
|
||||||
|
g.writeln('\t{')
|
||||||
|
g.writeln('\t\t// initialization of live function pointers')
|
||||||
|
for fname in g.hotcode_fn_names {
|
||||||
|
g.writeln('\t\timpl_live_${fname} = 0;')
|
||||||
|
}
|
||||||
|
|
||||||
|
g.writeln('\t\t// start background reloading thread')
|
||||||
|
if g.pref.os != .windows {
|
||||||
|
// unix:
|
||||||
|
so_name := file_base + '.so'
|
||||||
|
g.writeln('\t\tchar *live_library_name = "${so_dir}/$so_name";')
|
||||||
|
g.writeln('\t\tload_so(live_library_name);')
|
||||||
|
g.writeln('\t\tpthread_t _thread_so;')
|
||||||
|
g.writeln('\t\tpthread_create(&_thread_so , NULL, (void *)&reload_so, live_library_name);')
|
||||||
|
} else {
|
||||||
|
// windows:
|
||||||
|
so_extension := if g.pref.ccompiler == 'msvc' { '.dll' } else { '.so' }
|
||||||
|
so_name := file_base + so_extension
|
||||||
|
g.writeln('\t\tchar *live_library_name = "${so_dir}/$so_name";')
|
||||||
|
g.writeln('\t\tlive_fn_mutex = CreateMutexA(0, 0, 0);')
|
||||||
|
g.writeln('\t\tload_so(live_library_name);')
|
||||||
|
g.writeln('\t\tunsigned long _thread_so;')
|
||||||
|
g.writeln('\t\t_thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);')
|
||||||
|
}
|
||||||
|
g.writeln('\t}\t// end of live code initialization section')
|
||||||
|
g.writeln('')
|
||||||
|
}
|
|
@ -1058,7 +1058,7 @@ fn (mut p Parser) return_stmt() ast.Return {
|
||||||
|
|
||||||
// left hand side of `=` or `:=` in `a,b,c := 1,2,3`
|
// left hand side of `=` or `:=` in `a,b,c := 1,2,3`
|
||||||
fn (mut p Parser) global_decl() ast.GlobalDecl {
|
fn (mut p Parser) global_decl() ast.GlobalDecl {
|
||||||
if !p.pref.translated && !p.pref.is_live && !p.builtin_mod && !p.pref.building_v && p.mod !=
|
if !p.pref.translated && !p.pref.is_livemain && !p.builtin_mod && !p.pref.building_v && p.mod !=
|
||||||
'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !os.getwd().contains('/volt') && !p.pref.enable_globals {
|
'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !os.getwd().contains('/volt') && !p.pref.enable_globals {
|
||||||
p.error('use `v --enable-globals ...` to enable globals')
|
p.error('use `v --enable-globals ...` to enable globals')
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,9 @@ pub mut:
|
||||||
// nofmt bool // disable vfmt
|
// nofmt bool // disable vfmt
|
||||||
is_test bool // `v test string_test.v`
|
is_test bool // `v test string_test.v`
|
||||||
is_script bool // single file mode (`v program.v`), main function can be skipped
|
is_script bool // single file mode (`v program.v`), main function can be skipped
|
||||||
is_live bool // main program that contains live/hot code
|
is_livemain bool // main program that contains live/hot code
|
||||||
is_shared bool // an ordinary shared library, -shared, no matter if it is live or not
|
is_liveshared bool // a shared library, that will be used in a -live main program
|
||||||
|
is_shared bool // an ordinary shared library, -shared, no matter if it is live or not
|
||||||
is_prof bool // benchmark every function
|
is_prof bool // benchmark every function
|
||||||
profile_file string // the profile results will be stored inside profile_file
|
profile_file string // the profile results will be stored inside profile_file
|
||||||
translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc
|
translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc
|
||||||
|
@ -80,7 +81,7 @@ pub mut:
|
||||||
mod string
|
mod string
|
||||||
run_args []string // `v run x.v 1 2 3` => `1 2 3`
|
run_args []string // `v run x.v 1 2 3` => `1 2 3`
|
||||||
printfn_list []string // a list of generated function names, whose source should be shown, for debugging
|
printfn_list []string // a list of generated function names, whose source should be shown, for debugging
|
||||||
print_v_files bool // when true, just print the list of all parsed .v files then stop.
|
print_v_files bool // when true, just print the list of all parsed .v files then stop.
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn backend_from_string(s string) ?Backend {
|
pub fn backend_from_string(s string) ?Backend {
|
||||||
|
|
|
@ -16,7 +16,7 @@ fn pmessage() {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println('START')
|
println('START')
|
||||||
for i := 0; i<6*100; i++ {
|
for i := 0; i<3*100; i++ {
|
||||||
pmessage()
|
pmessage()
|
||||||
time.sleep_ms(10)
|
time.sleep_ms(10)
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ fn testsuite_end() {
|
||||||
eprintln('source: $source_file')
|
eprintln('source: $source_file')
|
||||||
eprintln('output: $output_file')
|
eprintln('output: $output_file')
|
||||||
$if !windows {
|
$if !windows {
|
||||||
os.system('cat $output_file')
|
os.system('cat $output_file | sort | uniq -c | sort -n')
|
||||||
}
|
}
|
||||||
println('---------------------------------------------------------------------------')
|
println('---------------------------------------------------------------------------')
|
||||||
output_lines := os.read_lines(output_file) or {
|
output_lines := os.read_lines(output_file) or {
|
||||||
|
@ -57,8 +57,8 @@ fn testsuite_end() {
|
||||||
println('---------------------------------------------------------------------------')
|
println('---------------------------------------------------------------------------')
|
||||||
assert histogram['START'] > 0
|
assert histogram['START'] > 0
|
||||||
assert histogram['END'] > 0
|
assert histogram['END'] > 0
|
||||||
assert histogram['CHANGED'] + histogram['ANOTHER'] > 0
|
|
||||||
assert histogram['ORIGINAL'] > 0
|
assert histogram['ORIGINAL'] > 0
|
||||||
|
assert histogram['CHANGED'] + histogram['ANOTHER'] > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_source(new string) {
|
fn change_source(new string) {
|
||||||
|
@ -75,7 +75,7 @@ fn test_live_program_can_be_compiled() {
|
||||||
eprintln('Compiling and running with: $cmd')
|
eprintln('Compiling and running with: $cmd')
|
||||||
res := os.system(cmd)
|
res := os.system(cmd)
|
||||||
eprintln('... running in the background')
|
eprintln('... running in the background')
|
||||||
time.sleep_ms(3000)
|
time.sleep_ms(1500)
|
||||||
assert res == 0
|
assert res == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +90,6 @@ fn test_live_program_can_be_changed_2() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_live_program_has_ended() {
|
fn test_live_program_has_ended() {
|
||||||
time.sleep_ms(10 * 1000)
|
time.sleep_ms(3500)
|
||||||
assert true
|
assert true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue