mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
builder: remove msvc code from non windows systems
This commit is contained in:
parent
12265a15ed
commit
a069577e9c
@ -6,7 +6,6 @@
|
|||||||
- [ ] Parallel C compilation
|
- [ ] Parallel C compilation
|
||||||
- [ ] `recover()` from panics
|
- [ ] `recover()` from panics
|
||||||
- [ ] vfmt: add missing imports (like goimports)
|
- [ ] vfmt: add missing imports (like goimports)
|
||||||
- [ ] merge v.c and v_win.c
|
|
||||||
- [ ] Recursive structs via optionals: `struct Node { next ?Node }`
|
- [ ] Recursive structs via optionals: `struct Node { next ?Node }`
|
||||||
- [ ] Optional function struct fields
|
- [ ] Optional function struct fields
|
||||||
- [ ] Handle function pointers safely, remove `if function == 0 {`
|
- [ ] Handle function pointers safely, remove `if function == 0 {`
|
||||||
@ -15,4 +14,4 @@
|
|||||||
- [ ] -usecache on by default
|
- [ ] -usecache on by default
|
||||||
- [ ] -skip-unused on by default
|
- [ ] -skip-unused on by default
|
||||||
- [ ] `any` type
|
- [ ] `any` type
|
||||||
- [ ] `copy()` builtin function for easier conversion from `[]Foo` to `[4]Foo`
|
- [ ] `copy()` builtin function (e.g. for easier conversion from `[]Foo` to `[4]Foo`)
|
||||||
|
@ -32,9 +32,11 @@ pub mut:
|
|||||||
pref &pref.Preferences
|
pref &pref.Preferences
|
||||||
module_search_paths []string
|
module_search_paths []string
|
||||||
parsed_files []&ast.File
|
parsed_files []&ast.File
|
||||||
cached_msvc MsvcResult
|
//$if windows {
|
||||||
table &ast.Table
|
cached_msvc MsvcResult
|
||||||
ccoptions CcompilerOptions
|
//}
|
||||||
|
table &ast.Table
|
||||||
|
ccoptions CcompilerOptions
|
||||||
//
|
//
|
||||||
// Note: changes in mod `builtin` force invalidation of every other .v file
|
// Note: changes in mod `builtin` force invalidation of every other .v file
|
||||||
mod_invalidates_paths map[string][]string // changes in mod `os`, invalidate only .v files, that do `import os`
|
mod_invalidates_paths map[string][]string // changes in mod `os`, invalidate only .v files, that do `import os`
|
||||||
@ -56,12 +58,15 @@ pub fn new_builder(pref &pref.Preferences) Builder {
|
|||||||
util.emanager.set_support_color(false)
|
util.emanager.set_support_color(false)
|
||||||
}
|
}
|
||||||
table.pointer_size = if pref.m64 { 8 } else { 4 }
|
table.pointer_size = if pref.m64 { 8 } else { 4 }
|
||||||
msvc := find_msvc(pref.m64) or {
|
mut msvc := MsvcResult{}
|
||||||
if pref.ccompiler == 'msvc' {
|
$if windows {
|
||||||
// verror('Cannot find MSVC on this OS')
|
msvc = find_msvc(pref.m64) or {
|
||||||
}
|
if pref.ccompiler == 'msvc' {
|
||||||
MsvcResult{
|
// verror('Cannot find MSVC on this OS')
|
||||||
valid: false
|
}
|
||||||
|
MsvcResult{
|
||||||
|
valid: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
util.timing_set_should_print(pref.show_timings || pref.is_verbose)
|
util.timing_set_should_print(pref.show_timings || pref.is_verbose)
|
||||||
|
@ -39,39 +39,6 @@ You can also use `v doctor`, to see what V knows about your current environment.
|
|||||||
You can also seek #help on Discord: https://discord.gg/vlang
|
You can also seek #help on Discord: https://discord.gg/vlang
|
||||||
'
|
'
|
||||||
|
|
||||||
pub fn (mut v Builder) find_win_cc() ? {
|
|
||||||
$if !windows {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ccompiler_version_res := os.execute('${os.quoted_path(v.pref.ccompiler)} -v')
|
|
||||||
if ccompiler_version_res.exit_code != 0 {
|
|
||||||
if v.pref.is_verbose {
|
|
||||||
println('$v.pref.ccompiler not found, looking for msvc...')
|
|
||||||
}
|
|
||||||
find_msvc(v.pref.m64) or {
|
|
||||||
if v.pref.is_verbose {
|
|
||||||
println('msvc not found, looking for thirdparty/tcc...')
|
|
||||||
}
|
|
||||||
vpath := os.dir(pref.vexe_path())
|
|
||||||
thirdparty_tcc := os.join_path(vpath, 'thirdparty', 'tcc', 'tcc.exe')
|
|
||||||
tcc_version_res := os.execute('${os.quoted_path(thirdparty_tcc)} -v')
|
|
||||||
if tcc_version_res.exit_code != 0 {
|
|
||||||
if v.pref.is_verbose {
|
|
||||||
println('tcc not found')
|
|
||||||
}
|
|
||||||
return error('tcc not found')
|
|
||||||
}
|
|
||||||
v.pref.ccompiler = thirdparty_tcc
|
|
||||||
v.pref.ccompiler_type = .tinyc
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v.pref.ccompiler = 'msvc'
|
|
||||||
v.pref.ccompiler_type = .msvc
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v.pref.ccompiler_type = pref.cc_from_string(v.pref.ccompiler)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut v Builder) show_c_compiler_output(res os.Result) {
|
fn (mut v Builder) show_c_compiler_output(res os.Result) {
|
||||||
println('======== C Compiler output ========')
|
println('======== C Compiler output ========')
|
||||||
println(res.output)
|
println(res.output)
|
||||||
@ -593,9 +560,11 @@ pub fn (mut v Builder) cc() {
|
|||||||
v.ccoptions.pre_args << '-c'
|
v.ccoptions.pre_args << '-c'
|
||||||
}
|
}
|
||||||
v.handle_usecache(vexe)
|
v.handle_usecache(vexe)
|
||||||
if ccompiler == 'msvc' {
|
$if windows {
|
||||||
v.cc_msvc()
|
if ccompiler == 'msvc' {
|
||||||
return
|
v.cc_msvc()
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
all_args := v.all_args(v.ccoptions)
|
all_args := v.all_args(v.ccoptions)
|
||||||
@ -918,11 +887,13 @@ fn (mut b Builder) build_thirdparty_obj_files() {
|
|||||||
for flag in b.get_os_cflags() {
|
for flag in b.get_os_cflags() {
|
||||||
if flag.value.ends_with('.o') {
|
if flag.value.ends_with('.o') {
|
||||||
rest_of_module_flags := b.get_rest_of_module_cflags(flag)
|
rest_of_module_flags := b.get_rest_of_module_cflags(flag)
|
||||||
if b.pref.ccompiler == 'msvc' {
|
$if windows {
|
||||||
b.build_thirdparty_obj_file_with_msvc(flag.value, rest_of_module_flags)
|
if b.pref.ccompiler == 'msvc' {
|
||||||
} else {
|
b.build_thirdparty_obj_file_with_msvc(flag.value, rest_of_module_flags)
|
||||||
b.build_thirdparty_obj_file(flag.value, rest_of_module_flags)
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
b.build_thirdparty_obj_file(flag.value, rest_of_module_flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
40
vlib/v/builder/cc_windows.v
Normal file
40
vlib/v/builder/cc_windows.v
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license
|
||||||
|
// that can be found in the LICENSE file.
|
||||||
|
module builder
|
||||||
|
|
||||||
|
import os
|
||||||
|
import v.pref
|
||||||
|
|
||||||
|
pub fn (mut v Builder) find_win_cc() ? {
|
||||||
|
$if !windows {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ccompiler_version_res := os.execute('${os.quoted_path(v.pref.ccompiler)} -v')
|
||||||
|
if ccompiler_version_res.exit_code != 0 {
|
||||||
|
if v.pref.is_verbose {
|
||||||
|
println('$v.pref.ccompiler not found, looking for msvc...')
|
||||||
|
}
|
||||||
|
find_msvc(v.pref.m64) or {
|
||||||
|
if v.pref.is_verbose {
|
||||||
|
println('msvc not found, looking for thirdparty/tcc...')
|
||||||
|
}
|
||||||
|
vpath := os.dir(pref.vexe_path())
|
||||||
|
thirdparty_tcc := os.join_path(vpath, 'thirdparty', 'tcc', 'tcc.exe')
|
||||||
|
tcc_version_res := os.execute('${os.quoted_path(thirdparty_tcc)} -v')
|
||||||
|
if tcc_version_res.exit_code != 0 {
|
||||||
|
if v.pref.is_verbose {
|
||||||
|
println('tcc not found')
|
||||||
|
}
|
||||||
|
return error('tcc not found')
|
||||||
|
}
|
||||||
|
v.pref.ccompiler = thirdparty_tcc
|
||||||
|
v.pref.ccompiler_type = .tinyc
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.pref.ccompiler = 'msvc'
|
||||||
|
v.pref.ccompiler_type = .msvc
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.pref.ccompiler_type = pref.cc_from_string(v.pref.ccompiler)
|
||||||
|
}
|
@ -1,14 +1,6 @@
|
|||||||
module builder
|
module builder
|
||||||
|
|
||||||
import os
|
// TODO move this to msvc_default.v
|
||||||
import v.pref
|
|
||||||
import v.util
|
|
||||||
import v.cflag
|
|
||||||
|
|
||||||
#flag windows -l shell32
|
|
||||||
#flag windows -l dbghelp
|
|
||||||
#flag windows -l advapi32
|
|
||||||
|
|
||||||
struct MsvcResult {
|
struct MsvcResult {
|
||||||
full_cl_exe_path string
|
full_cl_exe_path string
|
||||||
exe_path string
|
exe_path string
|
||||||
@ -21,536 +13,3 @@ struct MsvcResult {
|
|||||||
shared_include_path string
|
shared_include_path string
|
||||||
valid bool
|
valid bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// shell32 for RegOpenKeyExW etc
|
|
||||||
// Mimics a HKEY
|
|
||||||
type RegKey = voidptr
|
|
||||||
|
|
||||||
// Taken from the windows SDK
|
|
||||||
const (
|
|
||||||
hkey_local_machine = RegKey(0x80000002)
|
|
||||||
key_query_value = (0x0001)
|
|
||||||
key_wow64_32key = (0x0200)
|
|
||||||
key_enumerate_sub_keys = (0x0008)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Given a root key look for one of the subkeys in 'versions' and get the path
|
|
||||||
fn find_windows_kit_internal(key RegKey, versions []string) ?string {
|
|
||||||
$if windows {
|
|
||||||
unsafe {
|
|
||||||
for version in versions {
|
|
||||||
required_bytes := u32(0) // TODO mut
|
|
||||||
result := C.RegQueryValueEx(key, version.to_wide(), 0, 0, 0, &required_bytes)
|
|
||||||
length := required_bytes / 2
|
|
||||||
if result != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
alloc_length := (required_bytes + 2)
|
|
||||||
mut value := &u16(malloc_noscan(int(alloc_length)))
|
|
||||||
if isnil(value) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
//
|
|
||||||
else {
|
|
||||||
}
|
|
||||||
result2 := C.RegQueryValueEx(key, version.to_wide(), 0, 0, value, &alloc_length)
|
|
||||||
if result2 != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// We might need to manually null terminate this thing
|
|
||||||
// So just make sure that we do that
|
|
||||||
if value[length - 1] != u16(0) {
|
|
||||||
value[length] = u16(0)
|
|
||||||
}
|
|
||||||
res := string_from_wide(value)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return error('windows kit not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WindowsKit {
|
|
||||||
um_lib_path string
|
|
||||||
ucrt_lib_path string
|
|
||||||
um_include_path string
|
|
||||||
ucrt_include_path string
|
|
||||||
shared_include_path string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try and find the root key for installed windows kits
|
|
||||||
fn find_windows_kit_root(target_arch string) ?WindowsKit {
|
|
||||||
$if windows {
|
|
||||||
wkroot := find_windows_kit_root_by_reg(target_arch) or {
|
|
||||||
if wkroot := find_windows_kit_root_by_env(target_arch) {
|
|
||||||
return wkroot
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return wkroot
|
|
||||||
} $else {
|
|
||||||
return error('Host OS does not support finding a windows kit')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to find the root key for installed windows kits from registry
|
|
||||||
fn find_windows_kit_root_by_reg(target_arch string) ?WindowsKit {
|
|
||||||
$if windows {
|
|
||||||
root_key := RegKey(0)
|
|
||||||
path := 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots'
|
|
||||||
rc := C.RegOpenKeyEx(builder.hkey_local_machine, path.to_wide(), 0, builder.key_query_value | builder.key_wow64_32key | builder.key_enumerate_sub_keys,
|
|
||||||
&root_key)
|
|
||||||
|
|
||||||
if rc != 0 {
|
|
||||||
return error('Unable to open root key')
|
|
||||||
}
|
|
||||||
// Try and find win10 kit
|
|
||||||
kit_root := find_windows_kit_internal(root_key, ['KitsRoot10', 'KitsRoot81']) or {
|
|
||||||
C.RegCloseKey(root_key)
|
|
||||||
return error('Unable to find a windows kit')
|
|
||||||
}
|
|
||||||
C.RegCloseKey(root_key)
|
|
||||||
return new_windows_kit(kit_root, target_arch)
|
|
||||||
} $else {
|
|
||||||
return error('Host OS does not support finding a windows kit')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_windows_kit(kit_root string, target_arch string) ?WindowsKit {
|
|
||||||
kit_lib := kit_root + 'Lib'
|
|
||||||
files := os.ls(kit_lib)?
|
|
||||||
mut highest_path := ''
|
|
||||||
mut highest_int := 0
|
|
||||||
for f in files {
|
|
||||||
no_dot := f.replace('.', '')
|
|
||||||
v_int := no_dot.int()
|
|
||||||
if v_int > highest_int {
|
|
||||||
highest_int = v_int
|
|
||||||
highest_path = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kit_lib_highest := kit_lib + '\\$highest_path'
|
|
||||||
kit_include_highest := kit_lib_highest.replace('Lib', 'Include')
|
|
||||||
return WindowsKit{
|
|
||||||
um_lib_path: kit_lib_highest + '\\um\\$target_arch'
|
|
||||||
ucrt_lib_path: kit_lib_highest + '\\ucrt\\$target_arch'
|
|
||||||
um_include_path: kit_include_highest + '\\um'
|
|
||||||
ucrt_include_path: kit_include_highest + '\\ucrt'
|
|
||||||
shared_include_path: kit_include_highest + '\\shared'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_windows_kit_root_by_env(target_arch string) ?WindowsKit {
|
|
||||||
kit_root := os.getenv('WindowsSdkDir')
|
|
||||||
if kit_root == '' {
|
|
||||||
return error('empty WindowsSdkDir')
|
|
||||||
}
|
|
||||||
return new_windows_kit(kit_root, target_arch)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VsInstallation {
|
|
||||||
include_path string
|
|
||||||
lib_path string
|
|
||||||
exe_path string
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_vs(vswhere_dir string, host_arch string, target_arch string) ?VsInstallation {
|
|
||||||
$if windows {
|
|
||||||
vsinst := find_vs_by_reg(vswhere_dir, host_arch, target_arch) or {
|
|
||||||
if vsinst := find_vs_by_env(host_arch, target_arch) {
|
|
||||||
return vsinst
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return vsinst
|
|
||||||
} $else {
|
|
||||||
return error('Host OS does not support finding a Visual Studio installation')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_vs_by_reg(vswhere_dir string, host_arch string, target_arch string) ?VsInstallation {
|
|
||||||
$if windows {
|
|
||||||
// Emily:
|
|
||||||
// VSWhere is guaranteed to be installed at this location now
|
|
||||||
// If its not there then end user needs to update their visual studio
|
|
||||||
// installation!
|
|
||||||
res := os.execute('"$vswhere_dir\\Microsoft Visual Studio\\Installer\\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath')
|
|
||||||
// println('res: "$res"')
|
|
||||||
if res.exit_code != 0 {
|
|
||||||
return error_with_code(res.output, res.exit_code)
|
|
||||||
}
|
|
||||||
res_output := res.output.trim_space()
|
|
||||||
version := os.read_file('$res_output\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt') or {
|
|
||||||
// println('Unable to find msvc version')
|
|
||||||
return error('Unable to find vs installation')
|
|
||||||
}
|
|
||||||
// println('version: $version')
|
|
||||||
v := version.trim_space()
|
|
||||||
lib_path := '$res_output\\VC\\Tools\\MSVC\\$v\\lib\\$target_arch'
|
|
||||||
include_path := '$res_output\\VC\\Tools\\MSVC\\$v\\include'
|
|
||||||
if os.exists('$lib_path\\vcruntime.lib') {
|
|
||||||
p := '$res_output\\VC\\Tools\\MSVC\\$v\\bin\\Host$host_arch\\$target_arch'
|
|
||||||
// println('$lib_path $include_path')
|
|
||||||
return VsInstallation{
|
|
||||||
exe_path: p
|
|
||||||
lib_path: lib_path
|
|
||||||
include_path: include_path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println('Unable to find vs installation (attempted to use lib path "$lib_path")')
|
|
||||||
return error('Unable to find vs exe folder')
|
|
||||||
} $else {
|
|
||||||
return error('Host OS does not support finding a Visual Studio installation')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_vs_by_env(host_arch string, target_arch string) ?VsInstallation {
|
|
||||||
vs_dir := os.getenv('VSINSTALLDIR')
|
|
||||||
if vs_dir == '' {
|
|
||||||
return error('empty VSINSTALLDIR')
|
|
||||||
}
|
|
||||||
|
|
||||||
vc_tools_dir := os.getenv('VCToolsInstallDir')
|
|
||||||
if vc_tools_dir == '' {
|
|
||||||
return error('empty VCToolsInstallDir')
|
|
||||||
}
|
|
||||||
|
|
||||||
bin_dir := '${vc_tools_dir}bin\\Host$host_arch\\$target_arch'
|
|
||||||
lib_path := '${vc_tools_dir}lib\\$target_arch'
|
|
||||||
include_path := '${vc_tools_dir}include'
|
|
||||||
|
|
||||||
return VsInstallation{
|
|
||||||
exe_path: bin_dir
|
|
||||||
lib_path: lib_path
|
|
||||||
include_path: include_path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_msvc(m64_target bool) ?MsvcResult {
|
|
||||||
$if windows {
|
|
||||||
processor_architecture := os.getenv('PROCESSOR_ARCHITECTURE')
|
|
||||||
vswhere_dir := if processor_architecture == 'x86' {
|
|
||||||
'%ProgramFiles%'
|
|
||||||
} else {
|
|
||||||
'%ProgramFiles(x86)%'
|
|
||||||
}
|
|
||||||
host_arch := if processor_architecture == 'x86' { 'X86' } else { 'X64' }
|
|
||||||
target_arch := if !m64_target { 'X86' } else { 'X64' }
|
|
||||||
wk := find_windows_kit_root(target_arch) or { return error('Unable to find windows sdk') }
|
|
||||||
vs := find_vs(vswhere_dir, host_arch, target_arch) or {
|
|
||||||
return error('Unable to find visual studio')
|
|
||||||
}
|
|
||||||
return MsvcResult{
|
|
||||||
full_cl_exe_path: os.real_path(vs.exe_path + os.path_separator + 'cl.exe')
|
|
||||||
exe_path: vs.exe_path
|
|
||||||
um_lib_path: wk.um_lib_path
|
|
||||||
ucrt_lib_path: wk.ucrt_lib_path
|
|
||||||
vs_lib_path: vs.lib_path
|
|
||||||
um_include_path: wk.um_include_path
|
|
||||||
ucrt_include_path: wk.ucrt_include_path
|
|
||||||
vs_include_path: vs.include_path
|
|
||||||
shared_include_path: wk.shared_include_path
|
|
||||||
valid: true
|
|
||||||
}
|
|
||||||
} $else {
|
|
||||||
// This hack allows to at least see the generated .c file with `-os windows -cc msvc -o x.c`
|
|
||||||
// Please do not remove it, unless you also check that the above continues to work.
|
|
||||||
return MsvcResult{
|
|
||||||
full_cl_exe_path: '/usr/bin/true'
|
|
||||||
valid: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut v Builder) cc_msvc() {
|
|
||||||
r := v.cached_msvc
|
|
||||||
if r.valid == false {
|
|
||||||
verror('Cannot find MSVC on this OS')
|
|
||||||
}
|
|
||||||
out_name_obj := os.real_path(v.out_name_c + '.obj')
|
|
||||||
out_name_pdb := os.real_path(v.out_name_c + '.pdb')
|
|
||||||
out_name_cmd_line := os.real_path(v.out_name_c + '.rsp')
|
|
||||||
mut a := []string{}
|
|
||||||
//
|
|
||||||
env_cflags := os.getenv('CFLAGS')
|
|
||||||
mut all_cflags := '$env_cflags $v.pref.cflags'
|
|
||||||
if all_cflags != ' ' {
|
|
||||||
a << all_cflags
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Default arguments
|
|
||||||
// `-w` no warnings
|
|
||||||
// `/we4013` 2 unicode defines, see https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4013?redirectedfrom=MSDN&view=msvc-170
|
|
||||||
// `/volatile:ms` enables atomic volatile (gcc _Atomic)
|
|
||||||
// `/Fo` sets the object file name - needed so we can clean up after ourselves properly
|
|
||||||
// `/F 16777216` changes the stack size to 16MB, see https://docs.microsoft.com/en-us/cpp/build/reference/f-set-stack-size?view=msvc-170
|
|
||||||
a << ['-w', '/we4013', '/volatile:ms', '/Fo"$out_name_obj"', '/F 16777216']
|
|
||||||
if v.pref.is_prod {
|
|
||||||
a << '/O2'
|
|
||||||
}
|
|
||||||
if v.pref.is_debug {
|
|
||||||
a << '/MDd'
|
|
||||||
a << '/D_DEBUG'
|
|
||||||
// /Zi generates a .pdb
|
|
||||||
// /Fd sets the pdb file name (so its not just vc140 all the time)
|
|
||||||
a << ['/Zi', '/Fd"$out_name_pdb"']
|
|
||||||
} else {
|
|
||||||
a << '/MD'
|
|
||||||
a << '/DNDEBUG'
|
|
||||||
}
|
|
||||||
if v.pref.is_shared {
|
|
||||||
if !v.pref.out_name.ends_with('.dll') {
|
|
||||||
v.pref.out_name += '.dll'
|
|
||||||
}
|
|
||||||
// Build dll
|
|
||||||
a << '/LD'
|
|
||||||
} else if !v.pref.out_name.ends_with('.exe') {
|
|
||||||
v.pref.out_name += '.exe'
|
|
||||||
}
|
|
||||||
v.pref.out_name = os.real_path(v.pref.out_name)
|
|
||||||
// alibs := []string{} // builtin.o os.o http.o etc
|
|
||||||
if v.pref.build_mode == .build_module {
|
|
||||||
// Compile only
|
|
||||||
a << '/c'
|
|
||||||
} else if v.pref.build_mode == .default_mode {
|
|
||||||
/*
|
|
||||||
b := os.real_path( '${pref.default_module_path}/vlib/builtin.obj' )
|
|
||||||
alibs << '"$b"'
|
|
||||||
if !os.exists(b) {
|
|
||||||
println('`builtin.obj` not found')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
for imp in v.ast.imports {
|
|
||||||
if imp == 'webview' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
alibs << '"' + os.real_path( '${pref.default_module_path}/vlib/${imp}.obj' ) + '"'
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
if v.pref.sanitize {
|
|
||||||
eprintln('Sanitize not supported on msvc.')
|
|
||||||
}
|
|
||||||
// The C file we are compiling
|
|
||||||
// a << '"$TmpPath/$v.out_name_c"'
|
|
||||||
a << '"' + os.real_path(v.out_name_c) + '"'
|
|
||||||
// Emily:
|
|
||||||
// Not all of these are needed (but the compiler should discard them if they are not used)
|
|
||||||
// these are the defaults used by msbuild and visual studio
|
|
||||||
mut real_libs := ['kernel32.lib', 'user32.lib', 'advapi32.lib']
|
|
||||||
sflags := msvc_string_flags(v.get_os_cflags())
|
|
||||||
real_libs << sflags.real_libs
|
|
||||||
inc_paths := sflags.inc_paths
|
|
||||||
lib_paths := sflags.lib_paths
|
|
||||||
defines := sflags.defines
|
|
||||||
other_flags := sflags.other_flags
|
|
||||||
// Include the base paths
|
|
||||||
a << r.include_paths()
|
|
||||||
a << defines
|
|
||||||
a << inc_paths
|
|
||||||
a << other_flags
|
|
||||||
// Libs are passed to cl.exe which passes them to the linker
|
|
||||||
a << real_libs.join(' ')
|
|
||||||
a << '/link'
|
|
||||||
a << '/NOLOGO'
|
|
||||||
a << '/OUT:"$v.pref.out_name"'
|
|
||||||
a << r.library_paths()
|
|
||||||
if !all_cflags.contains('/DEBUG') {
|
|
||||||
// only use /DEBUG, if the user *did not* provide its own:
|
|
||||||
a << '/DEBUG:FULL' // required for prod builds to generate a PDB file
|
|
||||||
}
|
|
||||||
if v.pref.is_prod {
|
|
||||||
a << '/INCREMENTAL:NO' // Disable incremental linking
|
|
||||||
a << '/OPT:REF'
|
|
||||||
a << '/OPT:ICF'
|
|
||||||
}
|
|
||||||
a << lib_paths
|
|
||||||
env_ldflags := os.getenv('LDFLAGS')
|
|
||||||
if env_ldflags != '' {
|
|
||||||
a << env_ldflags
|
|
||||||
}
|
|
||||||
v.dump_c_options(a)
|
|
||||||
args := a.join(' ')
|
|
||||||
// write args to a file so that we dont smash createprocess
|
|
||||||
os.write_file(out_name_cmd_line, args) or {
|
|
||||||
verror('Unable to write response file to "$out_name_cmd_line"')
|
|
||||||
}
|
|
||||||
cmd := '"$r.full_cl_exe_path" "@$out_name_cmd_line"'
|
|
||||||
// It is hard to see it at first, but the quotes above ARE balanced :-| ...
|
|
||||||
// Also the double quotes at the start ARE needed.
|
|
||||||
v.show_cc(cmd, out_name_cmd_line, args)
|
|
||||||
if os.user_os() != 'windows' && !v.pref.out_name.ends_with('.c') {
|
|
||||||
verror('Cannot build with msvc on $os.user_os()')
|
|
||||||
}
|
|
||||||
util.timing_start('C msvc')
|
|
||||||
res := os.execute(cmd)
|
|
||||||
if res.exit_code != 0 {
|
|
||||||
eprintln(res.output)
|
|
||||||
verror('msvc error')
|
|
||||||
}
|
|
||||||
util.timing_measure('C msvc')
|
|
||||||
if v.pref.show_c_output {
|
|
||||||
v.show_c_compiler_output(res)
|
|
||||||
} else {
|
|
||||||
v.post_process_c_compiler_output(res)
|
|
||||||
}
|
|
||||||
// println(res)
|
|
||||||
// println('C OUTPUT:')
|
|
||||||
// Always remove the object file - it is completely unnecessary
|
|
||||||
os.rm(out_name_obj) or {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut v Builder) build_thirdparty_obj_file_with_msvc(path string, moduleflags []cflag.CFlag) {
|
|
||||||
msvc := v.cached_msvc
|
|
||||||
if msvc.valid == false {
|
|
||||||
verror('Cannot find MSVC on this OS')
|
|
||||||
}
|
|
||||||
// msvc expects .obj not .o
|
|
||||||
path_without_o_postfix := path[..path.len - 2] // remove .o
|
|
||||||
mut obj_path := '${path_without_o_postfix}.obj'
|
|
||||||
obj_path = os.real_path(obj_path)
|
|
||||||
if os.exists(obj_path) {
|
|
||||||
// println('$obj_path already built.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
println('$obj_path not found, building it (with msvc)...')
|
|
||||||
cfile := '${path_without_o_postfix}.c'
|
|
||||||
flags := msvc_string_flags(moduleflags)
|
|
||||||
inc_dirs := flags.inc_paths.join(' ')
|
|
||||||
defines := flags.defines.join(' ')
|
|
||||||
//
|
|
||||||
mut oargs := []string{}
|
|
||||||
env_cflags := os.getenv('CFLAGS')
|
|
||||||
mut all_cflags := '$env_cflags $v.pref.cflags'
|
|
||||||
if all_cflags != ' ' {
|
|
||||||
oargs << all_cflags
|
|
||||||
}
|
|
||||||
oargs << '/NOLOGO'
|
|
||||||
oargs << '/volatile:ms'
|
|
||||||
//
|
|
||||||
if v.pref.is_prod {
|
|
||||||
oargs << '/O2'
|
|
||||||
oargs << '/MD'
|
|
||||||
oargs << '/DNDEBUG'
|
|
||||||
} else {
|
|
||||||
oargs << '/MDd'
|
|
||||||
oargs << '/D_DEBUG'
|
|
||||||
}
|
|
||||||
oargs << defines
|
|
||||||
oargs << msvc.include_paths()
|
|
||||||
oargs << inc_dirs
|
|
||||||
oargs << '/c "$cfile"'
|
|
||||||
oargs << '/Fo"$obj_path"'
|
|
||||||
env_ldflags := os.getenv('LDFLAGS')
|
|
||||||
if env_ldflags != '' {
|
|
||||||
oargs << env_ldflags
|
|
||||||
}
|
|
||||||
v.dump_c_options(oargs)
|
|
||||||
str_oargs := oargs.join(' ')
|
|
||||||
cmd := '"$msvc.full_cl_exe_path" $str_oargs'
|
|
||||||
// Note: the quotes above ARE balanced.
|
|
||||||
$if trace_thirdparty_obj_files ? {
|
|
||||||
println('>>> build_thirdparty_obj_file_with_msvc cmd: $cmd')
|
|
||||||
}
|
|
||||||
res := os.execute(cmd)
|
|
||||||
if res.exit_code != 0 {
|
|
||||||
println('msvc: failed to build a thirdparty object; cmd: $cmd')
|
|
||||||
verror(res.output)
|
|
||||||
}
|
|
||||||
println(res.output)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MsvcStringFlags {
|
|
||||||
mut:
|
|
||||||
real_libs []string
|
|
||||||
inc_paths []string
|
|
||||||
lib_paths []string
|
|
||||||
defines []string
|
|
||||||
other_flags []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn (cflags []CFlag) msvc_string_flags() MsvcStringFlags {
|
|
||||||
pub fn msvc_string_flags(cflags []cflag.CFlag) MsvcStringFlags {
|
|
||||||
mut real_libs := []string{}
|
|
||||||
mut inc_paths := []string{}
|
|
||||||
mut lib_paths := []string{}
|
|
||||||
mut defines := []string{}
|
|
||||||
mut other_flags := []string{}
|
|
||||||
for flag in cflags {
|
|
||||||
// println('fl: $flag.name | flag arg: $flag.value')
|
|
||||||
// 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 flag.name == '-l' {
|
|
||||||
if flag.value.ends_with('.dll') {
|
|
||||||
verror('MSVC cannot link against a dll (`#flag -l $flag.value`)')
|
|
||||||
}
|
|
||||||
// MSVC has no method of linking against a .dll
|
|
||||||
// TODO: we should look for .defs aswell
|
|
||||||
lib_lib := flag.value + '.lib'
|
|
||||||
real_libs << lib_lib
|
|
||||||
} else if flag.name == '-I' {
|
|
||||||
inc_paths << flag.format()
|
|
||||||
} else if flag.name == '-D' {
|
|
||||||
defines << '/D$flag.value'
|
|
||||||
} else if flag.name == '-L' {
|
|
||||||
lib_paths << flag.value
|
|
||||||
lib_paths << flag.value + os.path_separator + 'msvc'
|
|
||||||
// The above allows putting msvc specific .lib files in a subfolder msvc/ ,
|
|
||||||
// where gcc will NOT find them, but cl will do...
|
|
||||||
// Note: gcc is smart enough to not need .lib files at all in most cases, the .dll is enough.
|
|
||||||
// When both a msvc .lib file and .dll file are present in the same folder,
|
|
||||||
// as for example for glfw3, compilation with gcc would fail.
|
|
||||||
} else if flag.value.ends_with('.o') {
|
|
||||||
// msvc expects .obj not .o
|
|
||||||
other_flags << '"${flag.value}bj"'
|
|
||||||
} else if flag.value.starts_with('-D') {
|
|
||||||
defines << '/D${flag.value[2..]}'
|
|
||||||
} else {
|
|
||||||
other_flags << flag.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mut lpaths := []string{}
|
|
||||||
for l in lib_paths {
|
|
||||||
lpaths << '/LIBPATH:"${os.real_path(l)}"'
|
|
||||||
}
|
|
||||||
return MsvcStringFlags{
|
|
||||||
real_libs: real_libs
|
|
||||||
inc_paths: inc_paths
|
|
||||||
lib_paths: lpaths
|
|
||||||
defines: defines
|
|
||||||
other_flags: other_flags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (r MsvcResult) include_paths() []string {
|
|
||||||
mut res := []string{cap: 4}
|
|
||||||
if r.ucrt_include_path != '' {
|
|
||||||
res << '-I "$r.ucrt_include_path"'
|
|
||||||
}
|
|
||||||
if r.vs_include_path != '' {
|
|
||||||
res << '-I "$r.vs_include_path"'
|
|
||||||
}
|
|
||||||
if r.um_include_path != '' {
|
|
||||||
res << '-I "$r.um_include_path"'
|
|
||||||
}
|
|
||||||
if r.shared_include_path != '' {
|
|
||||||
res << '-I "$r.shared_include_path"'
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (r MsvcResult) library_paths() []string {
|
|
||||||
mut res := []string{cap: 3}
|
|
||||||
if r.ucrt_lib_path != '' {
|
|
||||||
res << '/LIBPATH:"$r.ucrt_lib_path"'
|
|
||||||
}
|
|
||||||
if r.um_lib_path != '' {
|
|
||||||
res << '/LIBPATH:"$r.um_lib_path"'
|
|
||||||
}
|
|
||||||
if r.vs_lib_path != '' {
|
|
||||||
res << '/LIBPATH:"$r.vs_lib_path"'
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
543
vlib/v/builder/msvc_windows.v
Normal file
543
vlib/v/builder/msvc_windows.v
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
module builder
|
||||||
|
|
||||||
|
import os
|
||||||
|
import v.pref
|
||||||
|
import v.util
|
||||||
|
import v.cflag
|
||||||
|
|
||||||
|
#flag windows -l shell32
|
||||||
|
#flag windows -l dbghelp
|
||||||
|
#flag windows -l advapi32
|
||||||
|
|
||||||
|
// shell32 for RegOpenKeyExW etc
|
||||||
|
// Mimics a HKEY
|
||||||
|
type RegKey = voidptr
|
||||||
|
|
||||||
|
// Taken from the windows SDK
|
||||||
|
const (
|
||||||
|
hkey_local_machine = RegKey(0x80000002)
|
||||||
|
key_query_value = (0x0001)
|
||||||
|
key_wow64_32key = (0x0200)
|
||||||
|
key_enumerate_sub_keys = (0x0008)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Given a root key look for one of the subkeys in 'versions' and get the path
|
||||||
|
fn find_windows_kit_internal(key RegKey, versions []string) ?string {
|
||||||
|
$if windows {
|
||||||
|
unsafe {
|
||||||
|
for version in versions {
|
||||||
|
required_bytes := u32(0) // TODO mut
|
||||||
|
result := C.RegQueryValueEx(key, version.to_wide(), 0, 0, 0, &required_bytes)
|
||||||
|
length := required_bytes / 2
|
||||||
|
if result != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
alloc_length := (required_bytes + 2)
|
||||||
|
mut value := &u16(malloc_noscan(int(alloc_length)))
|
||||||
|
if isnil(value) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
//
|
||||||
|
else {
|
||||||
|
}
|
||||||
|
result2 := C.RegQueryValueEx(key, version.to_wide(), 0, 0, value, &alloc_length)
|
||||||
|
if result2 != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// We might need to manually null terminate this thing
|
||||||
|
// So just make sure that we do that
|
||||||
|
if value[length - 1] != u16(0) {
|
||||||
|
value[length] = u16(0)
|
||||||
|
}
|
||||||
|
res := string_from_wide(value)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error('windows kit not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WindowsKit {
|
||||||
|
um_lib_path string
|
||||||
|
ucrt_lib_path string
|
||||||
|
um_include_path string
|
||||||
|
ucrt_include_path string
|
||||||
|
shared_include_path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try and find the root key for installed windows kits
|
||||||
|
fn find_windows_kit_root(target_arch string) ?WindowsKit {
|
||||||
|
$if windows {
|
||||||
|
wkroot := find_windows_kit_root_by_reg(target_arch) or {
|
||||||
|
if wkroot := find_windows_kit_root_by_env(target_arch) {
|
||||||
|
return wkroot
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return wkroot
|
||||||
|
} $else {
|
||||||
|
return error('Host OS does not support finding a windows kit')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find the root key for installed windows kits from registry
|
||||||
|
fn find_windows_kit_root_by_reg(target_arch string) ?WindowsKit {
|
||||||
|
$if windows {
|
||||||
|
root_key := RegKey(0)
|
||||||
|
path := 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots'
|
||||||
|
rc := C.RegOpenKeyEx(builder.hkey_local_machine, path.to_wide(), 0, builder.key_query_value | builder.key_wow64_32key | builder.key_enumerate_sub_keys,
|
||||||
|
&root_key)
|
||||||
|
|
||||||
|
if rc != 0 {
|
||||||
|
return error('Unable to open root key')
|
||||||
|
}
|
||||||
|
// Try and find win10 kit
|
||||||
|
kit_root := find_windows_kit_internal(root_key, ['KitsRoot10', 'KitsRoot81']) or {
|
||||||
|
C.RegCloseKey(root_key)
|
||||||
|
return error('Unable to find a windows kit')
|
||||||
|
}
|
||||||
|
C.RegCloseKey(root_key)
|
||||||
|
return new_windows_kit(kit_root, target_arch)
|
||||||
|
} $else {
|
||||||
|
return error('Host OS does not support finding a windows kit')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_windows_kit(kit_root string, target_arch string) ?WindowsKit {
|
||||||
|
kit_lib := kit_root + 'Lib'
|
||||||
|
files := os.ls(kit_lib)?
|
||||||
|
mut highest_path := ''
|
||||||
|
mut highest_int := 0
|
||||||
|
for f in files {
|
||||||
|
no_dot := f.replace('.', '')
|
||||||
|
v_int := no_dot.int()
|
||||||
|
if v_int > highest_int {
|
||||||
|
highest_int = v_int
|
||||||
|
highest_path = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kit_lib_highest := kit_lib + '\\$highest_path'
|
||||||
|
kit_include_highest := kit_lib_highest.replace('Lib', 'Include')
|
||||||
|
return WindowsKit{
|
||||||
|
um_lib_path: kit_lib_highest + '\\um\\$target_arch'
|
||||||
|
ucrt_lib_path: kit_lib_highest + '\\ucrt\\$target_arch'
|
||||||
|
um_include_path: kit_include_highest + '\\um'
|
||||||
|
ucrt_include_path: kit_include_highest + '\\ucrt'
|
||||||
|
shared_include_path: kit_include_highest + '\\shared'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_windows_kit_root_by_env(target_arch string) ?WindowsKit {
|
||||||
|
kit_root := os.getenv('WindowsSdkDir')
|
||||||
|
if kit_root == '' {
|
||||||
|
return error('empty WindowsSdkDir')
|
||||||
|
}
|
||||||
|
return new_windows_kit(kit_root, target_arch)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VsInstallation {
|
||||||
|
include_path string
|
||||||
|
lib_path string
|
||||||
|
exe_path string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_vs(vswhere_dir string, host_arch string, target_arch string) ?VsInstallation {
|
||||||
|
$if windows {
|
||||||
|
vsinst := find_vs_by_reg(vswhere_dir, host_arch, target_arch) or {
|
||||||
|
if vsinst := find_vs_by_env(host_arch, target_arch) {
|
||||||
|
return vsinst
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return vsinst
|
||||||
|
} $else {
|
||||||
|
return error('Host OS does not support finding a Visual Studio installation')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_vs_by_reg(vswhere_dir string, host_arch string, target_arch string) ?VsInstallation {
|
||||||
|
$if windows {
|
||||||
|
// Emily:
|
||||||
|
// VSWhere is guaranteed to be installed at this location now
|
||||||
|
// If its not there then end user needs to update their visual studio
|
||||||
|
// installation!
|
||||||
|
res := os.execute('"$vswhere_dir\\Microsoft Visual Studio\\Installer\\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath')
|
||||||
|
// println('res: "$res"')
|
||||||
|
if res.exit_code != 0 {
|
||||||
|
return error_with_code(res.output, res.exit_code)
|
||||||
|
}
|
||||||
|
res_output := res.output.trim_space()
|
||||||
|
version := os.read_file('$res_output\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt') or {
|
||||||
|
// println('Unable to find msvc version')
|
||||||
|
return error('Unable to find vs installation')
|
||||||
|
}
|
||||||
|
// println('version: $version')
|
||||||
|
v := version.trim_space()
|
||||||
|
lib_path := '$res_output\\VC\\Tools\\MSVC\\$v\\lib\\$target_arch'
|
||||||
|
include_path := '$res_output\\VC\\Tools\\MSVC\\$v\\include'
|
||||||
|
if os.exists('$lib_path\\vcruntime.lib') {
|
||||||
|
p := '$res_output\\VC\\Tools\\MSVC\\$v\\bin\\Host$host_arch\\$target_arch'
|
||||||
|
// println('$lib_path $include_path')
|
||||||
|
return VsInstallation{
|
||||||
|
exe_path: p
|
||||||
|
lib_path: lib_path
|
||||||
|
include_path: include_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println('Unable to find vs installation (attempted to use lib path "$lib_path")')
|
||||||
|
return error('Unable to find vs exe folder')
|
||||||
|
} $else {
|
||||||
|
return error('Host OS does not support finding a Visual Studio installation')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_vs_by_env(host_arch string, target_arch string) ?VsInstallation {
|
||||||
|
vs_dir := os.getenv('VSINSTALLDIR')
|
||||||
|
if vs_dir == '' {
|
||||||
|
return error('empty VSINSTALLDIR')
|
||||||
|
}
|
||||||
|
|
||||||
|
vc_tools_dir := os.getenv('VCToolsInstallDir')
|
||||||
|
if vc_tools_dir == '' {
|
||||||
|
return error('empty VCToolsInstallDir')
|
||||||
|
}
|
||||||
|
|
||||||
|
bin_dir := '${vc_tools_dir}bin\\Host$host_arch\\$target_arch'
|
||||||
|
lib_path := '${vc_tools_dir}lib\\$target_arch'
|
||||||
|
include_path := '${vc_tools_dir}include'
|
||||||
|
|
||||||
|
return VsInstallation{
|
||||||
|
exe_path: bin_dir
|
||||||
|
lib_path: lib_path
|
||||||
|
include_path: include_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_msvc(m64_target bool) ?MsvcResult {
|
||||||
|
$if windows {
|
||||||
|
processor_architecture := os.getenv('PROCESSOR_ARCHITECTURE')
|
||||||
|
vswhere_dir := if processor_architecture == 'x86' {
|
||||||
|
'%ProgramFiles%'
|
||||||
|
} else {
|
||||||
|
'%ProgramFiles(x86)%'
|
||||||
|
}
|
||||||
|
host_arch := if processor_architecture == 'x86' { 'X86' } else { 'X64' }
|
||||||
|
target_arch := if !m64_target { 'X86' } else { 'X64' }
|
||||||
|
wk := find_windows_kit_root(target_arch) or { return error('Unable to find windows sdk') }
|
||||||
|
vs := find_vs(vswhere_dir, host_arch, target_arch) or {
|
||||||
|
return error('Unable to find visual studio')
|
||||||
|
}
|
||||||
|
return MsvcResult{
|
||||||
|
full_cl_exe_path: os.real_path(vs.exe_path + os.path_separator + 'cl.exe')
|
||||||
|
exe_path: vs.exe_path
|
||||||
|
um_lib_path: wk.um_lib_path
|
||||||
|
ucrt_lib_path: wk.ucrt_lib_path
|
||||||
|
vs_lib_path: vs.lib_path
|
||||||
|
um_include_path: wk.um_include_path
|
||||||
|
ucrt_include_path: wk.ucrt_include_path
|
||||||
|
vs_include_path: vs.include_path
|
||||||
|
shared_include_path: wk.shared_include_path
|
||||||
|
valid: true
|
||||||
|
}
|
||||||
|
} $else {
|
||||||
|
// This hack allows to at least see the generated .c file with `-os windows -cc msvc -o x.c`
|
||||||
|
// Please do not remove it, unless you also check that the above continues to work.
|
||||||
|
return MsvcResult{
|
||||||
|
full_cl_exe_path: '/usr/bin/true'
|
||||||
|
valid: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut v Builder) cc_msvc() {
|
||||||
|
r := v.cached_msvc
|
||||||
|
if r.valid == false {
|
||||||
|
verror('Cannot find MSVC on this OS')
|
||||||
|
}
|
||||||
|
out_name_obj := os.real_path(v.out_name_c + '.obj')
|
||||||
|
out_name_pdb := os.real_path(v.out_name_c + '.pdb')
|
||||||
|
out_name_cmd_line := os.real_path(v.out_name_c + '.rsp')
|
||||||
|
mut a := []string{}
|
||||||
|
//
|
||||||
|
env_cflags := os.getenv('CFLAGS')
|
||||||
|
mut all_cflags := '$env_cflags $v.pref.cflags'
|
||||||
|
if all_cflags != ' ' {
|
||||||
|
a << all_cflags
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Default arguments
|
||||||
|
// `-w` no warnings
|
||||||
|
// `/we4013` 2 unicode defines, see https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4013?redirectedfrom=MSDN&view=msvc-170
|
||||||
|
// `/volatile:ms` enables atomic volatile (gcc _Atomic)
|
||||||
|
// `/Fo` sets the object file name - needed so we can clean up after ourselves properly
|
||||||
|
// `/F 16777216` changes the stack size to 16MB, see https://docs.microsoft.com/en-us/cpp/build/reference/f-set-stack-size?view=msvc-170
|
||||||
|
a << ['-w', '/we4013', '/volatile:ms', '/Fo"$out_name_obj"', '/F 16777216']
|
||||||
|
if v.pref.is_prod {
|
||||||
|
a << '/O2'
|
||||||
|
}
|
||||||
|
if v.pref.is_debug {
|
||||||
|
a << '/MDd'
|
||||||
|
a << '/D_DEBUG'
|
||||||
|
// /Zi generates a .pdb
|
||||||
|
// /Fd sets the pdb file name (so its not just vc140 all the time)
|
||||||
|
a << ['/Zi', '/Fd"$out_name_pdb"']
|
||||||
|
} else {
|
||||||
|
a << '/MD'
|
||||||
|
a << '/DNDEBUG'
|
||||||
|
}
|
||||||
|
if v.pref.is_shared {
|
||||||
|
if !v.pref.out_name.ends_with('.dll') {
|
||||||
|
v.pref.out_name += '.dll'
|
||||||
|
}
|
||||||
|
// Build dll
|
||||||
|
a << '/LD'
|
||||||
|
} else if !v.pref.out_name.ends_with('.exe') {
|
||||||
|
v.pref.out_name += '.exe'
|
||||||
|
}
|
||||||
|
v.pref.out_name = os.real_path(v.pref.out_name)
|
||||||
|
// alibs := []string{} // builtin.o os.o http.o etc
|
||||||
|
if v.pref.build_mode == .build_module {
|
||||||
|
// Compile only
|
||||||
|
a << '/c'
|
||||||
|
} else if v.pref.build_mode == .default_mode {
|
||||||
|
/*
|
||||||
|
b := os.real_path( '${pref.default_module_path}/vlib/builtin.obj' )
|
||||||
|
alibs << '"$b"'
|
||||||
|
if !os.exists(b) {
|
||||||
|
println('`builtin.obj` not found')
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
for imp in v.ast.imports {
|
||||||
|
if imp == 'webview' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
alibs << '"' + os.real_path( '${pref.default_module_path}/vlib/${imp}.obj' ) + '"'
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
if v.pref.sanitize {
|
||||||
|
eprintln('Sanitize not supported on msvc.')
|
||||||
|
}
|
||||||
|
// The C file we are compiling
|
||||||
|
// a << '"$TmpPath/$v.out_name_c"'
|
||||||
|
a << '"' + os.real_path(v.out_name_c) + '"'
|
||||||
|
// Emily:
|
||||||
|
// Not all of these are needed (but the compiler should discard them if they are not used)
|
||||||
|
// these are the defaults used by msbuild and visual studio
|
||||||
|
mut real_libs := ['kernel32.lib', 'user32.lib', 'advapi32.lib']
|
||||||
|
sflags := msvc_string_flags(v.get_os_cflags())
|
||||||
|
real_libs << sflags.real_libs
|
||||||
|
inc_paths := sflags.inc_paths
|
||||||
|
lib_paths := sflags.lib_paths
|
||||||
|
defines := sflags.defines
|
||||||
|
other_flags := sflags.other_flags
|
||||||
|
// Include the base paths
|
||||||
|
a << r.include_paths()
|
||||||
|
a << defines
|
||||||
|
a << inc_paths
|
||||||
|
a << other_flags
|
||||||
|
// Libs are passed to cl.exe which passes them to the linker
|
||||||
|
a << real_libs.join(' ')
|
||||||
|
a << '/link'
|
||||||
|
a << '/NOLOGO'
|
||||||
|
a << '/OUT:"$v.pref.out_name"'
|
||||||
|
a << r.library_paths()
|
||||||
|
if !all_cflags.contains('/DEBUG') {
|
||||||
|
// only use /DEBUG, if the user *did not* provide its own:
|
||||||
|
a << '/DEBUG:FULL' // required for prod builds to generate a PDB file
|
||||||
|
}
|
||||||
|
if v.pref.is_prod {
|
||||||
|
a << '/INCREMENTAL:NO' // Disable incremental linking
|
||||||
|
a << '/OPT:REF'
|
||||||
|
a << '/OPT:ICF'
|
||||||
|
}
|
||||||
|
a << lib_paths
|
||||||
|
env_ldflags := os.getenv('LDFLAGS')
|
||||||
|
if env_ldflags != '' {
|
||||||
|
a << env_ldflags
|
||||||
|
}
|
||||||
|
v.dump_c_options(a)
|
||||||
|
args := a.join(' ')
|
||||||
|
// write args to a file so that we dont smash createprocess
|
||||||
|
os.write_file(out_name_cmd_line, args) or {
|
||||||
|
verror('Unable to write response file to "$out_name_cmd_line"')
|
||||||
|
}
|
||||||
|
cmd := '"$r.full_cl_exe_path" "@$out_name_cmd_line"'
|
||||||
|
// It is hard to see it at first, but the quotes above ARE balanced :-| ...
|
||||||
|
// Also the double quotes at the start ARE needed.
|
||||||
|
v.show_cc(cmd, out_name_cmd_line, args)
|
||||||
|
if os.user_os() != 'windows' && !v.pref.out_name.ends_with('.c') {
|
||||||
|
verror('Cannot build with msvc on $os.user_os()')
|
||||||
|
}
|
||||||
|
util.timing_start('C msvc')
|
||||||
|
res := os.execute(cmd)
|
||||||
|
if res.exit_code != 0 {
|
||||||
|
eprintln(res.output)
|
||||||
|
verror('msvc error')
|
||||||
|
}
|
||||||
|
util.timing_measure('C msvc')
|
||||||
|
if v.pref.show_c_output {
|
||||||
|
v.show_c_compiler_output(res)
|
||||||
|
} else {
|
||||||
|
v.post_process_c_compiler_output(res)
|
||||||
|
}
|
||||||
|
// println(res)
|
||||||
|
// println('C OUTPUT:')
|
||||||
|
// Always remove the object file - it is completely unnecessary
|
||||||
|
os.rm(out_name_obj) or {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut v Builder) build_thirdparty_obj_file_with_msvc(path string, moduleflags []cflag.CFlag) {
|
||||||
|
msvc := v.cached_msvc
|
||||||
|
if msvc.valid == false {
|
||||||
|
verror('Cannot find MSVC on this OS')
|
||||||
|
}
|
||||||
|
// msvc expects .obj not .o
|
||||||
|
path_without_o_postfix := path[..path.len - 2] // remove .o
|
||||||
|
mut obj_path := '${path_without_o_postfix}.obj'
|
||||||
|
obj_path = os.real_path(obj_path)
|
||||||
|
if os.exists(obj_path) {
|
||||||
|
// println('$obj_path already built.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println('$obj_path not found, building it (with msvc)...')
|
||||||
|
cfile := '${path_without_o_postfix}.c'
|
||||||
|
flags := msvc_string_flags(moduleflags)
|
||||||
|
inc_dirs := flags.inc_paths.join(' ')
|
||||||
|
defines := flags.defines.join(' ')
|
||||||
|
//
|
||||||
|
mut oargs := []string{}
|
||||||
|
env_cflags := os.getenv('CFLAGS')
|
||||||
|
mut all_cflags := '$env_cflags $v.pref.cflags'
|
||||||
|
if all_cflags != ' ' {
|
||||||
|
oargs << all_cflags
|
||||||
|
}
|
||||||
|
oargs << '/NOLOGO'
|
||||||
|
oargs << '/volatile:ms'
|
||||||
|
//
|
||||||
|
if v.pref.is_prod {
|
||||||
|
oargs << '/O2'
|
||||||
|
oargs << '/MD'
|
||||||
|
oargs << '/DNDEBUG'
|
||||||
|
} else {
|
||||||
|
oargs << '/MDd'
|
||||||
|
oargs << '/D_DEBUG'
|
||||||
|
}
|
||||||
|
oargs << defines
|
||||||
|
oargs << msvc.include_paths()
|
||||||
|
oargs << inc_dirs
|
||||||
|
oargs << '/c "$cfile"'
|
||||||
|
oargs << '/Fo"$obj_path"'
|
||||||
|
env_ldflags := os.getenv('LDFLAGS')
|
||||||
|
if env_ldflags != '' {
|
||||||
|
oargs << env_ldflags
|
||||||
|
}
|
||||||
|
v.dump_c_options(oargs)
|
||||||
|
str_oargs := oargs.join(' ')
|
||||||
|
cmd := '"$msvc.full_cl_exe_path" $str_oargs'
|
||||||
|
// Note: the quotes above ARE balanced.
|
||||||
|
$if trace_thirdparty_obj_files ? {
|
||||||
|
println('>>> build_thirdparty_obj_file_with_msvc cmd: $cmd')
|
||||||
|
}
|
||||||
|
res := os.execute(cmd)
|
||||||
|
if res.exit_code != 0 {
|
||||||
|
println('msvc: failed to build a thirdparty object; cmd: $cmd')
|
||||||
|
verror(res.output)
|
||||||
|
}
|
||||||
|
println(res.output)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MsvcStringFlags {
|
||||||
|
mut:
|
||||||
|
real_libs []string
|
||||||
|
inc_paths []string
|
||||||
|
lib_paths []string
|
||||||
|
defines []string
|
||||||
|
other_flags []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn (cflags []CFlag) msvc_string_flags() MsvcStringFlags {
|
||||||
|
pub fn msvc_string_flags(cflags []cflag.CFlag) MsvcStringFlags {
|
||||||
|
mut real_libs := []string{}
|
||||||
|
mut inc_paths := []string{}
|
||||||
|
mut lib_paths := []string{}
|
||||||
|
mut defines := []string{}
|
||||||
|
mut other_flags := []string{}
|
||||||
|
for flag in cflags {
|
||||||
|
// println('fl: $flag.name | flag arg: $flag.value')
|
||||||
|
// 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 flag.name == '-l' {
|
||||||
|
if flag.value.ends_with('.dll') {
|
||||||
|
verror('MSVC cannot link against a dll (`#flag -l $flag.value`)')
|
||||||
|
}
|
||||||
|
// MSVC has no method of linking against a .dll
|
||||||
|
// TODO: we should look for .defs aswell
|
||||||
|
lib_lib := flag.value + '.lib'
|
||||||
|
real_libs << lib_lib
|
||||||
|
} else if flag.name == '-I' {
|
||||||
|
inc_paths << flag.format()
|
||||||
|
} else if flag.name == '-D' {
|
||||||
|
defines << '/D$flag.value'
|
||||||
|
} else if flag.name == '-L' {
|
||||||
|
lib_paths << flag.value
|
||||||
|
lib_paths << flag.value + os.path_separator + 'msvc'
|
||||||
|
// The above allows putting msvc specific .lib files in a subfolder msvc/ ,
|
||||||
|
// where gcc will NOT find them, but cl will do...
|
||||||
|
// Note: gcc is smart enough to not need .lib files at all in most cases, the .dll is enough.
|
||||||
|
// When both a msvc .lib file and .dll file are present in the same folder,
|
||||||
|
// as for example for glfw3, compilation with gcc would fail.
|
||||||
|
} else if flag.value.ends_with('.o') {
|
||||||
|
// msvc expects .obj not .o
|
||||||
|
other_flags << '"${flag.value}bj"'
|
||||||
|
} else if flag.value.starts_with('-D') {
|
||||||
|
defines << '/D${flag.value[2..]}'
|
||||||
|
} else {
|
||||||
|
other_flags << flag.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mut lpaths := []string{}
|
||||||
|
for l in lib_paths {
|
||||||
|
lpaths << '/LIBPATH:"${os.real_path(l)}"'
|
||||||
|
}
|
||||||
|
return MsvcStringFlags{
|
||||||
|
real_libs: real_libs
|
||||||
|
inc_paths: inc_paths
|
||||||
|
lib_paths: lpaths
|
||||||
|
defines: defines
|
||||||
|
other_flags: other_flags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (r MsvcResult) include_paths() []string {
|
||||||
|
mut res := []string{cap: 4}
|
||||||
|
if r.ucrt_include_path != '' {
|
||||||
|
res << '-I "$r.ucrt_include_path"'
|
||||||
|
}
|
||||||
|
if r.vs_include_path != '' {
|
||||||
|
res << '-I "$r.vs_include_path"'
|
||||||
|
}
|
||||||
|
if r.um_include_path != '' {
|
||||||
|
res << '-I "$r.um_include_path"'
|
||||||
|
}
|
||||||
|
if r.shared_include_path != '' {
|
||||||
|
res << '-I "$r.shared_include_path"'
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (r MsvcResult) library_paths() []string {
|
||||||
|
mut res := []string{cap: 3}
|
||||||
|
if r.ucrt_lib_path != '' {
|
||||||
|
res << '/LIBPATH:"$r.ucrt_lib_path"'
|
||||||
|
}
|
||||||
|
if r.um_lib_path != '' {
|
||||||
|
res << '/LIBPATH:"$r.um_lib_path"'
|
||||||
|
}
|
||||||
|
if r.vs_lib_path != '' {
|
||||||
|
res << '/LIBPATH:"$r.vs_lib_path"'
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
@ -270,7 +270,7 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
|||||||
} else {
|
} else {
|
||||||
// str += str2 => `str = string__plus(str, str2)`
|
// str += str2 => `str = string__plus(str, str2)`
|
||||||
g.expr(left)
|
g.expr(left)
|
||||||
g.write(' = /*f*/string__plus(')
|
g.write(' = string__plus(')
|
||||||
}
|
}
|
||||||
g.is_assign_lhs = false
|
g.is_assign_lhs = false
|
||||||
str_add = true
|
str_add = true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user