diff --git a/cmd/v/help/build-c.txt b/cmd/v/help/build-c.txt index dcf415b283..a219c009b0 100644 --- a/cmd/v/help/build-c.txt +++ b/cmd/v/help/build-c.txt @@ -127,3 +127,8 @@ see also `v help build`. -show-c-output Prints the output, that your C compiler produced, while compiling your program. + + -dump-c-flags file.txt + Write all C flags into `file.txt`, one flag per line. + If `file.txt` is `-`, then write the flags to stdout, one flag per line. + diff --git a/vlib/v/builder/cc.v b/vlib/v/builder/cc.v index 12bc21a623..5feb9aa0e4 100644 --- a/vlib/v/builder/cc.v +++ b/vlib/v/builder/cc.v @@ -7,6 +7,7 @@ import os import v.cflag import v.pref import v.util +import v.vcache import term const ( @@ -135,7 +136,7 @@ fn (mut v Builder) rebuild_cached_module(vexe string, imp_path string) string { os.chdir(vroot) boptions := v.pref.build_options.join(' ') rebuild_cmd := '$vexe $boptions build-module $imp_path' - // eprintln('>> rebuild_cmd: $rebuild_cmd') + vcache.dlog('| Builder.' + @FN, 'vexe: $vexe | imp_path: $imp_path | rebuild_cmd: $rebuild_cmd') os.system(rebuild_cmd) rebuilded_o := v.pref.cache_manager.exists('.o', imp_path) or { panic('could not rebuild cache module for $imp_path, error: $err') @@ -177,6 +178,7 @@ mut: args []string // ordinary C options like `-O2` wargs []string // for `-Wxyz` *exclusively* o_args []string // for `-o target` + source_args []string // for `x.tmp.c` post_args []string // options that should go after .o_args linker_flags []string // `-lm` } @@ -184,8 +186,8 @@ mut: fn (mut v Builder) setup_ccompiler_options(ccompiler string) { mut ccoptions := CcompilerOptions{} // - mut debug_options := '-g' - mut optimization_options := '-O2' + mut debug_options := ['-g'] + mut optimization_options := ['-O2'] // arguments for the C compiler // TODO : activate -Werror once no warnings remain // '-Werror', @@ -233,28 +235,28 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) { } if ccoptions.is_cc_clang { if ccoptions.debug_mode { - debug_options = '-g -O0' + debug_options = ['-g', '-O0'] } - optimization_options = '-O3' + optimization_options = ['-O3'] mut have_flto := true $if openbsd { have_flto = false } if have_flto { - optimization_options += ' -flto' + optimization_options << '-flto' } } if ccoptions.is_cc_gcc { if ccoptions.debug_mode { - debug_options = '-g -no-pie' + debug_options = ['-g', '-no-pie'] } - optimization_options = '-O3 -fno-strict-aliasing -flto' + optimization_options = ['-O3', '-fno-strict-aliasing', '-flto'] } // if ccoptions.debug_mode { ccoptions.args << debug_options // $if macos { - // args << ' -ferror-limit=5000 ' + // args << '-ferror-limit=5000' // } } if v.pref.is_prod { @@ -287,7 +289,7 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) { ccoptions.linker_flags << '-nostdlib' } if ccoptions.debug_mode && os.user_os() != 'windows' && v.pref.build_mode != .build_module { - ccoptions.linker_flags << ' -rdynamic ' // needed for nicer symbolic backtraces + ccoptions.linker_flags << '-rdynamic' // needed for nicer symbolic backtraces } if ccompiler != 'msvc' && v.pref.os != .freebsd { ccoptions.wargs << '-Werror=implicit-function-declaration' @@ -307,7 +309,7 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) { } } // The C file we are compiling - ccoptions.post_args << '"$v.out_name_c"' + ccoptions.source_args << '"$v.out_name_c"' if v.pref.os == .macos { ccoptions.post_args << '-x none' } @@ -320,10 +322,11 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) { ccoptions.post_args << '-municode' } cflags := v.get_os_cflags() - // add .o files ccoptions.o_args << cflags.c_options_only_object_files() - // add all flags (-I -l -L etc) not .o files - ccoptions.post_args << cflags.c_options_without_object_files() + defines, others, libs := cflags.defines_others_libs() + ccoptions.args << defines + ccoptions.args << others + ccoptions.linker_flags << libs // TODO: why is this duplicated from above? if v.pref.use_cache && v.pref.build_mode != .build_module { // vexe := pref.vexe_path() @@ -382,6 +385,7 @@ fn (ccoptions CcompilerOptions) all_args() []string { all << ccoptions.env_cflags all << ccoptions.args all << ccoptions.o_args + all << ccoptions.source_args all << ccoptions.post_args all << ccoptions.linker_flags all << ccoptions.env_ldflags @@ -472,6 +476,17 @@ fn (mut v Builder) vjs_cc() bool { return false } +fn (mut v Builder) dump_c_options(all_args []string) { + if v.pref.dump_c_flags != '' { + non_empty_args := all_args.filter(it != '').join('\n') + '\n' + if v.pref.dump_c_flags == '-' { + print(non_empty_args) + } else { + os.write_file(v.pref.dump_c_flags, non_empty_args) or { panic(err) } + } + } +} + fn (mut v Builder) cc() { if os.executable().contains('vfmt') { return @@ -523,13 +538,13 @@ fn (mut v Builder) cc() { v.build_thirdparty_obj_files() v.setup_output_name() // - mut libs := '' // builtin.o os.o http.o etc + mut libs := []string{} // builtin.o os.o http.o etc if v.pref.build_mode == .build_module { v.ccoptions.args << '-c' } else if v.pref.use_cache { mut built_modules := []string{} builtin_obj_path := v.rebuild_cached_module(vexe, 'vlib/builtin') - libs += ' ' + builtin_obj_path + libs << builtin_obj_path for ast_file in v.parsed_files { is_test := ast_file.path.ends_with('_test.v') if is_test && ast_file.mod.name != 'main' { @@ -538,7 +553,7 @@ fn (mut v Builder) cc() { break } obj_path := v.rebuild_cached_module(vexe, imp_path) - libs += ' ' + obj_path + libs << obj_path built_modules << ast_file.mod.name } for imp_stmt in ast_file.imports { @@ -576,9 +591,10 @@ fn (mut v Builder) cc() { break } obj_path := v.rebuild_cached_module(vexe, imp_path) - libs += ' ' + obj_path + libs << obj_path if obj_path.ends_with('vlib/ui.o') { - v.ccoptions.post_args << '-framework Cocoa -framework Carbon' + v.ccoptions.post_args << '-framework Cocoa' + v.ccoptions.post_args << '-framework Carbon' } built_modules << imp } @@ -594,6 +610,7 @@ fn (mut v Builder) cc() { } // all_args := v.ccoptions.all_args() + v.dump_c_options(all_args) str_args := all_args.join(' ') // write args to response file response_file := '${v.out_name_c}.rsp' @@ -631,9 +648,9 @@ fn (mut v Builder) cc() { v.show_c_compiler_output(res) } os.chdir(original_pwd) - $if trace_use_cache ? { - eprintln('>>>> v.pref.use_cache: $v.pref.use_cache | v.pref.retry_compilation: $v.pref.retry_compilation | cmd res.exit_code: $res.exit_code | cmd: $cmd') - } + vcache.dlog('| Builder.' + @FN, '> v.pref.use_cache: $v.pref.use_cache | v.pref.retry_compilation: $v.pref.retry_compilation') + vcache.dlog('| Builder.' + @FN, '> cmd res.exit_code: $res.exit_code | cmd: $cmd') + vcache.dlog('| Builder.' + @FN, '> response_file_content:\n$response_file_content') if res.exit_code != 0 { if ccompiler.contains('tcc.exe') { // a TCC problem? Retry with the system cc: @@ -753,28 +770,38 @@ fn (mut b Builder) cc_linux_cross() { } } obj_file := b.out_name_c + '.o' - mut cc_args := '-fPIC -w -c -target x86_64-linux-gnu -c -o $obj_file $b.out_name_c -I $sysroot/include ' cflags := b.get_os_cflags() - cc_args += cflags.c_options_without_object_files() - cc_cmd := 'cc $cc_args' + defines, others, libs := cflags.defines_others_libs() + mut cc_args := []string{} + cc_args << '-w' + cc_args << '-fPIC' + cc_args << '-c' + cc_args << '-target x86_64-linux-gnu' + cc_args << defines + cc_args << '-I $sysroot/include ' + cc_args << others + cc_args << '-o "$obj_file"' + cc_args << '-c "$b.out_name_c"' + cc_args << libs + cc_cmd := 'cc ' + cc_args.join(' ') if b.pref.show_cc { println(cc_cmd) } - cc_res := os.exec(cc_cmd) or { - println('Cross compilation for Linux failed (first step, cc). Make sure you have clang installed.') - verror(err) - return - } + cc_res := os.exec(cc_cmd) or { os.Result{ + exit_code: 1 + output: 'no `cc` command found' + } } if cc_res.exit_code != 0 { println('Cross compilation for Linux failed (first step, cc). Make sure you have clang installed.') verror(cc_res.output) + return } - linker_args := ['-L $sysroot/usr/lib/x86_64-linux-gnu/', '--sysroot=$sysroot -v -o $b.pref.out_name -m elf_x86_64', - '-dynamic-linker /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2', '$sysroot/crt1.o $sysroot/crti.o $obj_file', - '-lc', '-lcrypto', '-lssl', '-lpthread', '$sysroot/crtn.o', cflags.c_options_only_object_files()] + mut linker_args := ['-L $sysroot/usr/lib/x86_64-linux-gnu/', '--sysroot=$sysroot', '-v', '-o $b.pref.out_name', + '-m elf_x86_64', '-dynamic-linker /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2', '$sysroot/crt1.o $sysroot/crti.o $obj_file', + '-lc', '-lcrypto', '-lssl', '-lpthread', '$sysroot/crtn.o'] + linker_args << cflags.c_options_only_object_files() // -ldl - linker_args_str := linker_args.join(' ') - linker_cmd := '$sysroot/ld.lld $linker_args_str' + linker_cmd := '$sysroot/ld.lld ' + linker_args.join(' ') // s = s.replace('SYSROOT', sysroot) // TODO $ inter bug // s = s.replace('-o hi', '-o ' + c.pref.out_name) if b.pref.show_cc { @@ -800,46 +827,48 @@ fn (mut c Builder) cc_windows_cross() { if !c.pref.out_name.ends_with('.exe') { c.pref.out_name += '.exe' } - mut args := '' - args += ' $c.pref.cflags ' - args += ' -o $c.pref.out_name -w -L. ' + mut args := []string{} + args << '$c.pref.cflags' + args << '-o $c.pref.out_name' + args << '-w -L.' // cflags := c.get_os_cflags() // -I flags - args += if c.pref.ccompiler == 'msvc' { - cflags.c_options_before_target_msvc() + if c.pref.ccompiler == 'msvc' { + args << cflags.c_options_before_target_msvc() } else { - cflags.c_options_before_target() + args << cflags.c_options_before_target() } - mut optimization_options := '' - mut debug_options := '' + mut optimization_options := []string{} + mut debug_options := []string{} if c.pref.is_prod { if c.pref.ccompiler != 'msvc' { - optimization_options = ' -O3 -fno-strict-aliasing -flto ' + optimization_options = ['-O3', '-fno-strict-aliasing', '-flto'] } } if c.pref.is_debug { if c.pref.ccompiler != 'msvc' { - debug_options = ' -O0 -g -gdwarf-2 ' + debug_options = ['-O0', '-g', '-gdwarf-2'] } } - mut libs := '' + mut libs := []string{} if false && c.pref.build_mode == .default_mode { - libs = '"$pref.default_module_path/vlib/builtin.o"' - if !os.exists(libs) { - verror('`$libs` not found') + builtin_o := '"$pref.default_module_path/vlib/builtin.o"' + libs << builtin_o + if !os.exists(builtin_o) { + verror('$builtin_o not found') } for imp in c.table.imports { - libs += ' "$pref.default_module_path/vlib/${imp}.o"' + libs << '"$pref.default_module_path/vlib/${imp}.o"' } } // add the thirdparty .o files, produced by all the #flag directives: - args += ' ' + cflags.c_options_only_object_files() + ' ' - args += ' $c.out_name_c ' - args += if c.pref.ccompiler == 'msvc' { - cflags.c_options_after_target_msvc() + args << cflags.c_options_only_object_files() + args << c.out_name_c + if c.pref.ccompiler == 'msvc' { + args << cflags.c_options_after_target_msvc() } else { - cflags.c_options_after_target() + args << cflags.c_options_after_target() } /* winroot := '${pref.default_module_path}/winroot' @@ -860,7 +889,14 @@ fn (mut c Builder) cc_windows_cross() { println(os.user_os()) panic('your platform is not supported yet') } - mut cmd := '$builder.mingw_cc $optimization_options $debug_options -std=gnu11 $args -municode' + mut all_args := []string{} + all_args << optimization_options + all_args << debug_options + all_args << '-std=gnu11' + all_args << args + all_args << '-municode' + c.dump_c_options(all_args) + mut cmd := '$builder.mingw_cc ' + all_args.join(' ') // cmd := 'clang -o $obj_name -w $include -m32 -c -target x86_64-win32 ${pref.default_module_path}/$c.out_name_c' if c.pref.is_verbose || c.pref.show_cc { println(cmd) @@ -908,8 +944,6 @@ fn (mut v Builder) build_thirdparty_obj_files() { fn (mut v Builder) build_thirdparty_obj_file(path string, moduleflags []cflag.CFlag) { obj_path := os.real_path(path) cfile := '${obj_path[..obj_path.len - 2]}.c' - btarget := moduleflags.c_options_before_target() - atarget := moduleflags.c_options_after_target() opath := v.pref.cache_manager.postfix_with_key2cpath('.o', obj_path) if os.exists(opath) { return @@ -928,8 +962,13 @@ fn (mut v Builder) build_thirdparty_obj_file(path string, moduleflags []cflag.CF current_folder := os.getwd() os.chdir(os.dir(pref.vexe_path())) // - cc_options := v.ccoptions.thirdparty_object_args([v.pref.third_party_option, btarget, '-o', - '"$opath"', '-c', '"$cfile"', atarget]).join(' ') + mut all_options := []string{} + all_options << v.pref.third_party_option + all_options << moduleflags.c_options_before_target() + all_options << '-o "$opath"' + all_options << '-c "$cfile"' + all_options << moduleflags.c_options_after_target() + cc_options := v.ccoptions.thirdparty_object_args(all_options).join(' ') cmd := '$v.pref.ccompiler $cc_options' $if trace_thirdparty_obj_files ? { println('>>> build_thirdparty_obj_files cmd: $cmd') diff --git a/vlib/v/cflag/cflags.v b/vlib/v/cflag/cflags.v index d6a26f2c05..77da12b05a 100644 --- a/vlib/v/cflag/cflags.v +++ b/vlib/v/cflag/cflags.v @@ -37,37 +37,28 @@ pub fn (cf &CFlag) format() string { } // TODO: implement msvc specific c_options_before_target and c_options_after_target ... -pub fn (cflags []CFlag) c_options_before_target_msvc() string { - return '' +pub fn (cflags []CFlag) c_options_before_target_msvc() []string { + return [] } -pub fn (cflags []CFlag) c_options_after_target_msvc() string { - return '' +pub fn (cflags []CFlag) c_options_after_target_msvc() []string { + return [] } -pub fn (cflags []CFlag) c_options_before_target() string { - // -I flags, optimization flags and so on +pub fn (cflags []CFlag) c_options_before_target() []string { + defines, others, _ := cflags.defines_others_libs() mut args := []string{} - for flag in cflags { - if flag.name != '-l' && !flag.value.ends_with('.o') { - args << flag.format() - } - } - return args.join(' ') + args << defines + args << others + return args } -pub fn (cflags []CFlag) c_options_after_target() string { - // -l flags (libs) - mut args := []string{} - for flag in cflags { - if flag.name == '-l' { - args << flag.format() - } - } - return args.join(' ') +pub fn (cflags []CFlag) c_options_after_target() []string { + _, _, libs := cflags.defines_others_libs() + return libs } -pub fn (cflags []CFlag) c_options_without_object_files() string { +pub 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') { @@ -75,15 +66,34 @@ pub fn (cflags []CFlag) c_options_without_object_files() string { } args << flag.format() } - return args.join(' ') + return args } -pub fn (cflags []CFlag) c_options_only_object_files() string { +pub 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(' ') + return args +} + +pub fn (cflags []CFlag) defines_others_libs() ([]string, []string, []string) { + copts_without_obj_files := cflags.c_options_without_object_files() + mut defines := []string{} + mut others := []string{} + mut libs := []string{} + for copt in copts_without_obj_files { + if copt.starts_with('-l') { + libs << copt + continue + } + if copt.starts_with('-D') { + defines << copt + continue + } + others << copt + } + return defines, others, libs } diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index e9b8dfd8db..99599fe88e 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -78,8 +78,10 @@ pub mut: // NB: passing -cg instead of -g will set is_vlines to false and is_debug to true, thus making v generate cleaner C files, // which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks). // use cached modules to speed up compilation. + dump_c_flags string // `-dump-c-flags file.txt` - let V store all C flags, passed to the backend C compiler + // in `file.txt`, one C flag/value per line. use_cache bool // = true - retry_compilation bool = true + retry_compilation bool = true // retry the compilation with another C compiler, if tcc fails. is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run // TODO Convert this into a []string cflags string // Additional options which will be passed to the C compiler. @@ -276,6 +278,10 @@ pub fn parse_args(args []string) (&Preferences, string) { '-show-c-output' { res.show_c_output = true } + '-dump-c-flags' { + res.dump_c_flags = cmdline.option(current_args, arg, '-') + i++ + } '-experimental' { res.experimental = true }