checker: add pkgconfig to comptime if (#10692)

pull/10724/head^2
Louis Schmieder 2021-07-09 17:17:04 +02:00 committed by GitHub
parent aa5b609d95
commit eb96ad11d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 66 additions and 14 deletions

View File

@ -4017,6 +4017,18 @@ If no flags are passed it will add `--cflags` and `--libs`, both lines below do
The `.pc` files are looked up into a hardcoded list of default pkg-config paths, the user can add The `.pc` files are looked up into a hardcoded list of default pkg-config paths, the user can add
extra paths by using the `PKG_CONFIG_PATH` environment variable. Multiple modules can be passed. extra paths by using the `PKG_CONFIG_PATH` environment variable. Multiple modules can be passed.
To check the existance of a pkg-config use `$pkgconfig('pkg')` as a compile time if condition to
check if a pkg-config exists. If it exists the branch will be created. Use `$else` or `$else $if`
to handle other cases.
```v ignore
$if $pkgconfig('mysqlclient') {
#pkgconfig mysqlclient
} $else $if $pkgconfig('mariadb') {
#pkgconfig mariadb
}
```
### Including C code ### Including C code
You can also include C code directly in your V module. You can also include C code directly in your V module.

View File

@ -731,8 +731,9 @@ pub:
body_pos token.Position body_pos token.Position
comments []Comment comments []Comment
pub mut: pub mut:
stmts []Stmt pkg_exist bool
scope &Scope stmts []Stmt
scope &Scope
} }
pub struct UnsafeExpr { pub struct UnsafeExpr {
@ -1439,6 +1440,8 @@ pub:
// //
is_env bool is_env bool
env_pos token.Position env_pos token.Position
//
is_pkgconfig bool
pub mut: pub mut:
sym TypeSymbol sym TypeSymbol
result_type Type result_type Type

View File

@ -6185,6 +6185,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
if !node.has_else || i < node.branches.len - 1 { if !node.has_else || i < node.branches.len - 1 {
if node.is_comptime { if node.is_comptime {
should_skip = c.comp_if_branch(branch.cond, branch.pos) should_skip = c.comp_if_branch(branch.cond, branch.pos)
node.branches[i].pkg_exist = !should_skip
} else { } else {
// check condition type is boolean // check condition type is boolean
c.expected_type = ast.bool_type c.expected_type = ast.bool_type
@ -6514,6 +6515,15 @@ fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool {
return !(expr as ast.BoolLiteral).val return !(expr as ast.BoolLiteral).val
} }
} }
ast.ComptimeCall {
if cond.is_pkgconfig {
mut m := pkgconfig.main([cond.args_var]) or {
c.error(err.msg, cond.pos)
return true
}
m.run() or { return true }
}
}
else { else {
c.error('invalid `\$if` condition', pos) c.error('invalid `\$if` condition', pos)
} }

View File

@ -1764,6 +1764,8 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) {
f.write("\$embed_file('$node.embed_file.rpath')") f.write("\$embed_file('$node.embed_file.rpath')")
} else if node.is_env { } else if node.is_env {
f.write("\$env('$node.args_var')") f.write("\$env('$node.args_var')")
} else if node.is_pkgconfig {
f.write("\$pkgconfig('$node.args_var')")
} else { } else {
inner_args := if node.args_var != '' { inner_args := if node.args_var != '' {
node.args_var node.args_var

View File

@ -232,7 +232,7 @@ fn (mut g Gen) comp_if(node ast.IfExpr) {
} else { } else {
g.write('#elif ') g.write('#elif ')
} }
comp_if_stmts_skip = !g.comp_if_cond(branch.cond) comp_if_stmts_skip = !g.comp_if_cond(branch.cond, branch.pkg_exist)
g.writeln('') g.writeln('')
} }
expr_str := g.out.last_n(g.out.len - start_pos).trim_space() expr_str := g.out.last_n(g.out.len - start_pos).trim_space()
@ -280,7 +280,7 @@ fn (mut g Gen) comp_if(node ast.IfExpr) {
// returning `false` means the statements inside the $if can be skipped // returning `false` means the statements inside the $if can be skipped
*/ */
// returns the value of the bool comptime expression // returns the value of the bool comptime expression
fn (mut g Gen) comp_if_cond(cond ast.Expr) bool { fn (mut g Gen) comp_if_cond(cond ast.Expr, pkg_exist bool) bool {
match cond { match cond {
ast.BoolLiteral { ast.BoolLiteral {
g.expr(cond) g.expr(cond)
@ -288,13 +288,13 @@ fn (mut g Gen) comp_if_cond(cond ast.Expr) bool {
} }
ast.ParExpr { ast.ParExpr {
g.write('(') g.write('(')
is_cond_true := g.comp_if_cond(cond.expr) is_cond_true := g.comp_if_cond(cond.expr, pkg_exist)
g.write(')') g.write(')')
return is_cond_true return is_cond_true
} }
ast.PrefixExpr { ast.PrefixExpr {
g.write(cond.op.str()) g.write(cond.op.str())
return g.comp_if_cond(cond.right) return g.comp_if_cond(cond.right, pkg_exist)
} }
ast.PostfixExpr { ast.PostfixExpr {
ifdef := g.comp_if_to_ifdef((cond.expr as ast.Ident).name, true) or { ifdef := g.comp_if_to_ifdef((cond.expr as ast.Ident).name, true) or {
@ -307,9 +307,9 @@ fn (mut g Gen) comp_if_cond(cond ast.Expr) bool {
ast.InfixExpr { ast.InfixExpr {
match cond.op { match cond.op {
.and, .logical_or { .and, .logical_or {
l := g.comp_if_cond(cond.left) l := g.comp_if_cond(cond.left, pkg_exist)
g.write(' $cond.op ') g.write(' $cond.op ')
r := g.comp_if_cond(cond.right) r := g.comp_if_cond(cond.right, pkg_exist)
return if cond.op == .and { l && r } else { l || r } return if cond.op == .and { l && r } else { l || r }
} }
.key_is, .not_is { .key_is, .not_is {
@ -381,6 +381,10 @@ fn (mut g Gen) comp_if_cond(cond ast.Expr) bool {
g.write('defined($ifdef)') g.write('defined($ifdef)')
return true return true
} }
ast.ComptimeCall {
g.write('$pkg_exist')
return true
}
else { else {
// should be unreachable, but just in case // should be unreachable, but just in case
g.write('1') g.write('1')

View File

@ -9,7 +9,7 @@ import v.pref
import v.token import v.token
const ( const (
supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file'] supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file', 'pkgconfig']
) )
// // #include, #flag, #v // // #include, #flag, #v
@ -45,7 +45,7 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
} }
p.check(.dollar) p.check(.dollar)
start_pos := p.prev_tok.position() start_pos := p.prev_tok.position()
error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()` and `\$vweb.html()` comptime functions are supported right now' error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()` and `\$vweb.html()` comptime functions are supported right now'
if p.peek_tok.kind == .dot { if p.peek_tok.kind == .dot {
n := p.check_name() // skip `vweb.html()` TODO n := p.check_name() // skip `vweb.html()` TODO
if n != 'vweb' { if n != 'vweb' {
@ -62,9 +62,9 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
is_embed_file := n == 'embed_file' is_embed_file := n == 'embed_file'
is_html := n == 'html' is_html := n == 'html'
// $env('ENV_VAR_NAME') // $env('ENV_VAR_NAME')
p.check(.lpar)
spos := p.tok.position()
if n == 'env' { if n == 'env' {
p.check(.lpar)
spos := p.tok.position()
s := p.tok.lit s := p.tok.lit
p.check(.string) p.check(.string)
p.check(.rpar) p.check(.rpar)
@ -77,8 +77,19 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
pos: spos.extend(p.prev_tok.position()) pos: spos.extend(p.prev_tok.position())
} }
} }
p.check(.lpar) if n == 'pkgconfig' {
spos := p.tok.position() s := p.tok.lit
p.check(.string)
p.check(.rpar)
return ast.ComptimeCall{
scope: 0
method_name: n
args_var: s
is_pkgconfig: true
env_pos: spos
pos: spos.extend(p.prev_tok.position())
}
}
literal_string_param := if is_html { '' } else { p.tok.lit } literal_string_param := if is_html { '' } else { p.tok.lit }
path_of_literal_string_param := literal_string_param.replace('/', os.path_separator) path_of_literal_string_param := literal_string_param.replace('/', os.path_separator)
if !is_html { if !is_html {

View File

@ -0,0 +1,10 @@
fn test_comptime_pkgconfig() {
$if $pkgconfig('mysqlclient') {
assert true
return
} $else {
assert true
return
}
assert false
}