vorelang programming language
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

462 lines
10 KiB

  1. module main
  2. import os
  3. #flag windows -l shell32
  4. struct MsvcResult {
  5. exe_path string
  6. um_lib_path string
  7. ucrt_lib_path string
  8. vs_lib_path string
  9. um_include_path string
  10. ucrt_include_path string
  11. vs_include_path string
  12. shared_include_path string
  13. }
  14. // Mimics a HKEY
  15. type RegKey voidptr
  16. // Taken from the windows SDK
  17. const (
  18. HKEY_LOCAL_MACHINE = RegKey(0x80000002)
  19. KEY_QUERY_VALUE = (0x0001)
  20. KEY_WOW64_32KEY = (0x0200)
  21. KEY_ENUMERATE_SUB_KEYS = (0x0008)
  22. )
  23. // Given a root key look for one of the subkeys in 'versions' and get the path
  24. fn find_windows_kit_internal(key RegKey, versions []string) ?string {
  25. $if windows {
  26. for version in versions {
  27. required_bytes := 0 // TODO mut
  28. result := C.RegQueryValueExW(key, version.to_wide(), 0, 0, 0, &required_bytes)
  29. length := required_bytes / 2
  30. if result != 0 {
  31. continue
  32. }
  33. alloc_length := (required_bytes + 2)
  34. mut value := &u16(malloc(alloc_length))
  35. if !value {
  36. continue
  37. }
  38. result2 := C.RegQueryValueExW(key, version.to_wide(), 0, 0, value, &alloc_length)
  39. if result2 != 0 {
  40. continue
  41. }
  42. // We might need to manually null terminate this thing
  43. // So just make sure that we do that
  44. if (value[length - 1] != u16(0)) {
  45. value[length] = u16(0)
  46. }
  47. return string_from_wide(value)
  48. }
  49. }
  50. return error('windows kit not found')
  51. }
  52. struct WindowsKit {
  53. um_lib_path string
  54. ucrt_lib_path string
  55. um_include_path string
  56. ucrt_include_path string
  57. shared_include_path string
  58. }
  59. // Try and find the root key for installed windows kits
  60. fn find_windows_kit_root() ?WindowsKit {
  61. $if windows {
  62. root_key := RegKey(0)
  63. rc := C.RegOpenKeyExA(
  64. HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots', 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &root_key)
  65. defer {C.RegCloseKey(root_key)}
  66. if rc != 0 {
  67. return error('Unable to open root key')
  68. }
  69. // Try and find win10 kit
  70. kit_root := find_windows_kit_internal(root_key, ['KitsRoot10', 'KitsRoot81']) or {
  71. return error('Unable to find a windows kit')
  72. }
  73. kit_lib := kit_root + 'Lib'
  74. // println(kit_lib)
  75. files := os.ls(kit_lib)
  76. mut highest_path := ''
  77. mut highest_int := 0
  78. for f in files {
  79. no_dot := f.replace('.', '')
  80. v_int := no_dot.int()
  81. if v_int > highest_int {
  82. highest_int = v_int
  83. highest_path = f
  84. }
  85. }
  86. kit_lib_highest := kit_lib + '\\$highest_path'
  87. kit_include_highest := kit_lib_highest.replace('Lib', 'Include')
  88. // println('$kit_lib_highest $kit_include_highest')
  89. return WindowsKit {
  90. um_lib_path: kit_lib_highest + '\\um\\x64'
  91. ucrt_lib_path: kit_lib_highest + '\\ucrt\\x64'
  92. um_include_path: kit_include_highest + '\\um'
  93. ucrt_include_path: kit_include_highest + '\\ucrt'
  94. shared_include_path: kit_include_highest + '\\shared'
  95. }
  96. }
  97. return error('Host OS does not support funding a windows kit')
  98. }
  99. struct VsInstallation {
  100. include_path string
  101. lib_path string
  102. exe_path string
  103. }
  104. fn find_vs() ?VsInstallation {
  105. $if !windows {
  106. return error('Host OS does not support finding a Vs installation')
  107. }
  108. // Emily:
  109. // VSWhere is guaranteed to be installed at this location now
  110. // If its not there then end user needs to update their visual studio
  111. // installation!
  112. res := os.exec('""%ProgramFiles(x86)%\\Microsoft Visual Studio\\Installer\\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath"') or {
  113. return error(err)
  114. }
  115. // println('res: "$res"')
  116. version := os.read_file('$res.output\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt') or {
  117. println('Unable to find msvc version')
  118. return error('Unable to find vs installation')
  119. }
  120. // println('version: $version')
  121. v := if version.ends_with('\n') {
  122. version.left(version.len - 2)
  123. } else {
  124. version
  125. }
  126. lib_path := '$res.output\\VC\\Tools\\MSVC\\$v\\lib\\x64'
  127. include_path := '$res.output\\VC\\Tools\\MSVC\\$v\\include'
  128. if os.file_exists('$lib_path\\vcruntime.lib') {
  129. p := '$res.output\\VC\\Tools\\MSVC\\$v\\bin\\Hostx64\\x64'
  130. // println('$lib_path $include_path')
  131. return VsInstallation{
  132. exe_path: p
  133. lib_path: lib_path
  134. include_path: include_path
  135. }
  136. }
  137. println('Unable to find vs installation (attempted to use lib path "$lib_path")')
  138. return error('Unable to find vs exe folder')
  139. }
  140. fn find_msvc() ?MsvcResult {
  141. $if windows {
  142. wk := find_windows_kit_root() or {
  143. return error('Unable to find windows sdk')
  144. }
  145. vs := find_vs() or {
  146. return error('Unable to find visual studio')
  147. }
  148. return MsvcResult {
  149. exe_path: vs.exe_path,
  150. um_lib_path: wk.um_lib_path,
  151. ucrt_lib_path: wk.ucrt_lib_path,
  152. vs_lib_path: vs.lib_path,
  153. um_include_path: wk.um_include_path,
  154. ucrt_include_path: wk.ucrt_include_path,
  155. vs_include_path: vs.include_path,
  156. shared_include_path: wk.shared_include_path,
  157. }
  158. }
  159. $else {
  160. panic('Cannot find msvc on this OS')
  161. }
  162. }
  163. struct ParsedFlag {
  164. f string
  165. arg string
  166. }
  167. pub fn (v mut V) cc_msvc() {
  168. r := find_msvc() or {
  169. // TODO: code reuse
  170. if !v.pref.is_debug && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' {
  171. os.rm('.$v.out_name_c')
  172. }
  173. panic('Cannot find MSVC on this OS.')
  174. }
  175. out_name_obj := v.out_name_c + '.obj'
  176. // Default arguments
  177. // volatile:ms enables atomic volatile (gcc _Atomic)
  178. // -w: no warnings
  179. // 2 unicode defines
  180. // /Fo sets the object file name - needed so we can clean up after ourselves properly
  181. mut a := ['-w', '/volatile:ms', '/D_UNICODE', '/DUNICODE', '/Fo$out_name_obj']
  182. if v.pref.is_prod {
  183. a << '/O2'
  184. a << '/MD'
  185. } else {
  186. a << '/Z7'
  187. a << '/MDd'
  188. }
  189. if v.pref.is_so {
  190. if !v.out_name.ends_with('.dll') {
  191. v.out_name = v.out_name + '.dll'
  192. }
  193. // Build dll
  194. a << '/LD'
  195. } else if !v.out_name.ends_with('.exe') {
  196. v.out_name = v.out_name + '.exe'
  197. }
  198. mut libs := ''// builtin.o os.o http.o etc
  199. if v.pref.build_mode == .build {
  200. }
  201. else if v.pref.build_mode == .embed_vlib {
  202. //
  203. }
  204. else if v.pref.build_mode == .default_mode {
  205. libs = '"$ModPath/vlib/builtin.obj"'
  206. if !os.file_exists(libs) {
  207. println('`builtin.obj` not found')
  208. exit(1)
  209. }
  210. for imp in v.table.imports {
  211. if imp == 'webview' {
  212. continue
  213. }
  214. libs += ' "$ModPath/vlib/${imp}.obj"'
  215. }
  216. }
  217. if v.pref.sanitize {
  218. println('Sanitize not supported on msvc.')
  219. }
  220. // The C file we are compiling
  221. //a << '"$TmpPath/$v.out_name_c"'
  222. a << '".$v.out_name_c"'
  223. // Emily:
  224. // Not all of these are needed (but the compiler should discard them if they are not used)
  225. // these are the defaults used by msbuild and visual studio
  226. mut real_libs := [
  227. 'kernel32.lib',
  228. 'user32.lib',
  229. 'gdi32.lib',
  230. 'winspool.lib',
  231. 'comdlg32.lib',
  232. 'advapi32.lib',
  233. 'shell32.lib',
  234. 'ole32.lib',
  235. 'oleaut32.lib',
  236. 'uuid.lib',
  237. 'odbc32.lib',
  238. 'odbccp32.lib',
  239. 'vcruntime.lib',
  240. ]
  241. mut lib_paths := []string{}
  242. mut other_flags := []string{}
  243. // Emily:
  244. // this is a hack to try and support -l -L and object files
  245. // passed on the command line
  246. for f in v.table.flags {
  247. // People like to put multiple flags per line (which really complicates things)
  248. // ...so we need to handle that
  249. mut rest := f
  250. mut flags := []ParsedFlag{}
  251. for {
  252. mut base := rest
  253. fl := if rest.starts_with('-') {
  254. base = rest.right(2).trim_space()
  255. rest.left(2)
  256. } else {
  257. ''
  258. }
  259. // Which ever one of these is lowest we use
  260. // TODO: we really shouldnt support all of these cmon
  261. mut lowest := base.index('-')
  262. for x in [base.index(' '), base.index(',')] {
  263. if (x < lowest && x != -1) || lowest == -1 {
  264. lowest = x
  265. }
  266. }
  267. arg := if lowest != -1 {
  268. rest = base.right(lowest).trim_space().trim(',')
  269. base.left(lowest).trim_space().trim(',')
  270. } else {
  271. rest = ''
  272. base.trim_space()
  273. }
  274. flags << ParsedFlag {
  275. fl, arg
  276. }
  277. if rest.len == 0 {
  278. break
  279. }
  280. }
  281. for flag in flags {
  282. fl := flag.f
  283. arg := flag.arg
  284. // We need to see if the flag contains -l
  285. // -l isnt recognised and these libs will be passed straight to the linker
  286. // by the compiler
  287. if fl == '-l' {
  288. if arg.ends_with('.dll') {
  289. panic('MSVC cannot link against a dll (`#flag -l $arg`)')
  290. }
  291. // MSVC has no method of linking against a .dll
  292. // TODO: we should look for .defs aswell
  293. lib_lib := arg + '.lib'
  294. real_libs << lib_lib
  295. }
  296. else if fl == '-L' {
  297. lib_paths << f.right(2).trim_space()
  298. }
  299. else if arg.ends_with('.o') {
  300. // msvc expects .obj not .o
  301. other_flags << arg + 'bj'
  302. }
  303. else {
  304. other_flags << arg
  305. }
  306. }
  307. }
  308. // Include the base paths
  309. a << '-I "$r.ucrt_include_path" -I "$r.vs_include_path" -I "$r.um_include_path" -I "$r.shared_include_path"'
  310. a << other_flags
  311. // Libs are passed to cl.exe which passes them to the linker
  312. a << real_libs.join(' ')
  313. a << '/link'
  314. a << '/NOLOGO'
  315. a << '/OUT:$v.out_name'
  316. a << '/LIBPATH:"$r.ucrt_lib_path"'
  317. a << '/LIBPATH:"$r.um_lib_path"'
  318. a << '/LIBPATH:"$r.vs_lib_path"'
  319. a << '/INCREMENTAL:NO' // Disable incremental linking
  320. for l in lib_paths {
  321. a << '/LIBPATH:"$l"'
  322. }
  323. if !v.pref.is_prod {
  324. a << '/DEBUG:FULL'
  325. } else {
  326. a << '/DEBUG:NONE'
  327. }
  328. args := a.join(' ')
  329. // println('$args')
  330. // println('$exe_path')
  331. escaped_path := r.exe_path
  332. cmd := '""$escaped_path\\cl.exe" $args"'
  333. // println('$cmd')
  334. _ := os.exec(cmd) or {
  335. println(err)
  336. panic('msvc error')
  337. }
  338. // println(res)
  339. // println('C OUTPUT:')
  340. if !v.pref.is_debug && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' {
  341. os.rm('.$v.out_name_c')
  342. }
  343. // Always remove the object file - it is completely unnecessary
  344. os.rm('$out_name_obj')
  345. }
  346. fn build_thirdparty_obj_file_with_msvc(flag string) {
  347. msvc := find_msvc() or {
  348. println('Could not find visual studio')
  349. return
  350. }
  351. mut obj_path := flag.all_after(' ')
  352. if obj_path.ends_with('.o') {
  353. // msvc expects .obj not .o
  354. obj_path = obj_path + 'bj'
  355. }
  356. if os.file_exists(obj_path) {
  357. return
  358. }
  359. println('$obj_path not found, building it (with msvc)...')
  360. parent := obj_path.all_before_last('/').trim_space()
  361. files := os.ls(parent)
  362. mut cfiles := ''
  363. for file in files {
  364. if file.ends_with('.c') {
  365. cfiles += parent + '/' + file + ' '
  366. }
  367. }
  368. include_string := '-I "$msvc.ucrt_include_path" -I "$msvc.vs_include_path" -I "$msvc.um_include_path" -I "$msvc.shared_include_path"'
  369. println('$cfiles')
  370. res := os.exec('""$msvc.exe_path\\cl.exe" /volatile:ms /Z7 $include_string /c $cfiles /Fo"$obj_path" /D_UNICODE /DUNICODE"') or {
  371. panic(err)
  372. }
  373. println(res.output)
  374. }