From a585c8c22cc587672f18606b7fc78444fd4adb85 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Mon, 23 Sep 2019 00:51:59 +0300 Subject: [PATCH] compiler: compile thirdparty object files using module specific flags --- compiler/cc.v | 35 ++----- compiler/cflags.v | 72 +++++++++++++- compiler/cgen.v | 6 +- compiler/comptime.v | 2 +- compiler/msvc.v | 231 ++++++++++++++++++++++++-------------------- 5 files changed, 211 insertions(+), 135 deletions(-) diff --git a/compiler/cc.v b/compiler/cc.v index e02f162f55..ba07751e01 100644 --- a/compiler/cc.v +++ b/compiler/cc.v @@ -147,15 +147,10 @@ fn (v mut V) cc() { cflags := v.get_os_cflags() // add .o files - for flag in cflags { - if !flag.value.ends_with('.o') { continue } - a << flag.format() - } + a << cflags.c_options_only_object_files() + // add all flags (-I -l -L etc) not .o files - for flag in cflags { - if flag.value.ends_with('.o') { continue } - a << flag.format() - } + a << cflags.c_options_without_object_files() a << libs // Without these libs compilation will fail on Linux @@ -274,12 +269,7 @@ fn (c mut V) cc_windows_cross() { mut args := '-o $c.out_name -w -L. ' cflags := c.get_os_cflags() // -I flags - for flag in cflags { - if flag.name != '-l' { - args += flag.format() - args += ' ' - } - } + args += cflags.c_options_before_target() mut libs := '' if c.pref.build_mode == .default_mode { libs = '"$ModPath/vlib/builtin.o"' @@ -292,13 +282,7 @@ fn (c mut V) cc_windows_cross() { } } args += ' $c.out_name_c ' - // -l flags (libs) - for flag in cflags { - if flag.name == '-l' { - args += flag.format() - args += ' ' - } - } + args += cflags.c_options_after_target() println('Cross compiling for Windows...') winroot := '$ModPath/winroot' if !os.dir_exists(winroot) { @@ -339,14 +323,15 @@ fn (c mut V) cc_windows_cross() { println('Done!') } -fn (c V) build_thirdparty_obj_files() { +fn (c &V) build_thirdparty_obj_files() { for flag in c.get_os_cflags() { - if flag.value.ends_with('.o') { + if flag.value.ends_with('.o') { + rest_of_module_flags := c.get_rest_of_module_cflags( flag ) if c.os == .msvc { - build_thirdparty_obj_file_with_msvc(flag.value) + build_thirdparty_obj_file_with_msvc(flag.value, rest_of_module_flags) } else { - build_thirdparty_obj_file(flag.value) + build_thirdparty_obj_file(flag.value, rest_of_module_flags) } } } diff --git a/compiler/cflags.v b/compiler/cflags.v index 2edb6a2ee9..ec7e1e5a65 100644 --- a/compiler/cflags.v +++ b/compiler/cflags.v @@ -8,13 +8,18 @@ import os // parsed cflag struct CFlag{ + mod string // the module in which the flag was given os string // eg. windows | darwin | linux name string // eg. -I value string // eg. /path/to/include } +fn (c &CFlag) str() string { + return 'CFlag{ name: "$c.name" value: "$c.value" mod: "$c.mod" os: "$c.os" }' +} + // get flags for current os -fn (v V) get_os_cflags() []CFlag { +fn (v &V) get_os_cflags() []CFlag { mut flags := []CFlag for flag in v.table.cflags { if flag.os == '' @@ -27,6 +32,18 @@ fn (v V) get_os_cflags() []CFlag { return flags } +fn (v &V) get_rest_of_module_cflags(c &CFlag) []CFlag { + mut flags := []CFlag + cflags := v.get_os_cflags() + for flag in cflags { + if c.mod == flag.mod { + if c.name == flag.name && c.value == flag.value && c.os == flag.os { continue } + flags << flag + } + } + return flags +} + // format flag fn (cf &CFlag) format() string { mut value := cf.value @@ -52,7 +69,7 @@ fn (table &Table) has_cflag(cflag CFlag) bool { // parse the flags to (table.cflags) []CFlag // Note: clean up big time (joe-c) -fn (table mut Table) parse_cflag(cflag string) { +fn (table mut Table) parse_cflag(cflag string, mod string) { allowed_flags := [ 'framework', 'library', @@ -107,6 +124,7 @@ fn (table mut Table) parse_cflag(cflag string) { index = -1 } cf := CFlag{ + mod: mod, os: fos, name: name, value: value @@ -119,3 +137,53 @@ fn (table mut Table) parse_cflag(cflag string) { } } } + +//TODO: implement msvc specific c_options_before_target and c_options_after_target ... +fn (cflags []CFlag) c_options_before_target() string { + $if msvc { + return '' + } + // -I flags, optimization flags and so on + mut args:=[]string + for flag in cflags { + if flag.name != '-l' { + args << flag.format() + } + } + return args.join(' ') +} + +fn (cflags []CFlag) c_options_after_target() string { + $if msvc { + return '' + } + // -l flags (libs) + mut args:=[]string + for flag in cflags { + if flag.name == '-l' { + args << flag.format() + } + } + return args.join(' ') +} + +fn (cflags []CFlag) c_options_without_object_files() string { + mut args:=[]string + for flag in cflags { + if flag.value.ends_with('.o') || flag.value.ends_with('.obj') { + continue + } + args << flag.format() + } + return args.join(' ') +} + +fn (cflags []CFlag) c_options_only_object_files() string { + mut args:=[]string + for flag in cflags { + if flag.value.ends_with('.o') || flag.value.ends_with('.obj') { + args << flag.format() + } + } + return args.join(' ') +} diff --git a/compiler/cgen.v b/compiler/cgen.v index bcb746901c..dbaf707588 100644 --- a/compiler/cgen.v +++ b/compiler/cgen.v @@ -239,7 +239,7 @@ fn (g mut CGen) add_to_main(s string) { } -fn build_thirdparty_obj_file(path string) { +fn build_thirdparty_obj_file(path string, moduleflags []CFlag) { obj_path := os.realpath(path) if os.file_exists(obj_path) { return @@ -255,7 +255,9 @@ fn build_thirdparty_obj_file(path string) { } cc := find_c_compiler() cc_thirdparty_options := find_c_compiler_thirdparty_options() - cmd := '$cc $cc_thirdparty_options -c -o "$obj_path" $cfiles' + btarget := moduleflags.c_options_before_target() + atarget := moduleflags.c_options_after_target() + cmd := '$cc $cc_thirdparty_options $btarget -c -o "$obj_path" $cfiles $atarget ' res := os.exec(cmd) or { println('failed thirdparty object build cmd: $cmd') cerror(err) diff --git a/compiler/comptime.v b/compiler/comptime.v index 96c4cb73fc..2fc8f55a2b 100644 --- a/compiler/comptime.v +++ b/compiler/comptime.v @@ -151,7 +151,7 @@ fn (p mut Parser) chash() { flag = flag.replace('@VROOT', p.vroot) flag = flag.replace('@VMOD', ModPath) p.log('adding flag "$flag"') - p.table.parse_cflag(flag) + p.table.parse_cflag(flag, p.mod) return } if hash.starts_with('include') { diff --git a/compiler/msvc.v b/compiler/msvc.v index b61e071637..bea072fdb6 100644 --- a/compiler/msvc.v +++ b/compiler/msvc.v @@ -295,13 +295,131 @@ pub fn (v mut V) cc_msvc() { 'odbccp32.lib' ] - mut inc_paths := []string{} - mut lib_paths := []string{} - mut other_flags := []string{} + sflags := v.get_os_cflags().msvc_string_flags() + real_libs << sflags.real_libs + inc_paths := sflags.inc_paths + lib_paths := sflags.lib_paths + other_flags := sflags.other_flags - for flag in v.get_os_cflags() { - //println('fl: $flag.name | flag arg: $flag.value') + // Include the base paths + a << '-I "$r.ucrt_include_path"' + a << '-I "$r.vs_include_path"' + a << '-I "$r.um_include_path"' + a << '-I "$r.shared_include_path"' + 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.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 + + if !v.pref.is_prod { + a << '/DEBUG:FULL' + } else { + a << '/DEBUG:NONE' + } + + a << lib_paths + + args := a.join(' ') + + cmd := '""$r.full_cl_exe_path" $args"' + // It is hard to see it at first, but the quotes above ARE balanced :-| ... + // Also the double quotes at the start ARE needed. + if v.pref.show_c_cmd || v.pref.is_verbose { + println('\n========== cl cmd line:') + println(cmd) + println('==========\n') + } + + // println('$cmd') + + res := os.exec(cmd) or { + println(err) + cerror('msvc error') + return + } + if res.exit_code != 0 { + cerror(res.output) + } + // println(res) + // println('C OUTPUT:') + + if !v.pref.is_debug && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' { + os.rm(v.out_name_c) + } + + // Always remove the object file - it is completely unnecessary + os.rm(out_name_obj) +} +fn build_thirdparty_obj_file_with_msvc(path string, moduleflags []CFlag) { + msvc := find_msvc() or { + println('Could not find visual studio') + return + } + + // msvc expects .obj not .o + mut obj_path := '${path}bj' + + obj_path = os.realpath(obj_path) + + if os.file_exists(obj_path) { + println('$obj_path already build.') + return + } + + println('$obj_path not found, building it (with msvc)...') + parent := os.dir(obj_path) + files := os.ls(parent) + + mut cfiles := '' + for file in files { + if file.ends_with('.c') { + cfiles += '"' + os.realpath( parent + os.PathSeparator + 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: $cfiles') + + btarget := moduleflags.c_options_before_target() + atarget := moduleflags.c_options_after_target() + cmd := '""$msvc.full_cl_exe_path" /volatile:ms /Z7 $include_string /c $btarget $cfiles $atarget /Fo"$obj_path""' + //NB: the quotes above ARE balanced. + println('thirdparty cmd line: $cmd') + res := os.exec(cmd) or { + cerror(err) + return + } + println(res.output) +} + + +struct MsvcStringFlags { +mut: + real_libs []string + inc_paths []string + lib_paths []string + other_flags []string +} + +fn (cflags []CFlag) msvc_string_flags() MsvcStringFlags { + mut real_libs := []string + mut inc_paths := []string + mut lib_paths := []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 @@ -335,107 +453,10 @@ pub fn (v mut V) cc_msvc() { } } - // Include the base paths - a << '-I "$r.ucrt_include_path"' - a << '-I "$r.vs_include_path"' - a << '-I "$r.um_include_path"' - a << '-I "$r.shared_include_path"' - - 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.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 - + mut lpaths := []string for l in lib_paths { - a << '/LIBPATH:"' + os.realpath(l) + '"' + lpaths << '/LIBPATH:"' + os.realpath(l) + '"' } - if !v.pref.is_prod { - a << '/DEBUG:FULL' - } else { - a << '/DEBUG:NONE' - } - - args := a.join(' ') - - cmd := '""$r.full_cl_exe_path" $args"' - // It is hard to see it at first, but the quotes above ARE balanced :-| ... - // Also the double quotes at the start ARE needed. - if v.pref.show_c_cmd || v.pref.is_verbose { - println('\n========== cl cmd line:') - println(cmd) - println('==========\n') - } - - // println('$cmd') - - res := os.exec(cmd) or { - println(err) - cerror('msvc error') - return - } - if res.exit_code != 0 { - cerror(res.output) - } - // println(res) - // println('C OUTPUT:') - - if !v.pref.is_debug && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' { - os.rm(v.out_name_c) - } - - // Always remove the object file - it is completely unnecessary - os.rm(out_name_obj) + return MsvcStringFlags{ real_libs, inc_paths, lpaths, other_flags } } - -fn build_thirdparty_obj_file_with_msvc(path string) { - msvc := find_msvc() or { - println('Could not find visual studio') - return - } - - // msvc expects .obj not .o - mut obj_path := '${path}bj' - - obj_path = os.realpath(obj_path) - - if os.file_exists(obj_path) { - println('$obj_path already build.') - return - } - - println('$obj_path not found, building it (with msvc)...') - parent := os.dir(obj_path) - files := os.ls(parent) - - mut cfiles := '' - for file in files { - if file.ends_with('.c') { - cfiles += '"' + os.realpath( parent + os.PathSeparator + 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: $cfiles') - - cmd := '""$msvc.full_cl_exe_path" /volatile:ms /Z7 $include_string /c $cfiles /Fo"$obj_path""' - //NB: the quotes above ARE balanced. - println('thirdparty cmd line: $cmd') - res := os.exec(cmd) or { - cerror(err) - return - } - println(res.output) -} -