builder: implement `-dump-c-flags flags.txt`

pull/8420/head
Delyan Angelov 2021-01-29 18:05:09 +02:00
parent 49b01549da
commit 4d180171ba
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
4 changed files with 146 additions and 86 deletions

View File

@ -127,3 +127,8 @@ see also `v help build`.
-show-c-output -show-c-output
Prints the output, that your C compiler produced, while compiling your program. 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.

View File

@ -7,6 +7,7 @@ import os
import v.cflag import v.cflag
import v.pref import v.pref
import v.util import v.util
import v.vcache
import term import term
const ( const (
@ -135,7 +136,7 @@ fn (mut v Builder) rebuild_cached_module(vexe string, imp_path string) string {
os.chdir(vroot) os.chdir(vroot)
boptions := v.pref.build_options.join(' ') boptions := v.pref.build_options.join(' ')
rebuild_cmd := '$vexe $boptions build-module $imp_path' 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) os.system(rebuild_cmd)
rebuilded_o := v.pref.cache_manager.exists('.o', imp_path) or { rebuilded_o := v.pref.cache_manager.exists('.o', imp_path) or {
panic('could not rebuild cache module for $imp_path, error: $err') panic('could not rebuild cache module for $imp_path, error: $err')
@ -177,6 +178,7 @@ mut:
args []string // ordinary C options like `-O2` args []string // ordinary C options like `-O2`
wargs []string // for `-Wxyz` *exclusively* wargs []string // for `-Wxyz` *exclusively*
o_args []string // for `-o target` o_args []string // for `-o target`
source_args []string // for `x.tmp.c`
post_args []string // options that should go after .o_args post_args []string // options that should go after .o_args
linker_flags []string // `-lm` linker_flags []string // `-lm`
} }
@ -184,8 +186,8 @@ mut:
fn (mut v Builder) setup_ccompiler_options(ccompiler string) { fn (mut v Builder) setup_ccompiler_options(ccompiler string) {
mut ccoptions := CcompilerOptions{} mut ccoptions := CcompilerOptions{}
// //
mut debug_options := '-g' mut debug_options := ['-g']
mut optimization_options := '-O2' mut optimization_options := ['-O2']
// arguments for the C compiler // arguments for the C compiler
// TODO : activate -Werror once no warnings remain // TODO : activate -Werror once no warnings remain
// '-Werror', // '-Werror',
@ -233,28 +235,28 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) {
} }
if ccoptions.is_cc_clang { if ccoptions.is_cc_clang {
if ccoptions.debug_mode { if ccoptions.debug_mode {
debug_options = '-g -O0' debug_options = ['-g', '-O0']
} }
optimization_options = '-O3' optimization_options = ['-O3']
mut have_flto := true mut have_flto := true
$if openbsd { $if openbsd {
have_flto = false have_flto = false
} }
if have_flto { if have_flto {
optimization_options += ' -flto' optimization_options << '-flto'
} }
} }
if ccoptions.is_cc_gcc { if ccoptions.is_cc_gcc {
if ccoptions.debug_mode { 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 { if ccoptions.debug_mode {
ccoptions.args << debug_options ccoptions.args << debug_options
// $if macos { // $if macos {
// args << ' -ferror-limit=5000 ' // args << '-ferror-limit=5000'
// } // }
} }
if v.pref.is_prod { if v.pref.is_prod {
@ -287,7 +289,7 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) {
ccoptions.linker_flags << '-nostdlib' ccoptions.linker_flags << '-nostdlib'
} }
if ccoptions.debug_mode && os.user_os() != 'windows' && v.pref.build_mode != .build_module { 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 { if ccompiler != 'msvc' && v.pref.os != .freebsd {
ccoptions.wargs << '-Werror=implicit-function-declaration' 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 // 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 { if v.pref.os == .macos {
ccoptions.post_args << '-x none' ccoptions.post_args << '-x none'
} }
@ -320,10 +322,11 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) {
ccoptions.post_args << '-municode' ccoptions.post_args << '-municode'
} }
cflags := v.get_os_cflags() cflags := v.get_os_cflags()
// add .o files
ccoptions.o_args << cflags.c_options_only_object_files() ccoptions.o_args << cflags.c_options_only_object_files()
// add all flags (-I -l -L etc) not .o files defines, others, libs := cflags.defines_others_libs()
ccoptions.post_args << cflags.c_options_without_object_files() ccoptions.args << defines
ccoptions.args << others
ccoptions.linker_flags << libs
// TODO: why is this duplicated from above? // TODO: why is this duplicated from above?
if v.pref.use_cache && v.pref.build_mode != .build_module { if v.pref.use_cache && v.pref.build_mode != .build_module {
// vexe := pref.vexe_path() // vexe := pref.vexe_path()
@ -382,6 +385,7 @@ fn (ccoptions CcompilerOptions) all_args() []string {
all << ccoptions.env_cflags all << ccoptions.env_cflags
all << ccoptions.args all << ccoptions.args
all << ccoptions.o_args all << ccoptions.o_args
all << ccoptions.source_args
all << ccoptions.post_args all << ccoptions.post_args
all << ccoptions.linker_flags all << ccoptions.linker_flags
all << ccoptions.env_ldflags all << ccoptions.env_ldflags
@ -472,6 +476,17 @@ fn (mut v Builder) vjs_cc() bool {
return false 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() { fn (mut v Builder) cc() {
if os.executable().contains('vfmt') { if os.executable().contains('vfmt') {
return return
@ -523,13 +538,13 @@ fn (mut v Builder) cc() {
v.build_thirdparty_obj_files() v.build_thirdparty_obj_files()
v.setup_output_name() 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 { if v.pref.build_mode == .build_module {
v.ccoptions.args << '-c' v.ccoptions.args << '-c'
} else if v.pref.use_cache { } else if v.pref.use_cache {
mut built_modules := []string{} mut built_modules := []string{}
builtin_obj_path := v.rebuild_cached_module(vexe, 'vlib/builtin') 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 { for ast_file in v.parsed_files {
is_test := ast_file.path.ends_with('_test.v') is_test := ast_file.path.ends_with('_test.v')
if is_test && ast_file.mod.name != 'main' { if is_test && ast_file.mod.name != 'main' {
@ -538,7 +553,7 @@ fn (mut v Builder) cc() {
break break
} }
obj_path := v.rebuild_cached_module(vexe, imp_path) obj_path := v.rebuild_cached_module(vexe, imp_path)
libs += ' ' + obj_path libs << obj_path
built_modules << ast_file.mod.name built_modules << ast_file.mod.name
} }
for imp_stmt in ast_file.imports { for imp_stmt in ast_file.imports {
@ -576,9 +591,10 @@ fn (mut v Builder) cc() {
break break
} }
obj_path := v.rebuild_cached_module(vexe, imp_path) obj_path := v.rebuild_cached_module(vexe, imp_path)
libs += ' ' + obj_path libs << obj_path
if obj_path.ends_with('vlib/ui.o') { 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 built_modules << imp
} }
@ -594,6 +610,7 @@ fn (mut v Builder) cc() {
} }
// //
all_args := v.ccoptions.all_args() all_args := v.ccoptions.all_args()
v.dump_c_options(all_args)
str_args := all_args.join(' ') str_args := all_args.join(' ')
// write args to response file // write args to response file
response_file := '${v.out_name_c}.rsp' response_file := '${v.out_name_c}.rsp'
@ -631,9 +648,9 @@ fn (mut v Builder) cc() {
v.show_c_compiler_output(res) v.show_c_compiler_output(res)
} }
os.chdir(original_pwd) os.chdir(original_pwd)
$if trace_use_cache ? { vcache.dlog('| Builder.' + @FN, '> v.pref.use_cache: $v.pref.use_cache | v.pref.retry_compilation: $v.pref.retry_compilation')
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, '> 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 res.exit_code != 0 {
if ccompiler.contains('tcc.exe') { if ccompiler.contains('tcc.exe') {
// a TCC problem? Retry with the system cc: // 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' 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() cflags := b.get_os_cflags()
cc_args += cflags.c_options_without_object_files() defines, others, libs := cflags.defines_others_libs()
cc_cmd := 'cc $cc_args' 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 { if b.pref.show_cc {
println(cc_cmd) println(cc_cmd)
} }
cc_res := os.exec(cc_cmd) or { cc_res := os.exec(cc_cmd) or { os.Result{
println('Cross compilation for Linux failed (first step, cc). Make sure you have clang installed.') exit_code: 1
verror(err) output: 'no `cc` command found'
return } }
}
if cc_res.exit_code != 0 { if cc_res.exit_code != 0 {
println('Cross compilation for Linux failed (first step, cc). Make sure you have clang installed.') println('Cross compilation for Linux failed (first step, cc). Make sure you have clang installed.')
verror(cc_res.output) 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', mut linker_args := ['-L $sysroot/usr/lib/x86_64-linux-gnu/', '--sysroot=$sysroot', '-v', '-o $b.pref.out_name',
'-dynamic-linker /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2', '$sysroot/crt1.o $sysroot/crti.o $obj_file', '-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()] '-lc', '-lcrypto', '-lssl', '-lpthread', '$sysroot/crtn.o']
linker_args << cflags.c_options_only_object_files()
// -ldl // -ldl
linker_args_str := linker_args.join(' ') linker_cmd := '$sysroot/ld.lld ' + linker_args.join(' ')
linker_cmd := '$sysroot/ld.lld $linker_args_str'
// s = s.replace('SYSROOT', sysroot) // TODO $ inter bug // s = s.replace('SYSROOT', sysroot) // TODO $ inter bug
// s = s.replace('-o hi', '-o ' + c.pref.out_name) // s = s.replace('-o hi', '-o ' + c.pref.out_name)
if b.pref.show_cc { if b.pref.show_cc {
@ -800,46 +827,48 @@ fn (mut c Builder) cc_windows_cross() {
if !c.pref.out_name.ends_with('.exe') { if !c.pref.out_name.ends_with('.exe') {
c.pref.out_name += '.exe' c.pref.out_name += '.exe'
} }
mut args := '' mut args := []string{}
args += ' $c.pref.cflags ' args << '$c.pref.cflags'
args += ' -o $c.pref.out_name -w -L. ' args << '-o $c.pref.out_name'
args << '-w -L.'
// //
cflags := c.get_os_cflags() cflags := c.get_os_cflags()
// -I flags // -I flags
args += if c.pref.ccompiler == 'msvc' { if c.pref.ccompiler == 'msvc' {
cflags.c_options_before_target_msvc() args << cflags.c_options_before_target_msvc()
} else { } else {
cflags.c_options_before_target() args << cflags.c_options_before_target()
} }
mut optimization_options := '' mut optimization_options := []string{}
mut debug_options := '' mut debug_options := []string{}
if c.pref.is_prod { if c.pref.is_prod {
if c.pref.ccompiler != 'msvc' { 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.is_debug {
if c.pref.ccompiler != 'msvc' { 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 { if false && c.pref.build_mode == .default_mode {
libs = '"$pref.default_module_path/vlib/builtin.o"' builtin_o := '"$pref.default_module_path/vlib/builtin.o"'
if !os.exists(libs) { libs << builtin_o
verror('`$libs` not found') if !os.exists(builtin_o) {
verror('$builtin_o not found')
} }
for imp in c.table.imports { 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: // add the thirdparty .o files, produced by all the #flag directives:
args += ' ' + cflags.c_options_only_object_files() + ' ' args << cflags.c_options_only_object_files()
args += ' $c.out_name_c ' args << c.out_name_c
args += if c.pref.ccompiler == 'msvc' { if c.pref.ccompiler == 'msvc' {
cflags.c_options_after_target_msvc() args << cflags.c_options_after_target_msvc()
} else { } else {
cflags.c_options_after_target() args << cflags.c_options_after_target()
} }
/* /*
winroot := '${pref.default_module_path}/winroot' winroot := '${pref.default_module_path}/winroot'
@ -860,7 +889,14 @@ fn (mut c Builder) cc_windows_cross() {
println(os.user_os()) println(os.user_os())
panic('your platform is not supported yet') 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' // 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 { if c.pref.is_verbose || c.pref.show_cc {
println(cmd) 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) { fn (mut v Builder) build_thirdparty_obj_file(path string, moduleflags []cflag.CFlag) {
obj_path := os.real_path(path) obj_path := os.real_path(path)
cfile := '${obj_path[..obj_path.len - 2]}.c' 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) opath := v.pref.cache_manager.postfix_with_key2cpath('.o', obj_path)
if os.exists(opath) { if os.exists(opath) {
return return
@ -928,8 +962,13 @@ fn (mut v Builder) build_thirdparty_obj_file(path string, moduleflags []cflag.CF
current_folder := os.getwd() current_folder := os.getwd()
os.chdir(os.dir(pref.vexe_path())) os.chdir(os.dir(pref.vexe_path()))
// //
cc_options := v.ccoptions.thirdparty_object_args([v.pref.third_party_option, btarget, '-o', mut all_options := []string{}
'"$opath"', '-c', '"$cfile"', atarget]).join(' ') 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' cmd := '$v.pref.ccompiler $cc_options'
$if trace_thirdparty_obj_files ? { $if trace_thirdparty_obj_files ? {
println('>>> build_thirdparty_obj_files cmd: $cmd') println('>>> build_thirdparty_obj_files cmd: $cmd')

View File

@ -37,37 +37,28 @@ pub fn (cf &CFlag) format() string {
} }
// TODO: implement msvc specific c_options_before_target and c_options_after_target ... // TODO: implement msvc specific c_options_before_target and c_options_after_target ...
pub fn (cflags []CFlag) c_options_before_target_msvc() string { pub fn (cflags []CFlag) c_options_before_target_msvc() []string {
return '' return []
} }
pub fn (cflags []CFlag) c_options_after_target_msvc() string { pub fn (cflags []CFlag) c_options_after_target_msvc() []string {
return '' return []
} }
pub fn (cflags []CFlag) c_options_before_target() string { pub fn (cflags []CFlag) c_options_before_target() []string {
// -I flags, optimization flags and so on defines, others, _ := cflags.defines_others_libs()
mut args := []string{} mut args := []string{}
for flag in cflags { args << defines
if flag.name != '-l' && !flag.value.ends_with('.o') { args << others
args << flag.format() return args
}
}
return args.join(' ')
} }
pub fn (cflags []CFlag) c_options_after_target() string { pub fn (cflags []CFlag) c_options_after_target() []string {
// -l flags (libs) _, _, libs := cflags.defines_others_libs()
mut args := []string{} return libs
for flag in cflags {
if flag.name == '-l' {
args << flag.format()
}
}
return args.join(' ')
} }
pub fn (cflags []CFlag) c_options_without_object_files() string { pub fn (cflags []CFlag) c_options_without_object_files() []string {
mut args := []string{} mut args := []string{}
for flag in cflags { for flag in cflags {
if flag.value.ends_with('.o') || flag.value.ends_with('.obj') { 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() 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{} mut args := []string{}
for flag in cflags { for flag in cflags {
if flag.value.ends_with('.o') || flag.value.ends_with('.obj') { if flag.value.ends_with('.o') || flag.value.ends_with('.obj') {
args << flag.format() 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
} }

View File

@ -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, // 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). // 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. // 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 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 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 // TODO Convert this into a []string
cflags string // Additional options which will be passed to the C compiler. 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' { '-show-c-output' {
res.show_c_output = true res.show_c_output = true
} }
'-dump-c-flags' {
res.dump_c_flags = cmdline.option(current_args, arg, '-')
i++
}
'-experimental' { '-experimental' {
res.experimental = true res.experimental = true
} }