From e25ea7f9dd986f1bf95b0d2b0c080fbece59023a Mon Sep 17 00:00:00 2001 From: Emily Hudson Date: Tue, 23 Jul 2019 22:23:13 +0100 Subject: [PATCH] add MSVC C backend support; fix live code reloading on Windows; other Windows fixes --- compiler/fn.v | 37 +- compiler/main.v | 123 ++- compiler/msvc.v | 278 +++++++ compiler/parser.v | 71 +- .../microsoft_craziness/microsoft_craziness.c | 24 + .../microsoft_craziness/microsoft_craziness.h | 718 ++++++++++++++++++ vlib/os/os.v | 12 +- vlib/os/os_nix.v | 1 + vlib/time/time.v | 9 +- 9 files changed, 1231 insertions(+), 42 deletions(-) create mode 100644 compiler/msvc.v create mode 100644 thirdparty/microsoft_craziness/microsoft_craziness.c create mode 100644 thirdparty/microsoft_craziness/microsoft_craziness.h diff --git a/compiler/fn.v b/compiler/fn.v index 7dc2cf5001..d98c877914 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -254,6 +254,13 @@ fn (p mut Parser) fn_decl() { typ = 'int' str_args = 'int argc, char** argv' } + + mut dll_export_linkage := '' + + if p.os == .msvc && p.attr == 'live' && p.pref.is_so { + dll_export_linkage = '__declspec(dllexport) ' + } + // Only in C code generate User_register() instead of register() // Internally it's still stored as "register" in type User // mut fn_name_cgen := f.name @@ -269,7 +276,7 @@ fn (p mut Parser) fn_decl() { if p.pref.obfuscate { p.genln('; // $f.name') } - p.genln('$typ $fn_name_cgen($str_args) {') + p.genln('$dll_export_linkage$typ $fn_name_cgen($str_args) {') } if is_fn_header { p.genln('$typ $fn_name_cgen($str_args);') @@ -330,7 +337,7 @@ fn (p mut Parser) fn_decl() { fn_name_cgen = '(* $fn_name_cgen )' } // Actual fn declaration! - mut fn_decl := '$typ $fn_name_cgen($str_args)' + mut fn_decl := '$dll_export_linkage$typ $fn_name_cgen($str_args)' if p.pref.obfuscate { fn_decl += '; // ${f.name}' } @@ -363,11 +370,21 @@ fn (p mut Parser) fn_decl() { // We are in live code reload mode, call the .so loader in bg if p.pref.is_live { file_base := p.file_path.replace('.v', '') - so_name := file_base + '.so' - p.genln(' + if p.os != .windows && p.os != .msvc { + so_name := file_base + '.so' + p.genln(' load_so("$so_name"); pthread_t _thread_so; pthread_create(&_thread_so , NULL, &reload_so, NULL); ') + } else { + so_name := file_base + if p.os == .msvc {'.dll'} else {'.so'} + p.genln(' +live_fn_mutex = CreateMutexA(0, 0, 0); +load_so("$so_name"); +unsigned long _thread_so; +_thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0); + ') + } } if p.pref.is_test && !p.scanner.file_path.contains('/volt') { p.error('tests cannot have function `main`') @@ -454,6 +471,7 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type // str_args contains the args for the wrapper function: // wrapper(arg_struct * arg) { fn("arg->a, arg->b"); } mut str_args := '' + mut did_gen_something := false for i, arg in f.args { arg_struct += '$arg.typ $arg.name ;'// Add another field (arg) to the tmp struct definition str_args += 'arg->$arg.name' @@ -472,7 +490,14 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type p.check(.comma) str_args += ',' } + did_gen_something = true } + + if p.os == .msvc && !did_gen_something { + // Msvc doesnt like empty struct + arg_struct += 'void *____dummy_variable;' + } + arg_struct += '} $arg_struct_name ;' // Also register the wrapper, so we can use the original function without modifying it fn_name = p.table.cgen_name(f) @@ -482,7 +507,7 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type // Create thread object tmp_nr := p.get_tmp_counter() thread_name = '_thread$tmp_nr' - if p.os != .windows { + if p.os != .windows && p.os != .msvc { p.genln('pthread_t $thread_name;') } tmp2 := p.get_tmp() @@ -491,7 +516,7 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type parg = ' $tmp_struct' } // Call the wrapper - if p.os == .windows { + if p.os == .windows || p.os == .msvc { p.genln(' CreateThread(0,0, $wrapper_name, $parg, 0,0);') } else { diff --git a/compiler/main.v b/compiler/main.v index 0eee09ab11..ef552b95c0 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -29,7 +29,7 @@ fn modules_path() string { } const ( - SupportedPlatforms = ['windows', 'mac', 'linux', 'freebsd', 'openbsd', 'netbsd', 'dragonfly'] + SupportedPlatforms = ['windows', 'mac', 'linux', 'freebsd', 'openbsd', 'netbsd', 'dragonfly', 'msvc'] ModPath = modules_path() ) @@ -41,6 +41,7 @@ enum OS { openbsd netbsd dragonfly + msvc } enum Pass { @@ -189,9 +190,22 @@ fn (v mut V) compile() { #include // int64_t etc -#ifdef _WIN32 +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN #include -//#include +//#include +#ifdef _MSC_VER +// On MSVC these are the same (as long as /volatile:ms is passed) +#define _Atomic volatile +#endif + +void pthread_mutex_lock(HANDLE *m) { + WaitForSingleObject(*m, INFINITE); +} + +void pthread_mutex_unlock(HANDLE *m) { + ReleaseMutex(*m); +} #else #include #endif @@ -246,12 +260,22 @@ int load_so(byteptr); void reload_so(); void init_consts();') - 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;') + if v.os != .windows && v.os != .msvc { + 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;') + } + if v.pref.is_live { + cgen.genln('HANDLE live_fn_mutex = 0;') + } } + imports_json := v.table.imports.contains('json') // TODO remove global UI hack @@ -386,8 +410,24 @@ string _STR_TMP(const char *fmt, ...) { 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. - vexe := os.args[0] - os.system('$vexe -o $file_base -shared $file') + mut vexe := os.args[0] + + if os.user_os() == 'windows' { + vexe = vexe.replace('\\', '\\\\') + } + + mut msvc := '' + if v.os == .msvc { + msvc = '-os msvc' + } + + mut debug := '' + + if v.pref.is_debug { + debug = '-debug' + } + + os.system('$vexe $msvc $debug -o $file_base -shared $file') cgen.genln(' void lfnmutex_print(char *s){ @@ -397,7 +437,10 @@ void lfnmutex_print(char *s){ fflush(stderr); } } +') + if v.os != .windows && v.os != .msvc { + cgen.genln(' #include void* live_lib=0; int load_so(byteptr path) { @@ -412,8 +455,28 @@ int load_so(byteptr path) { return 0; } ') - for so_fn in cgen.so_fns { - cgen.genln('$so_fn = dlsym(live_lib, "$so_fn"); ') + for so_fn in cgen.so_fns { + cgen.genln('$so_fn = dlsym(live_lib, "$so_fn"); ') + } + } + else { + cgen.genln(' +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; @@ -434,8 +497,17 @@ void reload_so() { //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); - sprintf(compile_cmd, "$vexe -o %s -shared $file", 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)) ) { @@ -443,13 +515,17 @@ void reload_so() { continue; } - lfnmutex_print("reload_so locking..."); - pthread_mutex_lock(&live_fn_mutex); + 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); + 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}"); //} @@ -485,7 +561,8 @@ void reload_so() { './' + v.out_name } $if windows { - cmd = v.out_name + cmd = v.out_name + cmd = cmd.replace('/', '\\') } if os.args.len > 3 { cmd += ' ' + os.args.right(3).join(' ') @@ -569,8 +646,6 @@ fn (c &V) cc_windows_cross() { } println('Done!') } - - fn (v mut V) cc() { // Cross compiling for Windows @@ -580,6 +655,11 @@ fn (v mut V) cc() { return } } + if v.os == .msvc { + cc_msvc(v) + return + } + linux_host := os.user_os() == 'linux' v.log('cc() isprod=$v.pref.is_prod outname=$v.out_name') mut a := [v.pref.cflags, '-w'] // arguments for the C compiler @@ -752,7 +832,7 @@ fn (v &V) v_files_from_dir(dir string) []string { if file.ends_with('_test.v') { continue } - if file.ends_with('_win.v') && v.os != .windows { + if file.ends_with('_win.v') && (v.os != .windows && v.os != .msvc) { continue } if file.ends_with('_lin.v') && v.os != .linux { @@ -761,7 +841,7 @@ fn (v &V) v_files_from_dir(dir string) []string { if file.ends_with('_mac.v') && v.os != .mac { continue } - if file.ends_with('_nix.v') && v.os == .windows { + if file.ends_with('_nix.v') && (v.os == .windows || v.os == .msvc) { continue } res << '$dir/$file' @@ -1004,6 +1084,7 @@ fn new_v(args[]string) *V { case 'openbsd': _os = .openbsd case 'netbsd': _os = .netbsd case 'dragonfly': _os = .dragonfly + case 'msvc': _os = .msvc } } builtins := [ diff --git a/compiler/msvc.v b/compiler/msvc.v new file mode 100644 index 0000000000..df46c80f72 --- /dev/null +++ b/compiler/msvc.v @@ -0,0 +1,278 @@ +module main + +import os + +#flag -I @VROOT/thirdparty/microsoft_craziness +#flag windows @VROOT/thirdparty/microsoft_craziness/microsoft_craziness.o +#flag windows -l ole32 +#flag windows -l oleaut32 + +// Emily: If these arent included then msvc assumes that +// these return int (which should be 64bit) +// but then it goes and sign extends our pointer types anyway +// which breaks everything +#include + +struct MsvcResult { + sdk_ver int + + windows_sdk_root_path string + exe_path string + + um_lib_path string + ucrt_lib_path string + vs_lib_path string + + um_include_path string + ucrt_include_path string + vs_include_path string + shared_include_path string +} + +struct FindResult { + sdk_ver int + windows_sdk_root byteptr + windows_sdk_um_library_path byteptr + windows_sdk_ucrt_library_path byteptr + vs_exe_path byteptr + vs_library_path byteptr +} + +fn C.find_visual_studio_and_windows_sdk() *FindResult +fn C.wide_string_to_narrow_temp(byteptr) byteptr + +fn find_msvc() *MsvcResult { + $if windows { + r := C.find_visual_studio_and_windows_sdk() + + windows_sdk_root := tos_clone(C.wide_string_to_narrow_temp(r.windows_sdk_root)) + ucrt_lib_folder := tos_clone(C.wide_string_to_narrow_temp(r.windows_sdk_ucrt_library_path)) + um_lib_folder := tos_clone(C.wide_string_to_narrow_temp(r.windows_sdk_um_library_path)) + vs_lib_folder := tos_clone(C.wide_string_to_narrow_temp(r.vs_library_path)) + exe_folder := tos_clone(C.wide_string_to_narrow_temp(r.vs_exe_path)) + + mut ucrt_include_folder := ucrt_lib_folder.replace('Lib', 'Include') + mut vs_include_folder := vs_lib_folder.replace('lib', 'include') + + if ucrt_include_folder.ends_with('\\x64') { + ucrt_include_folder = ucrt_include_folder.left(ucrt_include_folder.len - 4) + } + if vs_include_folder.ends_with('\\x64') { + vs_include_folder = vs_include_folder.left(vs_include_folder.len - 4) + } + + um_include_folder := ucrt_include_folder.replace('ucrt', 'um') + shared_include_folder := ucrt_include_folder.replace('ucrt', 'shared') + + return &MsvcResult { + sdk_ver: r.sdk_ver, + windows_sdk_root_path: windows_sdk_root, + exe_path: exe_folder, + + um_lib_path: um_lib_folder, + ucrt_lib_path: ucrt_lib_folder, + vs_lib_path: vs_lib_folder, + + um_include_path: um_include_folder, + ucrt_include_path: ucrt_include_folder, + vs_include_path: vs_include_folder, + shared_include_path: shared_include_folder, + } + } + $else { + panic('Cannot find msvc on this platform') + } +} + +pub fn cc_msvc(v *V) { + r := find_msvc() + + mut a := ['-w', '/volatile:ms'] // arguments for the C compiler + + // cl.exe is stupid so these are in a different order to the ones below! + + if v.pref.is_prod { + a << '/O2' + a << '/MD' + } else { + a << '/Z7' + a << '/MDd' + } + + if v.pref.is_so { + // Dont think we have to do anything for this + if !v.out_name.ends_with('.dll') { + v.out_name = v.out_name + '.dll' + } + + // Build dll + a << '/LD' + } else if !v.out_name.ends_with('.exe') { + v.out_name = v.out_name + '.exe' + } + + mut libs := ''// builtin.o os.o http.o etc + if v.pref.build_mode == .build { + } + else if v.pref.build_mode == .embed_vlib { + // + } + else if v.pref.build_mode == .default_mode { + libs = '"$ModPath/vlib/builtin.obj"' + if !os.file_exists(libs) { + println('`builtin.obj` not found') + exit(1) + } + for imp in v.table.imports { + if imp == 'webview' { + continue + } + libs += ' "$ModPath/vlib/${imp}.obj"' + } + } + + if v.pref.sanitize { + println('Sanitize not supported on msvc.') + } + + // The C file we are compiling + //a << '"$TmpPath/$v.out_name_c"' + // this isnt correct for some reason + // so fix that now + + a << '".$v.out_name_c"' + + mut other_flags := []string{} + mut real_libs := []string{} + mut lib_paths := []string{} + + for f in v.table.flags { + // We need to see if the flag contains -l + // -l isnt recognised and these libs will be passed straight to the linker + // by the compiler + if f.starts_with('-l') { + lib_base := f.right(2).trim_space() + + // MSVC has no method of linking against a .dll + // TODO: we should look for .defs aswell + lib_lib := lib_base + '.lib' + real_libs << lib_lib + } + else if f.starts_with('-L') { + lib_paths << f.right(2).trim_space() + } + else if f.ends_with('.o') { + // msvc expects .obj not .o + other_flags << f + 'bj' + } + else { + other_flags << f + } + } + + default_libs := [ + 'kernel32.lib', + 'user32.lib', + 'gdi32.lib', + 'winspool.lib', + 'comdlg32.lib', + 'advapi32.lib', + 'shell32.lib', + 'ole32.lib', + 'oleaut32.lib', + 'uuid.lib', + 'odbc32.lib', + 'odbccp32.lib', + 'vcruntime.lib', + 'kernel32.lib', + ] + + for l in default_libs { + real_libs << l + } + + + // flags := v.table.flags.join(' ') + + // Include the base paths + a << '-I "$r.ucrt_include_path" -I "$r.vs_include_path" -I "$r.um_include_path" -I "$r.shared_include_path"' + + // Msvc also doesnt have atomic + // TODO: dont rely on gcc's _Atomic semantics! + a << other_flags + + // TODO: libs will need to be actually handled properly + a << real_libs.join(' ') + + a << '/link' + a << '/NOLOGO' + a << '/OUT:$v.out_name' + a << '/LIBPATH:"$r.ucrt_lib_path"' + a << '/LIBPATH:"$r.um_lib_path"' + a << '/LIBPATH:"$r.vs_lib_path"' + a << '/INCREMENTAL:NO' // Disable incremental linking + + for l in lib_paths { + a << '/LIBPATH:"$l"' + } + + if !v.pref.is_prod { + a << '/DEBUG:FULL' + } + + args := a.join(' ') + + // println('$args') + // println('$exe_path') + + escaped_path := r.exe_path + + cmd := '""$escaped_path\\cl.exe" $args"' + + // println('$cmd') + + res := os.exec(cmd) + // println(res) + // println('C OUTPUT:') + if res.contains('error') { + println(res) + panic('msvc error') + } + + if !v.pref.is_debug && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' { + os.rm('.$v.out_name_c') + } + +} + +fn build_thirdparty_obj_file_with_msvc(flag string) { + msvc := find_msvc() + + mut obj_path := flag.all_after(' ') + + if obj_path.ends_with('.o') { + // msvc expects .obj not .o + obj_path = obj_path + 'bj' + } + + if os.file_exists(obj_path) { + return + } + println('$obj_path not found, building it (with msvc)...') + parent := obj_path.all_before_last('/').trim_space() + files := os.ls(parent) + + mut cfiles := '' + for file in files { + if file.ends_with('.c') { + cfiles += parent + '/' + file + ' ' + } + } + + include_string := '-I "$msvc.ucrt_include_path" -I "$msvc.vs_include_path" -I "$msvc.um_include_path" -I "$msvc.shared_include_path"' + + println('$cfiles') + + res := os.exec('""$msvc.exe_path\\cl.exe" /volatile:ms /Z7 $include_string /c $cfiles /Fo"$obj_path""') + println(res) +} \ No newline at end of file diff --git a/compiler/parser.v b/compiler/parser.v index 213f2f1095..a39e7fd9cd 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -540,6 +540,7 @@ fn (p mut Parser) struct_decl() { } println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.run') */ + mut did_gen_something := false for p.tok != .rcbr { if p.tok == .key_pub { if is_pub { @@ -604,16 +605,25 @@ fn (p mut Parser) struct_decl() { attr = p.check_name() p.check(.rsbr) } + did_gen_something = true + typ.add_field(field_name, field_type, is_mut, attr, access_mod) p.fgenln('') } + if !is_ph && p.first_run() { p.table.register_type2(typ) //println('registering 1 nrfields=$typ.fields.len') } + p.check(.rcbr) if !is_c { - p.gen_type('}; ') + if p.os == .msvc && !did_gen_something { + p.gen_type('void *____dummy_variable; };') + p.fgenln('') + } else { + p.gen_type('}; ') + } } if is_objc { p.gen_type('@end') @@ -1891,7 +1901,10 @@ fn (p mut Parser) index_expr(typ string, fn_ph int) string { tmp_ok := p.get_tmp() if is_map { p.gen('$tmp') - def := type_default(typ) + mut def := type_default(typ) + if p.os == .msvc && def == '{}' { + def = '{0}' + } p.cgen.insert_before('$typ $tmp = $def; bool $tmp_ok = map_get($index_expr, & $tmp);') } else if is_arr { @@ -2015,6 +2028,11 @@ fn (p mut Parser) expression() string { } // 3 + 4 else if is_num { + if p.os == .msvc && typ == 'void*' { + // Msvc errors on void* pointer arithmatic + // ... So cast to byte* and then do the add + p.cgen.set_placeholder(ph, '(byte*)') + } p.gen(tok_op.str()) } // Vec + Vec @@ -2455,7 +2473,11 @@ fn (p mut Parser) array_init() string { name := p.check_name() if p.table.known_type(name) { p.cgen.resetln('') - p.gen('{}') + if p.os == .msvc { + p.gen('{0}') + } else { + p.gen('{}') + } return '[$lit]$name' } else { @@ -2539,7 +2561,11 @@ fn (p mut Parser) array_init() string { // p.gen('$new_arr($vals.len, $vals.len, sizeof($typ), ($typ[]) $c_arr );') // TODO why need !first_run()?? Otherwise it goes to the very top of the out.c file if !p.first_run() { - p.cgen.set_placeholder(new_arr_ph, '$new_arr($i, $i, sizeof($typ), ($typ[]) { ') + if p.os == .msvc && i == 0 { + p.cgen.set_placeholder(new_arr_ph, '$new_arr($i, $i, sizeof($typ), ($typ[]) {0 ') + } else { + p.cgen.set_placeholder(new_arr_ph, '$new_arr($i, $i, sizeof($typ), ($typ[]) { ') + } } typ = 'array_$typ' p.register_array(typ) @@ -2602,6 +2628,7 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { no_star := typ.replace('*', '') p.gen('ALLOC_INIT($no_star, {') } + mut did_gen_something := false // Loop thru all struct init keys and assign values // u := User{age:20, name:'bob'} // Remember which fields were set, so that we dont have to zero them later @@ -2627,6 +2654,7 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { p.gen(',') } p.fgenln('') + did_gen_something = true } // If we already set some fields, need to prepend a comma if t.fields.len != inited_fields.len && inited_fields.len > 0 { @@ -2650,6 +2678,7 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { p.gen(',') } } + did_gen_something = true } } // Point{3,4} syntax @@ -2680,7 +2709,13 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { if p.tok != .rcbr { p.error('too many fields initialized: `$typ` has $T.fields.len field(s)') } + did_gen_something = true } + + if p.os == .msvc && !did_gen_something { + p.gen('0') + } + p.gen('}') if ptr { p.gen(')') @@ -2761,6 +2796,7 @@ fn os_name_to_ifdef(name string) string { case 'openbsd': return '__OpenBSD__' case 'netbsd': return '__NetBSD__' case 'dragonfly': return '__DragonFly__' + case 'msvc': return '_MSC_VER' } panic('bad os ifdef name "$name"') return '' @@ -2852,7 +2888,7 @@ fn (p mut Parser) chash() { else if hash.contains('darwin') && p.os != .mac { return } - else if hash.contains('windows') && p.os != .windows { + else if hash.contains('windows') && (p.os != .windows && p.os != .msvc) { return } // Remove "linux" etc from flag @@ -2867,8 +2903,13 @@ fn (p mut Parser) chash() { } p.log('adding flag "$flag"') // `@VROOT/thirdparty/glad/glad.o`, make sure it exists, otherwise build it - if has_vroot && flag.contains('.o') { - build_thirdparty_obj_file(flag) + if has_vroot && flag.contains('.o') { + if p.os == .msvc { + build_thirdparty_obj_file_with_msvc(flag) + } + else { + build_thirdparty_obj_file(flag) + } } p.table.flags << flag return @@ -3072,7 +3113,10 @@ fn (p mut Parser) for_st() { p.genln('for (int l = 0; l < keys_$tmp .len; l++) {') p.genln(' string $i = ((string*)keys_$tmp .data)[l];') //p.genln(' string $i = *(string*) ( array__get(keys_$tmp, l) );') - def := type_default(var_typ) + mut def := type_default(typ) + if def == '{}' { + def = '{0}' + } // TODO don't call map_get() for each key, fetch values while traversing // the tree (replace `map_keys()` above with `map_key_vals()`) p.genln('$var_typ $val = $def; map_get($tmp, $i, & $val);') @@ -3262,8 +3306,15 @@ fn (p mut Parser) return_st() { tmp := p.get_tmp() ret := p.cgen.cur_line.right(ph) - p.cgen.resetln('$expr_type $tmp = ($expr_type)($ret);') - p.cgen(p.cur_fn.defer_text) + if p.os != .msvc { + p.cgen.cur_line = '$expr_type $tmp = ($expr_type)($ret);' + p.cgen.resetln('$expr_type $tmp = ($expr_type)($ret);') + } else { + // Both the return type and the expression type have already been concluded + // to be the same - the cast is slightly pointless + // and msvc cant do it + p.cgen.resetln('$expr_type $tmp = ($ret);') + } p.gen('return opt_ok(&$tmp, sizeof($expr_type))') } else { diff --git a/thirdparty/microsoft_craziness/microsoft_craziness.c b/thirdparty/microsoft_craziness/microsoft_craziness.c new file mode 100644 index 0000000000..68d41288fb --- /dev/null +++ b/thirdparty/microsoft_craziness/microsoft_craziness.c @@ -0,0 +1,24 @@ +#define MICROSOFT_CRAZINESS_IMPLEMENTATION + +#include "microsoft_craziness.h" + +#ifdef _WIN32 + +// Never hang around to a pointer from this function +// This is not thread safe +char *wide_string_to_narrow_temp(wchar_t *wc) { + static char buffer[10000][10]; + static int counter = 0; + + char *cur_buffer = buffer[counter++]; + + int len = wcslen(wc); + int c = wcstombs(cur_buffer, wc, len); + cur_buffer[c] = 0; + + // something assert len == c + + return cur_buffer; +} + +#endif \ No newline at end of file diff --git a/thirdparty/microsoft_craziness/microsoft_craziness.h b/thirdparty/microsoft_craziness/microsoft_craziness.h new file mode 100644 index 0000000000..02f1d6673f --- /dev/null +++ b/thirdparty/microsoft_craziness/microsoft_craziness.h @@ -0,0 +1,718 @@ +// +// Author: Jonathan Blow +// Version: 1 +// Date: 31 August, 2018 +// +// This code is released under the MIT license, which you can find at +// +// https://opensource.org/licenses/MIT +// +// +// +// See the comments for how to use this library just below the includes. +// + +// +// NOTE(Kalinovcic): I have translated the original implementation to C, +// and made a preprocessor define that enables people to include it without +// the implementation, just as a header. +// +// I also fixed two bugs: +// - If COM initialization for VS2017 fails, we actually properly continue +// searching for earlier versions in the registry. +// - For earlier versions, the code now returns the "bin\amd64" VS directory. +// Previously it was returning the "bin" directory, which is for x86 stuff. +// +// To include the implementation, before including microsoft_craziness.h, +// define: +// +// #define MICROSOFT_CRAZINESS_IMPLEMENTATION +// #include "microsoft_craziness.h" +// + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MICROSOFT_CRAZINESS_HEADER_GUARD +#define MICROSOFT_CRAZINESS_HEADER_GUARD + +#ifdef _WIN32 + +#include + +typedef struct { + int windows_sdk_version; // Zero if no Windows SDK found. + + wchar_t *windows_sdk_root; + wchar_t *windows_sdk_um_library_path; + wchar_t *windows_sdk_ucrt_library_path; + + wchar_t *vs_exe_path; + wchar_t *vs_library_path; +} Find_Result; + +Find_Result *find_visual_studio_and_windows_sdk(); +void free_resources(Find_Result *result); + +// Emily: wide_string_to_narrow_string_facepalm +char *wide_string_to_narrow_temp(wchar_t *); + +#endif + +#endif // MICROSOFT_CRAZINESS_HEADER_GUARD + +// +// HOW TO USE THIS CODE +// +// The purpose of this file is to find the folders that contain libraries +// you may need to link against, on Windows, if you are linking with any +// compiled C or C++ code. This will be necessary for many non-C++ programming +// language environments that want to provide compatibility. +// +// We find the place where the Visual Studio libraries live (for example, +// libvcruntime.lib), where the linker and compiler executables live +// (for example, link.exe), and where the Windows SDK libraries reside +// (kernel32.lib, libucrt.lib). +// +// We all wish you didn't have to worry about so many weird dependencies, +// but we don't really have a choice about this, sadly. +// +// I don't claim that this is the absolute best way to solve this problem, +// and so far we punt on things (if you have multiple versions of Visual Studio +// installed, we return the first one, rather than the newest). But it +// will solve the basic problem for you as simply as I know how to do it, +// and because there isn't too much code here, it's easy to modify and expand. +// +// +// Here is the API you need to know about: +// + +// +// Call find_visual_studio_and_windows_sdk, look at the resulting +// paths, then call free_resources on the result. +// +// Everything else in this file is implementation details that you +// don't need to care about. +// + +// +// This file was about 400 lines before we started adding these comments. +// You might think that's way too much code to do something as simple +// as finding a few library and executable paths. I agree. However, +// Microsoft's own solution to this problem, called "vswhere", is a +// mere EIGHT THOUSAND LINE PROGRAM, spread across 70 files, +// that they posted to github *unironically*. +// +// I am not making this up: https://github.com/Microsoft/vswhere +// +// Several people have therefore found the need to solve this problem +// themselves. We referred to some of these other solutions when +// figuring out what to do, most prominently ziglang's version, +// by Ryan Saunderson. +// +// I hate this kind of code. The fact that we have to do this at all +// is stupid, and the actual maneuvers we need to go through +// are just painful. If programming were like this all the time, +// I would quit. +// +// Because this is such an absurd waste of time, I felt it would be +// useful to package the code in an easily-reusable way, in the +// style of the stb libraries. We haven't gone as all-out as some +// of the stb libraries do (which compile in C with no includes, often). +// For this version you need C++ and the headers at the top of the file. +// +// We return the strings as Windows wide character strings. Aesthetically +// I don't like that (I think most sane programs are UTF-8 internally), +// but apparently, not all valid Windows file paths can even be converted +// correctly to UTF-8. So have fun with that. It felt safest and simplest +// to stay with wchar_t since all of this code is fully ensconced in +// Windows crazy-land. +// +// One other shortcut I took is that this is hardcoded to return the +// folders for x64 libraries. If you want x86 or arm, you can make +// slight edits to the code below, or, if enough people want this, +// I can work it in here. +// + +#ifdef MICROSOFT_CRAZINESS_IMPLEMENTATION +#ifndef MICROSOFT_CRAZINESS_IMPLEMENTATION_GUARD +#define MICROSOFT_CRAZINESS_IMPLEMENTATION_GUARD + +#ifdef _WIN32 + +#include // For _get_osfhandle +#include +#include +#include +#include + +void free_resources(Find_Result *result) { + free(result->windows_sdk_root); + free(result->windows_sdk_um_library_path); + free(result->windows_sdk_ucrt_library_path); + free(result->vs_exe_path); + free(result->vs_library_path); +} + +// COM objects for the ridiculous Microsoft craziness. + +#undef INTERFACE +#define INTERFACE ISetupInstance +DECLARE_INTERFACE_(ISetupInstance, IUnknown) { + BEGIN_INTERFACE + + // IUnknown methods + STDMETHOD(QueryInterface)(THIS_ REFIID, void **) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + // ISetupInstance methods + STDMETHOD(GetInstanceId)(THIS_ _Out_ BSTR * pbstrInstanceId) PURE; + STDMETHOD(GetInstallDate)(THIS_ _Out_ LPFILETIME pInstallDate) PURE; + STDMETHOD(GetInstallationName)(THIS_ _Out_ BSTR * pbstrInstallationName) PURE; + STDMETHOD(GetInstallationPath)(THIS_ _Out_ BSTR * pbstrInstallationPath) PURE; + STDMETHOD(GetInstallationVersion) + (THIS_ _Out_ BSTR * pbstrInstallationVersion) PURE; + STDMETHOD(GetDisplayName) + (THIS_ _In_ LCID lcid, _Out_ BSTR * pbstrDisplayName) PURE; + STDMETHOD(GetDescription) + (THIS_ _In_ LCID lcid, _Out_ BSTR * pbstrDescription) PURE; + STDMETHOD(ResolvePath) + (THIS_ _In_opt_z_ LPCOLESTR pwszRelativePath, _Out_ BSTR * pbstrAbsolutePath) + PURE; + + END_INTERFACE +}; + +#undef INTERFACE +#define INTERFACE IEnumSetupInstances +DECLARE_INTERFACE_(IEnumSetupInstances, IUnknown) { + BEGIN_INTERFACE + + // IUnknown methods + STDMETHOD(QueryInterface)(THIS_ REFIID, void **) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + // IEnumSetupInstances methods + STDMETHOD(Next) + (THIS_ _In_ ULONG celt, + _Out_writes_to_(celt, *pceltFetched) ISetupInstance * *rgelt, + _Out_opt_ _Deref_out_range_(0, celt) ULONG * pceltFetched) PURE; + STDMETHOD(Skip)(THIS_ _In_ ULONG celt) PURE; + STDMETHOD(Reset)(THIS) PURE; + STDMETHOD(Clone)(THIS_ IEnumSetupInstances * *ppenum) PURE; + + END_INTERFACE +}; + +#undef INTERFACE +#define INTERFACE ISetupConfiguration +DECLARE_INTERFACE_(ISetupConfiguration, IUnknown) { + BEGIN_INTERFACE + + // IUnknown methods + STDMETHOD(QueryInterface)(THIS_ REFIID, void **) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + + // ISetupConfiguration methods + STDMETHOD(EnumInstances) + (THIS_ _Out_ IEnumSetupInstances * *ppEnumInstances) PURE; + STDMETHOD(GetInstanceForCurrentProcess) + (THIS_ _Out_ ISetupInstance * *ppInstance) PURE; + STDMETHOD(GetInstanceForPath) + (THIS_ _In_z_ LPCWSTR wzPath, _Out_ ISetupInstance * *ppInstance) PURE; + + END_INTERFACE +}; + +#ifdef __cplusplus +#define CALL_STDMETHOD(object, method, ...) object->method(__VA_ARGS__) +#define CALL_STDMETHOD_(object, method) object->method() +#else +#define CALL_STDMETHOD(object, method, ...) \ + object->lpVtbl->method(object, __VA_ARGS__) +#define CALL_STDMETHOD_(object, method) object->lpVtbl->method(object) +#endif + +// The beginning of the actual code that does things. + +typedef struct { + int32_t best_version[4]; // For Windows 8 versions, only two of these numbers + // are used. + wchar_t *best_name; +} Version_Data; + +bool os_file_exists(wchar_t *name) { + // @Robustness: What flags do we really want to check here? + + auto attrib = GetFileAttributesW(name); + if (attrib == INVALID_FILE_ATTRIBUTES) + return false; + if (attrib & FILE_ATTRIBUTE_DIRECTORY) + return false; + + return true; +} + +#define concat2(a, b) concat(a, b, NULL, NULL) +#define concat3(a, b, c) concat(a, b, c, NULL) +#define concat4(a, b, c, d) concat(a, b, c, d) +wchar_t *concat(wchar_t *a, wchar_t *b, wchar_t *c, wchar_t *d) { + // Concatenate up to 4 wide strings together. Allocated with malloc. + // If you don't like that, use a programming language that actually + // helps you with using custom allocators. Or just edit the code. + + auto len_a = wcslen(a); + auto len_b = wcslen(b); + + auto len_c = 0; + if (c) + len_c = wcslen(c); + + auto len_d = 0; + if (d) + len_d = wcslen(d); + + wchar_t *result = (wchar_t *)malloc((len_a + len_b + len_c + len_d + 1) * 2); + memcpy(result, a, len_a * 2); + memcpy(result + len_a, b, len_b * 2); + + if (c) + memcpy(result + len_a + len_b, c, len_c * 2); + if (d) + memcpy(result + len_a + len_b + len_c, d, len_d * 2); + + result[len_a + len_b + len_c + len_d] = 0; + + return result; +} + +typedef void (*Visit_Proc_W)(wchar_t *short_name, wchar_t *full_name, + Version_Data *data); +bool visit_files_w(wchar_t *dir_name, Version_Data *data, Visit_Proc_W proc) { + + // Visit everything in one folder (non-recursively). If it's a directory + // that doesn't start with ".", call the visit proc on it. The visit proc + // will see if the filename conforms to the expected versioning pattern. + + WIN32_FIND_DATAW find_data; + + wchar_t *wildcard_name = concat2(dir_name, L"\\*"); + HANDLE handle = FindFirstFileW(wildcard_name, &find_data); + free(wildcard_name); + + if (handle == INVALID_HANDLE_VALUE) + return false; + + while (true) { + if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + (find_data.cFileName[0] != '.')) { + wchar_t *full_name = concat3(dir_name, L"\\", find_data.cFileName); + proc(find_data.cFileName, full_name, data); + free(full_name); + } + + BOOL success = FindNextFileW(handle, &find_data); + if (!success) + break; + } + + FindClose(handle); + + return true; +} + +wchar_t *find_windows_kit_root_with_key(HKEY key, wchar_t *version) { + // Given a key to an already opened registry entry, + // get the value stored under the 'version' subkey. + // If that's not the right terminology, hey, I never do registry stuff. + + DWORD required_length; + auto rc = RegQueryValueExW(key, version, NULL, NULL, NULL, &required_length); + if (rc != 0) + return NULL; + + DWORD length = + required_length + 2; // The +2 is for the maybe optional zero later on. + // Probably we are over-allocating. + wchar_t *value = (wchar_t *)malloc(length); + if (!value) + return NULL; + + rc = RegQueryValueExW(key, version, NULL, NULL, (LPBYTE)value, + &length); // We know that version is zero-terminated... + if (rc != 0) + return NULL; + + // The documentation says that if the string for some reason was not stored + // with zero-termination, we need to manually terminate it. Sigh!! + + if (value[length]) { + value[length + 1] = 0; + } + + return value; +} + +void win10_best(wchar_t *short_name, wchar_t *full_name, Version_Data *data) { + // Find the Windows 10 subdirectory with the highest version number. + + int i0, i1, i2, i3; + auto success = swscanf_s(short_name, L"%d.%d.%d.%d", &i0, &i1, &i2, &i3); + if (success < 4) + return; + + if (i0 < data->best_version[0]) + return; + else if (i0 == data->best_version[0]) { + if (i1 < data->best_version[1]) + return; + else if (i1 == data->best_version[1]) { + if (i2 < data->best_version[2]) + return; + else if (i2 == data->best_version[2]) { + if (i3 < data->best_version[3]) + return; + } + } + } + + // we have to copy_string and free here because visit_files free's the + // full_name string after we execute this function, so Win*_Data would contain + // an invalid pointer. + if (data->best_name) + free(data->best_name); + data->best_name = _wcsdup(full_name); + + if (data->best_name) { + data->best_version[0] = i0; + data->best_version[1] = i1; + data->best_version[2] = i2; + data->best_version[3] = i3; + } +} + +void win8_best(wchar_t *short_name, wchar_t *full_name, Version_Data *data) { + // Find the Windows 8 subdirectory with the highest version number. + + int i0, i1; + auto success = swscanf_s(short_name, L"winv%d.%d", &i0, &i1); + if (success < 2) + return; + + if (i0 < data->best_version[0]) + return; + else if (i0 == data->best_version[0]) { + if (i1 < data->best_version[1]) + return; + } + + // we have to copy_string and free here because visit_files free's the + // full_name string after we execute this function, so Win*_Data would contain + // an invalid pointer. + if (data->best_name) + free(data->best_name); + data->best_name = _wcsdup(full_name); + + if (data->best_name) { + data->best_version[0] = i0; + data->best_version[1] = i1; + } +} + +void find_windows_kit_root(Find_Result *result) { + // Information about the Windows 10 and Windows 8 development kits + // is stored in the same place in the registry. We open a key + // to that place, first checking preferntially for a Windows 10 kit, + // then, if that's not found, a Windows 8 kit. + + HKEY main_key; + + LSTATUS rc = RegOpenKeyExA( + HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", + 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &main_key); + if (rc != S_OK) + return; + + // Look for a Windows 10 entry. + wchar_t *windows10_root = + find_windows_kit_root_with_key(main_key, L"KitsRoot10"); + + if (windows10_root) { + wchar_t *windows10_lib = concat2(windows10_root, L"Lib"); + free(windows10_root); + + Version_Data data = {0}; + visit_files_w(windows10_lib, &data, win10_best); + free(windows10_lib); + + if (data.best_name) { + result->windows_sdk_version = 10; + result->windows_sdk_root = data.best_name; + RegCloseKey(main_key); + return; + } + } + + // Look for a Windows 8 entry. + wchar_t *windows8_root = + find_windows_kit_root_with_key(main_key, L"KitsRoot81"); + + if (windows8_root) { + wchar_t *windows8_lib = concat2(windows8_root, L"Lib"); + free(windows8_root); + + Version_Data data = {0}; + visit_files_w(windows8_lib, &data, win8_best); + free(windows8_lib); + + if (data.best_name) { + result->windows_sdk_version = 8; + result->windows_sdk_root = data.best_name; + RegCloseKey(main_key); + return; + } + } + + // If we get here, we failed to find anything. + RegCloseKey(main_key); +} + +bool find_visual_studio_2017_by_fighting_through_microsoft_craziness( + Find_Result *result) { + HRESULT rc = CoInitialize(NULL); + // "Subsequent valid calls return false." So ignore false. + // if rc != S_OK return false; + + GUID my_uid = {0x42843719, + 0xDB4C, + 0x46C2, + {0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B}}; + GUID CLSID_SetupConfiguration = { + 0x177F0C4A, + 0x1CD3, + 0x4DE7, + {0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D}}; + + ISetupConfiguration *config = NULL; + + // NOTE(Kalinovcic): This is so stupid... These functions take references, so + // the code is different for C and C++...... +#ifdef __cplusplus + HRESULT hr = CoCreateInstance(CLSID_SetupConfiguration, NULL, + CLSCTX_INPROC_SERVER, my_uid, (void **)&config); +#else + HRESULT hr = + CoCreateInstance(&CLSID_SetupConfiguration, NULL, CLSCTX_INPROC_SERVER, + &my_uid, (void **)&config); +#endif + + if (hr != 0) + return false; + + IEnumSetupInstances *instances = NULL; + hr = CALL_STDMETHOD(config, EnumInstances, &instances); + CALL_STDMETHOD_(config, Release); + if (hr != 0) + return false; + if (!instances) + return false; + + bool found_visual_studio_2017 = false; + while (1) { + ULONG found = 0; + ISetupInstance *instance = NULL; + HRESULT hr = CALL_STDMETHOD(instances, Next, 1, &instance, &found); + if (hr != S_OK) + break; + + BSTR bstr_inst_path; + hr = CALL_STDMETHOD(instance, GetInstallationPath, &bstr_inst_path); + CALL_STDMETHOD_(instance, Release); + if (hr != S_OK) + continue; + + wchar_t *tools_filename = concat2( + bstr_inst_path, + L"\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"); + SysFreeString(bstr_inst_path); + + FILE *f; + errno_t open_result = _wfopen_s(&f, tools_filename, L"rt"); + free(tools_filename); + if (open_result != 0) + continue; + if (!f) + continue; + + LARGE_INTEGER tools_file_size; + HANDLE file_handle = (HANDLE)_get_osfhandle(_fileno(f)); + BOOL success = GetFileSizeEx(file_handle, &tools_file_size); + if (!success) { + fclose(f); + continue; + } + + uint64_t version_bytes = + (tools_file_size.QuadPart + 1) * + 2; // Warning: This multiplication by 2 presumes there is no + // variable-length encoding in the wchars (wacky characters in the + // file could betray this expectation). + wchar_t *version = (wchar_t *)malloc(version_bytes); + + wchar_t *read_result = fgetws(version, version_bytes, f); + fclose(f); + if (!read_result) + continue; + + wchar_t *version_tail = wcschr(version, '\n'); + if (version_tail) + *version_tail = 0; // Stomp the data, because nobody cares about it. + + wchar_t *library_path = + concat4(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\lib\\x64"); + wchar_t *library_file = + concat2(library_path, + L"\\vcruntime.lib"); // @Speed: Could have library_path point to + // this string, with a smaller count, to + // save on memory flailing! + + if (os_file_exists(library_file)) { + wchar_t *link_exe_path = concat4(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", + version, L"\\bin\\Hostx64\\x64"); + free(version); + + result->vs_exe_path = link_exe_path; + result->vs_library_path = library_path; + found_visual_studio_2017 = true; + break; + } + + free(version); + + /* + Ryan Saunderson said: + "Clang uses the 'SetupInstance->GetInstallationVersion' / + ISetupHelper->ParseVersion to find the newest version and then reads the + tools file to define the tools path - which is definitely better than + what i did." + + So... @Incomplete: Should probably pick the newest version... + */ + } + + CALL_STDMETHOD_(instances, Release); + return found_visual_studio_2017; +} + +void find_visual_studio_by_fighting_through_microsoft_craziness( + Find_Result *result) { + // The name of this procedure is kind of cryptic. Its purpose is + // to fight through Microsoft craziness. The things that the fine + // Visual Studio team want you to do, JUST TO FIND A SINGLE FOLDER + // THAT EVERYONE NEEDS TO FIND, are ridiculous garbage. + + // For earlier versions of Visual Studio, you'd find this information in the + // registry, similarly to the Windows Kits above. But no, now it's the future, + // so to ask the question "Where is the Visual Studio folder?" you have to do + // a bunch of COM object instantiation, enumeration, and querying. (For extra + // bonus points, try doing this in a new, underdeveloped programming language + // where you don't have COM routines up and running yet. So fun.) + // + // If all this COM object instantiation, enumeration, and querying doesn't + // give us a useful result, we drop back to the registry-checking method. + + bool found_visual_studio_2017 = + find_visual_studio_2017_by_fighting_through_microsoft_craziness(result); + if (found_visual_studio_2017) + return; + + // If we get here, we didn't find Visual Studio 2017. Try earlier versions. + + HKEY vs7_key; + HRESULT rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, + KEY_QUERY_VALUE | KEY_WOW64_32KEY, &vs7_key); + if (rc != S_OK) + return; + + // Hardcoded search for 4 prior Visual Studio versions. Is there something + // better to do here? + wchar_t *versions[] = {L"14.0", L"12.0", L"11.0", L"10.0"}; + const int NUM_VERSIONS = sizeof(versions) / sizeof(versions[0]); + + for (int i = 0; i < NUM_VERSIONS; i++) { + wchar_t *v = versions[i]; + + DWORD dw_type; + DWORD cb_data; + + LSTATUS rc = RegQueryValueExW(vs7_key, v, NULL, &dw_type, NULL, &cb_data); + if ((rc == ERROR_FILE_NOT_FOUND) || (dw_type != REG_SZ)) { + continue; + } + + wchar_t *buffer = (wchar_t *)malloc(cb_data); + if (!buffer) + return; + + rc = RegQueryValueExW(vs7_key, v, NULL, NULL, (LPBYTE)buffer, &cb_data); + if (rc != 0) + continue; + + // @Robustness: Do the zero-termination thing suggested in the RegQueryValue + // docs? + + wchar_t *lib_path = concat2(buffer, L"VC\\Lib\\amd64"); + + // Check to see whether a vcruntime.lib actually exists here. + wchar_t *vcruntime_filename = concat2(lib_path, L"\\vcruntime.lib"); + bool vcruntime_exists = os_file_exists(vcruntime_filename); + free(vcruntime_filename); + + if (vcruntime_exists) { + result->vs_exe_path = concat2(buffer, L"VC\\bin\\amd64"); + result->vs_library_path = lib_path; + + free(buffer); + RegCloseKey(vs7_key); + return; + } + + free(lib_path); + free(buffer); + } + + RegCloseKey(vs7_key); + + // If we get here, we failed to find anything. +} + +Find_Result *find_visual_studio_and_windows_sdk() { + Find_Result *result = malloc(sizeof(Find_Result)); + + find_windows_kit_root(result); + + if (result->windows_sdk_root) { + result->windows_sdk_um_library_path = + concat2(result->windows_sdk_root, L"\\um\\x64"); + result->windows_sdk_ucrt_library_path = + concat2(result->windows_sdk_root, L"\\ucrt\\x64"); + } + + find_visual_studio_by_fighting_through_microsoft_craziness(result); + + return result; +} + +#endif + +#endif // MICROSOFT_CRAZINESS_IMPLEMENTATION_GUARD +#endif // MICROSOFT_CRAZINESS_IMPLEMENTATION + +#ifdef __cplusplus +} +#endif diff --git a/vlib/os/os.v b/vlib/os/os.v index f4ee0a38df..1f94314f83 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -6,8 +6,8 @@ module os #include #include -#include #include + //#include // for backtrace_symbols_fd /* @@ -340,6 +340,11 @@ pub fn dir_exists(path string) bool { pub fn mkdir(path string) { $if windows { path = path.replace('/', '\\') + // Windows doesnt recursively create the folders + // so we need to help it out here + if path.last_index('\\') != -1 { + mkdir(path.all_before_last('\\')) + } C.CreateDirectory(path.str, 0) } $else { @@ -480,7 +485,10 @@ pub fn user_os() string { } $if dragonfly { return 'dragonfly' - } + } + // $if msvc { + // return 'windows' + // } return 'unknown' } diff --git a/vlib/os/os_nix.v b/vlib/os/os_nix.v index 0a5e556ecc..56c82ea511 100644 --- a/vlib/os/os_nix.v +++ b/vlib/os/os_nix.v @@ -1,6 +1,7 @@ module os #include +#include const ( PathSeparator = '/' diff --git a/vlib/time/time.v b/vlib/time/time.v index b82902e2dd..1107b14180 100644 --- a/vlib/time/time.v +++ b/vlib/time/time.v @@ -289,9 +289,12 @@ pub fn ticks() i64 { $if windows { return C.GetTickCount() } - ts := C.timeval{} - C.gettimeofday(&ts,0) - return ts.tv_sec * 1000 + (ts.tv_usec / 1000) + $else { + ts := C.timeval{} + C.gettimeofday(&ts,0) + return ts.tv_sec * 1000 + (ts.tv_usec / 1000) + } + /* t := i64(C.mach_absolute_time()) # Nanoseconds elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime *) &t );