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
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
You can also include C code directly in your V module.

View File

@ -731,6 +731,7 @@ pub:
body_pos token.Position
comments []Comment
pub mut:
pkg_exist bool
stmts []Stmt
scope &Scope
}
@ -1439,6 +1440,8 @@ pub:
//
is_env bool
env_pos token.Position
//
is_pkgconfig bool
pub mut:
sym TypeSymbol
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.is_comptime {
should_skip = c.comp_if_branch(branch.cond, branch.pos)
node.branches[i].pkg_exist = !should_skip
} else {
// check condition type is boolean
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
}
}
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 {
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')")
} else if node.is_env {
f.write("\$env('$node.args_var')")
} else if node.is_pkgconfig {
f.write("\$pkgconfig('$node.args_var')")
} else {
inner_args := if node.args_var != '' {
node.args_var

View File

@ -232,7 +232,7 @@ fn (mut g Gen) comp_if(node ast.IfExpr) {
} else {
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('')
}
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
*/
// 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 {
ast.BoolLiteral {
g.expr(cond)
@ -288,13 +288,13 @@ fn (mut g Gen) comp_if_cond(cond ast.Expr) bool {
}
ast.ParExpr {
g.write('(')
is_cond_true := g.comp_if_cond(cond.expr)
is_cond_true := g.comp_if_cond(cond.expr, pkg_exist)
g.write(')')
return is_cond_true
}
ast.PrefixExpr {
g.write(cond.op.str())
return g.comp_if_cond(cond.right)
return g.comp_if_cond(cond.right, pkg_exist)
}
ast.PostfixExpr {
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 {
match cond.op {
.and, .logical_or {
l := g.comp_if_cond(cond.left)
l := g.comp_if_cond(cond.left, pkg_exist)
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 }
}
.key_is, .not_is {
@ -381,6 +381,10 @@ fn (mut g Gen) comp_if_cond(cond ast.Expr) bool {
g.write('defined($ifdef)')
return true
}
ast.ComptimeCall {
g.write('$pkg_exist')
return true
}
else {
// should be unreachable, but just in case
g.write('1')

View File

@ -9,7 +9,7 @@ import v.pref
import v.token
const (
supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file']
supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file', 'pkgconfig']
)
// // #include, #flag, #v
@ -45,7 +45,7 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
}
p.check(.dollar)
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 {
n := p.check_name() // skip `vweb.html()` TODO
if n != 'vweb' {
@ -62,9 +62,9 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
is_embed_file := n == 'embed_file'
is_html := n == 'html'
// $env('ENV_VAR_NAME')
if n == 'env' {
p.check(.lpar)
spos := p.tok.position()
if n == 'env' {
s := p.tok.lit
p.check(.string)
p.check(.rpar)
@ -77,8 +77,19 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
pos: spos.extend(p.prev_tok.position())
}
}
p.check(.lpar)
spos := p.tok.position()
if n == 'pkgconfig' {
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 }
path_of_literal_string_param := literal_string_param.replace('/', os.path_separator)
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
}