cgen: produce cleaner error on missing C headers (with optional explanation) (#6637)
Implements support for `#include <openssl/rand.h> # Please install OpenSSL`.pull/6638/head
parent
aad122334b
commit
3c2202572b
|
@ -15,7 +15,7 @@ module openssl
|
||||||
// Brew
|
// Brew
|
||||||
#flag darwin -I/usr/local/opt/openssl/include
|
#flag darwin -I/usr/local/opt/openssl/include
|
||||||
#flag darwin -L/usr/local/opt/openssl/lib
|
#flag darwin -L/usr/local/opt/openssl/lib
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h> # Please install OpenSSL
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
|
||||||
|
|
|
@ -628,8 +628,10 @@ pub:
|
||||||
mod string
|
mod string
|
||||||
pos token.Position
|
pos token.Position
|
||||||
pub mut:
|
pub mut:
|
||||||
val string
|
val string // example: 'include <openssl/rand.h> # please install openssl // comment'
|
||||||
kind string
|
kind string // : 'include'
|
||||||
|
main string // : '<openssl/rand.h>'
|
||||||
|
msg string // : 'please install openssl'
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -10,6 +10,7 @@ import v.pref
|
||||||
import term
|
import term
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
c_verror_message_marker = 'VERROR_MESSAGE '
|
||||||
c_error_info = '
|
c_error_info = '
|
||||||
==================
|
==================
|
||||||
C error. This should never happen.
|
C error. This should never happen.
|
||||||
|
@ -75,6 +76,37 @@ fn (mut v Builder) find_win_cc() ? {
|
||||||
v.pref.ccompiler_type = pref.cc_from_string(v.pref.ccompiler)
|
v.pref.ccompiler_type = pref.cc_from_string(v.pref.ccompiler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut v Builder) post_process_c_compiler_output(res os.Result) {
|
||||||
|
if res.exit_code == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for emsg_marker in [c_verror_message_marker, 'error: include file '] {
|
||||||
|
if res.output.contains(emsg_marker) {
|
||||||
|
emessage := res.output.all_after(emsg_marker).all_before('\n').all_before('\r').trim_right('\r\n')
|
||||||
|
verror(emessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v.pref.is_debug {
|
||||||
|
eword := 'error:'
|
||||||
|
khighlight := if term.can_show_color_on_stdout() { term.red(eword) } else { eword }
|
||||||
|
println(res.output.trim_right('\r\n').replace(eword, khighlight))
|
||||||
|
} else {
|
||||||
|
if res.output.len < 30 {
|
||||||
|
println(res.output)
|
||||||
|
} else {
|
||||||
|
elines := error_context_lines(res.output, 'error:', 1, 12)
|
||||||
|
println('==================')
|
||||||
|
for eline in elines {
|
||||||
|
println(eline)
|
||||||
|
}
|
||||||
|
println('...')
|
||||||
|
println('==================')
|
||||||
|
println('(Use `v -cg` to print the entire error message)\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
verror(c_error_info)
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut v Builder) cc() {
|
fn (mut v Builder) cc() {
|
||||||
if os.executable().contains('vfmt') {
|
if os.executable().contains('vfmt') {
|
||||||
return
|
return
|
||||||
|
@ -491,9 +523,10 @@ fn (mut v Builder) cc() {
|
||||||
verror(err)
|
verror(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if res.exit_code != 0 {
|
diff := time.ticks() - ticks
|
||||||
// the command could not be found by the system
|
v.timing_message('C ${ccompiler:3}', diff)
|
||||||
if res.exit_code == 127 {
|
if res.exit_code == 127 {
|
||||||
|
// the command could not be found by the system
|
||||||
$if linux {
|
$if linux {
|
||||||
// TCC problems on linux? Try GCC.
|
// TCC problems on linux? Try GCC.
|
||||||
if ccompiler.contains('tcc') {
|
if ccompiler.contains('tcc') {
|
||||||
|
@ -506,34 +539,12 @@ fn (mut v Builder) cc() {
|
||||||
'-----------------------------------------------------------\n' + 'Probably your C compiler is missing. \n' +
|
'-----------------------------------------------------------\n' + 'Probably your C compiler is missing. \n' +
|
||||||
'Please reinstall it, or make it available in your PATH.\n\n' + missing_compiler_info())
|
'Please reinstall it, or make it available in your PATH.\n\n' + missing_compiler_info())
|
||||||
}
|
}
|
||||||
if v.pref.is_debug {
|
v.post_process_c_compiler_output(res)
|
||||||
eword := 'error:'
|
|
||||||
khighlight := if term.can_show_color_on_stdout() { term.red(eword) } else { eword }
|
|
||||||
println(res.output.trim_right('\r\n').replace(eword, khighlight))
|
|
||||||
verror(c_error_info)
|
|
||||||
} else {
|
|
||||||
if res.output.len < 30 {
|
|
||||||
println(res.output)
|
|
||||||
} else {
|
|
||||||
elines := error_context_lines(res.output, 'error:', 1, 12)
|
|
||||||
println('==================')
|
|
||||||
for eline in elines {
|
|
||||||
println(eline)
|
|
||||||
}
|
|
||||||
println('...')
|
|
||||||
println('==================')
|
|
||||||
println('(Use `v -cg` to print the entire error message)\n')
|
|
||||||
}
|
|
||||||
verror(c_error_info)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
diff := time.ticks() - ticks
|
|
||||||
// Print the C command
|
// Print the C command
|
||||||
if v.pref.is_verbose {
|
if v.pref.is_verbose {
|
||||||
println('$ccompiler took $diff ms')
|
println('$ccompiler took $diff ms')
|
||||||
println('=========\n')
|
println('=========\n')
|
||||||
}
|
}
|
||||||
v.timing_message('C ${ccompiler:3}', diff)
|
|
||||||
// Link it if we are cross compiling and need an executable
|
// Link it if we are cross compiling and need an executable
|
||||||
/*
|
/*
|
||||||
if v.os == .linux && !linux_host && v.pref.build_mode != .build {
|
if v.os == .linux && !linux_host && v.pref.build_mode != .build {
|
||||||
|
|
|
@ -308,9 +308,7 @@ pub fn (mut v Builder) cc_msvc() {
|
||||||
}
|
}
|
||||||
diff := time.ticks() - ticks
|
diff := time.ticks() - ticks
|
||||||
v.timing_message('C msvc', diff)
|
v.timing_message('C msvc', diff)
|
||||||
if res.exit_code != 0 {
|
v.post_process_c_compiler_output(res)
|
||||||
verror(res.output)
|
|
||||||
}
|
|
||||||
// println(res)
|
// println(res)
|
||||||
// println('C OUTPUT:')
|
// println('C OUTPUT:')
|
||||||
// Always remove the object file - it is completely unnecessary
|
// Always remove the object file - it is completely unnecessary
|
||||||
|
|
|
@ -2459,7 +2459,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if node.kind == 'include' {
|
if node.kind == 'include' {
|
||||||
mut flag := node.val[8..]
|
mut flag := node.main
|
||||||
if flag.contains('@VROOT') {
|
if flag.contains('@VROOT') {
|
||||||
vroot := util.resolve_vroot(flag, c.file.path) or {
|
vroot := util.resolve_vroot(flag, c.file.path) or {
|
||||||
c.error(err, node.pos)
|
c.error(err, node.pos)
|
||||||
|
@ -2475,7 +2475,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
||||||
}
|
}
|
||||||
} else if node.kind == 'flag' {
|
} else if node.kind == 'flag' {
|
||||||
// #flag linux -lm
|
// #flag linux -lm
|
||||||
mut flag := node.val[5..]
|
mut flag := node.main
|
||||||
// expand `@VROOT` to its absolute path
|
// expand `@VROOT` to its absolute path
|
||||||
if flag.contains('@VROOT') {
|
if flag.contains('@VROOT') {
|
||||||
flag = util.resolve_vroot(flag, c.file.path) or {
|
flag = util.resolve_vroot(flag, c.file.path) or {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
builder error: Header file <missing/folder/header1.h>, needed for module `main` was not found. Please install the corresponding development headers.
|
|
@ -0,0 +1,6 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
// The following header file is intentionally missing.
|
||||||
|
// The #include does not have the optional explanation part
|
||||||
|
// after a `#` sign:
|
||||||
|
#include <missing/folder/header1.h>
|
|
@ -0,0 +1 @@
|
||||||
|
builder error: Header file <missing/folder/header.h>, needed for module `main` was not found. Please install missing C library.
|
|
@ -0,0 +1,6 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
// The following header file is intentionally missing.
|
||||||
|
// The part after `#` is an explanation message, that V will
|
||||||
|
// show, when it is not found:
|
||||||
|
#include <missing/folder/header.h> # Please install missing C library
|
|
@ -85,6 +85,13 @@ fn (mut tasks []TaskDescription) run() {
|
||||||
$if noskip ? {
|
$if noskip ? {
|
||||||
m_skip_files = []
|
m_skip_files = []
|
||||||
}
|
}
|
||||||
|
$if tinyc {
|
||||||
|
// NB: tcc does not support __has_include, so the detection mechanism
|
||||||
|
// used for the other compilers does not work. It still provides a
|
||||||
|
// cleaner error message, than a generic C error, but without the explanation.
|
||||||
|
m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_1.vv'
|
||||||
|
m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv'
|
||||||
|
}
|
||||||
for i in 0 .. tasks.len {
|
for i in 0 .. tasks.len {
|
||||||
if tasks[i].path in m_skip_files {
|
if tasks[i].path in m_skip_files {
|
||||||
tasks[i].is_skipped = true
|
tasks[i].is_skipped = true
|
||||||
|
|
|
@ -960,18 +960,41 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
ast.HashStmt {
|
ast.HashStmt {
|
||||||
// #include etc
|
// #include etc
|
||||||
if node.kind == 'include' {
|
if node.kind == 'include' {
|
||||||
if node.val.contains('.m') {
|
mut missing_message := 'Header file $node.main, needed for module `$node.mod` was not found.'
|
||||||
|
if node.msg != '' {
|
||||||
|
missing_message += ' ${node.msg}.'
|
||||||
|
} else {
|
||||||
|
missing_message += ' Please install the corresponding development headers.'
|
||||||
|
}
|
||||||
|
mut guarded_include := '
|
||||||
|
|#if defined(__has_include)
|
||||||
|
|
|
||||||
|
|#if __has_include($node.main)
|
||||||
|
|#include $node.main
|
||||||
|
|#else
|
||||||
|
|#error VERROR_MESSAGE $missing_message
|
||||||
|
|#endif
|
||||||
|
|
|
||||||
|
|#else
|
||||||
|
|#include $node.main
|
||||||
|
|#endif
|
||||||
|
'.strip_margin()
|
||||||
|
if node.main == '<errno.h>' {
|
||||||
|
// fails with musl-gcc and msvc; but an unguarded include works:
|
||||||
|
guarded_include = '#include $node.main'
|
||||||
|
}
|
||||||
|
if node.main.contains('.m') {
|
||||||
// Objective C code import, include it after V types, so that e.g. `string` is
|
// Objective C code import, include it after V types, so that e.g. `string` is
|
||||||
// available there
|
// available there
|
||||||
g.definitions.writeln('// added by module `$node.mod`:')
|
g.definitions.writeln('// added by module `$node.mod`:')
|
||||||
g.definitions.writeln('#$node.val')
|
g.definitions.writeln(guarded_include)
|
||||||
} else {
|
} else {
|
||||||
g.includes.writeln('// added by module `$node.mod`:')
|
g.includes.writeln('// added by module `$node.mod`:')
|
||||||
g.includes.writeln('#$node.val')
|
g.includes.writeln(guarded_include)
|
||||||
}
|
}
|
||||||
} else if node.kind == 'define' {
|
} else if node.kind == 'define' {
|
||||||
g.includes.writeln('// defined by module `$node.mod`:')
|
g.includes.writeln('// defined by module `$node.mod`:')
|
||||||
g.includes.writeln('#$node.val')
|
g.includes.writeln('#define $node.main')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast.Import {}
|
ast.Import {}
|
||||||
|
|
|
@ -22,11 +22,23 @@ fn (mut p Parser) hash() ast.HashStmt {
|
||||||
val := p.tok.lit
|
val := p.tok.lit
|
||||||
kind := val.all_before(' ')
|
kind := val.all_before(' ')
|
||||||
p.next()
|
p.next()
|
||||||
|
mut main := ''
|
||||||
|
mut msg := ''
|
||||||
|
content := val.all_after('$kind ').all_before('//')
|
||||||
|
if content.contains(' #') {
|
||||||
|
main = content.all_before(' #').trim_space()
|
||||||
|
msg = content.all_after(' #').trim_space()
|
||||||
|
} else {
|
||||||
|
main = content.trim_space()
|
||||||
|
msg = ''
|
||||||
|
}
|
||||||
// p.trace('a.v', 'kind: ${kind:-10s} | pos: ${pos:-45s} | hash: $val')
|
// p.trace('a.v', 'kind: ${kind:-10s} | pos: ${pos:-45s} | hash: $val')
|
||||||
return ast.HashStmt{
|
return ast.HashStmt{
|
||||||
mod: p.mod
|
mod: p.mod
|
||||||
val: val
|
val: val
|
||||||
kind: kind
|
kind: kind
|
||||||
|
main: main
|
||||||
|
msg: msg
|
||||||
pos: pos
|
pos: pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ pub fn (mut app App) settings(username string) vweb.Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
['/:user/:repo/settings']
|
['/:user/:repo/settings']
|
||||||
pub fn (mut app App) user_repo_settings(username, repository string) vweb.Result {
|
pub fn (mut app App) user_repo_settings(username string, repository string) vweb.Result {
|
||||||
if username !in known_users {
|
if username !in known_users {
|
||||||
return app.vweb.not_found()
|
return app.vweb.not_found()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue