229 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			V
		
	
	
module compiler
 | 
						|
 | 
						|
import os
 | 
						|
import time
 | 
						|
 | 
						|
fn (v &V) generate_hotcode_reloading_compiler_flags() []string {
 | 
						|
	mut a := []string
 | 
						|
	if v.pref.is_live || v.pref.is_so {
 | 
						|
		// See 'man dlopen', and test running a GUI program compiled with -live
 | 
						|
		if (v.os == .linux || os.user_os() == 'linux'){
 | 
						|
			a << '-rdynamic'
 | 
						|
		}
 | 
						|
		if (v.os == .mac || os.user_os() == 'mac'){
 | 
						|
			a << '-flat_namespace'
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return a
 | 
						|
}
 | 
						|
 | 
						|
fn (v &V) generate_hotcode_reloading_declarations() {
 | 
						|
	mut cgen := v.cgen
 | 
						|
	if v.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 &V) generate_hotcode_reloading_main_caller() {
 | 
						|
	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.filename(v.dir).replace('.v', '')
 | 
						|
	if v.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, &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 &V) generate_hot_reload_code() {
 | 
						|
	mut cgen := v.cgen
 | 
						|
	
 | 
						|
	// Hot code reloading
 | 
						|
	if v.pref.is_live {
 | 
						|
		mut file := os.realpath(v.dir)
 | 
						|
		file_base := os.filename(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 -shared $file'
 | 
						|
		if v.pref.show_c_cmd {
 | 
						|
			println(cmd_compile_shared_library)
 | 
						|
		}
 | 
						|
		ticks := time.ticks()
 | 
						|
		os.system(cmd_compile_shared_library)
 | 
						|
		diff := time.ticks() - ticks
 | 
						|
		println('compiling shared library took $diff ms')
 | 
						|
		println('=========\n')
 | 
						|
		
 | 
						|
		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);
 | 
						|
	}
 | 
						|
}
 | 
						|
')
 | 
						|
 | 
						|
		if v.os != .windows {
 | 
						|
			cgen.genln('
 | 
						|
#include <dlfcn.h>
 | 
						|
void* live_lib=0;
 | 
						|
int load_so(byteptr path) {
 | 
						|
	char cpath[1024];
 | 
						|
	sprintf(cpath,"./%s", path);
 | 
						|
	//printf("load_so %s\\n", cpath);
 | 
						|
	if (live_lib) dlclose(live_lib);
 | 
						|
	live_lib = dlopen(cpath, RTLD_LAZY);
 | 
						|
	if (!live_lib) {
 | 
						|
		puts("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('
 | 
						|
void pthread_mutex_lock(HANDLE *m) {
 | 
						|
	WaitForSingleObject(*m, INFINITE);
 | 
						|
}
 | 
						|
 | 
						|
void pthread_mutex_unlock(HANDLE *m) {
 | 
						|
	ReleaseMutex(*m);
 | 
						|
}
 | 
						|
 | 
						|
void* live_lib=0;
 | 
						|
int load_so(byteptr path) {
 | 
						|
	char cpath[1024];
 | 
						|
	sprintf(cpath, "./%s", path);
 | 
						|
	if (live_lib) FreeLibrary(live_lib);
 | 
						|
	live_lib = LoadLibraryA(cpath);
 | 
						|
	if (!live_lib) {
 | 
						|
		puts("open failed");
 | 
						|
		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[1024];
 | 
						|
	char new_so_name[1024];
 | 
						|
	char compile_cmd[1024];
 | 
						|
	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
 | 
						|
			sprintf(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
 | 
						|
			sprintf(new_so_name, "%s.dll", new_so_base);
 | 
						|
			#else
 | 
						|
			sprintf(new_so_name, "%s.so", new_so_base);
 | 
						|
			#endif
 | 
						|
			sprintf(compile_cmd, "$vexe $msvc -o %s -shared $file", new_so_base);
 | 
						|
			os__system(tos2(compile_cmd));
 | 
						|
 | 
						|
			if( !os__file_exists(tos2(new_so_name)) ) {
 | 
						|
				fprintf(stderr, "Errors while compiling $file\\n");
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			lfnmutex_print("reload_so locking...");
 | 
						|
			pthread_mutex_lock(&live_fn_mutex);
 | 
						|
			lfnmutex_print("reload_so locked");
 | 
						|
 | 
						|
			live_lib = 0; // hack: force skipping dlclose/1, the code may be still used...
 | 
						|
			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; }')
 | 
						|
	}
 | 
						|
}
 |