diff --git a/doc/docs.md b/doc/docs.md index e9dbc7db95..98ee2ceb99 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -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. diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 7e1bd43f78..a07cacdf49 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -731,8 +731,9 @@ pub: body_pos token.Position comments []Comment pub mut: - stmts []Stmt - scope &Scope + pkg_exist bool + stmts []Stmt + scope &Scope } pub struct UnsafeExpr { @@ -1439,6 +1440,8 @@ pub: // is_env bool env_pos token.Position + // + is_pkgconfig bool pub mut: sym TypeSymbol result_type Type diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index edae74653b..f2db021d5b 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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) } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index cd0fad711f..b21b6947b1 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -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 diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index ccfc8d8f72..1a45401bd2 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -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') diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index ebbebee3cd..2cd0f26a34 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -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') + p.check(.lpar) + spos := p.tok.position() if n == 'env' { - p.check(.lpar) - spos := p.tok.position() 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 { diff --git a/vlib/v/tests/comptime_if_pkgconfig_test.v b/vlib/v/tests/comptime_if_pkgconfig_test.v new file mode 100644 index 0000000000..77fd357135 --- /dev/null +++ b/vlib/v/tests/comptime_if_pkgconfig_test.v @@ -0,0 +1,10 @@ +fn test_comptime_pkgconfig() { + $if $pkgconfig('mysqlclient') { + assert true + return + } $else { + assert true + return + } + assert false +}