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
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.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')

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 ...
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
}

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,
// 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
}