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
 | ||||
| #flag darwin -I/usr/local/opt/openssl/include | ||||
| #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/err.h> | ||||
| 
 | ||||
|  |  | |||
|  | @ -628,8 +628,10 @@ pub: | |||
| 	mod  string | ||||
| 	pos  token.Position | ||||
| pub mut: | ||||
| 	val  string | ||||
| 	kind string | ||||
| 	val  string // example: 'include <openssl/rand.h> # please install openssl // comment'
 | ||||
| 	kind string // : 'include'
 | ||||
| 	main string // : '<openssl/rand.h>'
 | ||||
| 	msg  string // : 'please install openssl'
 | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ import v.pref | |||
| import term | ||||
| 
 | ||||
| const ( | ||||
| 	c_verror_message_marker = 'VERROR_MESSAGE ' | ||||
| 	c_error_info            = ' | ||||
| ================== | ||||
| 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) | ||||
| } | ||||
| 
 | ||||
| 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() { | ||||
| 	if os.executable().contains('vfmt') { | ||||
| 		return | ||||
|  | @ -491,9 +523,10 @@ fn (mut v Builder) cc() { | |||
| 		verror(err) | ||||
| 		return | ||||
| 	} | ||||
| 	if res.exit_code != 0 { | ||||
| 		// the command could not be found by the system
 | ||||
| 	diff := time.ticks() - ticks | ||||
| 	v.timing_message('C ${ccompiler:3}', diff) | ||||
| 	if res.exit_code == 127 { | ||||
| 		// the command could not be found by the system
 | ||||
| 		$if linux { | ||||
| 			// TCC problems on linux? Try GCC.
 | ||||
| 			if ccompiler.contains('tcc') { | ||||
|  | @ -506,34 +539,12 @@ fn (mut v Builder) cc() { | |||
| 			'-----------------------------------------------------------\n' + 'Probably your C compiler is missing. \n' + | ||||
| 			'Please reinstall it, or make it available in your PATH.\n\n' + missing_compiler_info()) | ||||
| 	} | ||||
| 		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)) | ||||
| 			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 | ||||
| 	v.post_process_c_compiler_output(res) | ||||
| 	// Print the C command
 | ||||
| 	if v.pref.is_verbose { | ||||
| 		println('$ccompiler took $diff ms') | ||||
| 		println('=========\n') | ||||
| 	} | ||||
| 	v.timing_message('C ${ccompiler:3}', diff) | ||||
| 	// Link it if we are cross compiling and need an executable
 | ||||
| 	/* | ||||
| 	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 | ||||
| 	v.timing_message('C msvc', diff) | ||||
| 	if res.exit_code != 0 { | ||||
| 		verror(res.output) | ||||
| 	} | ||||
| 	v.post_process_c_compiler_output(res) | ||||
| 	// println(res)
 | ||||
| 	// println('C OUTPUT:')
 | ||||
| 	// Always remove the object file - it is completely unnecessary
 | ||||
|  |  | |||
|  | @ -2459,7 +2459,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { | |||
| 		return | ||||
| 	} | ||||
| 	if node.kind == 'include' { | ||||
| 		mut flag := node.val[8..] | ||||
| 		mut flag := node.main | ||||
| 		if flag.contains('@VROOT') { | ||||
| 			vroot := util.resolve_vroot(flag, c.file.path) or { | ||||
| 				c.error(err, node.pos) | ||||
|  | @ -2475,7 +2475,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { | |||
| 		} | ||||
| 	} else if node.kind == 'flag' { | ||||
| 		// #flag linux -lm
 | ||||
| 		mut flag := node.val[5..] | ||||
| 		mut flag := node.main | ||||
| 		// expand `@VROOT` to its absolute path
 | ||||
| 		if flag.contains('@VROOT') { | ||||
| 			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 ? { | ||||
| 		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 { | ||||
| 		if tasks[i].path in m_skip_files { | ||||
| 			tasks[i].is_skipped = true | ||||
|  |  | |||
|  | @ -960,18 +960,41 @@ fn (mut g Gen) stmt(node ast.Stmt) { | |||
| 		ast.HashStmt { | ||||
| 			// #include etc
 | ||||
| 			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
 | ||||
| 					// available there
 | ||||
| 					g.definitions.writeln('// added by module `$node.mod`:') | ||||
| 					g.definitions.writeln('#$node.val') | ||||
| 					g.definitions.writeln(guarded_include) | ||||
| 				} else { | ||||
| 					g.includes.writeln('// added by module `$node.mod`:') | ||||
| 					g.includes.writeln('#$node.val') | ||||
| 					g.includes.writeln(guarded_include) | ||||
| 				} | ||||
| 			} else if node.kind == 'define' { | ||||
| 				g.includes.writeln('// defined by module `$node.mod`:') | ||||
| 				g.includes.writeln('#$node.val') | ||||
| 				g.includes.writeln('#define $node.main') | ||||
| 			} | ||||
| 		} | ||||
| 		ast.Import {} | ||||
|  |  | |||
|  | @ -22,11 +22,23 @@ fn (mut p Parser) hash() ast.HashStmt { | |||
| 	val := p.tok.lit | ||||
| 	kind := val.all_before(' ') | ||||
| 	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')
 | ||||
| 	return ast.HashStmt{ | ||||
| 		mod: p.mod | ||||
| 		val: val | ||||
| 		kind: kind | ||||
| 		main: main | ||||
| 		msg: msg | ||||
| 		pos: pos | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -70,7 +70,7 @@ pub fn (mut app App) settings(username string) vweb.Result { | |||
| } | ||||
| 
 | ||||
| ['/: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 { | ||||
| 		return app.vweb.not_found() | ||||
| 	} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue