Skip to content

Commit

Permalink
checker,cgen: add support for a #postinclude directive
Browse files Browse the repository at this point in the history
  • Loading branch information
spytheman committed Feb 11, 2025
1 parent a4101f4 commit d82757a
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 32 deletions.
22 changes: 16 additions & 6 deletions doc/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -7949,26 +7949,36 @@ seamlessly across all platforms.
However, since the Windows header libraries use extremely generic names such as `Rectangle`,
this will cause a conflict if you wish to use C code that also has a name defined as `Rectangle`.
For very specific cases like this, we have `#preinclude`.
For very specific cases like this, V has `#preinclude` and `#postinclude` directives.
This will allow things to be configured before V adds in its built in libraries.
These directives allow things to be configured *before* V adds in its built in libraries,
and *after* all of the V code generation has completed (and thus all of the prototypes,
declarations and definitions are already present).
Example usage:
```v ignore
// This will include before built in libraries are used.
#preinclude "pre_include.h"
// This will include after built in libraries are used.
#include "include.h"
// This will include after all of the V code generation is complete,
// including the one for the main function of the project
#postinclude "post_include.h"
```
An example of what might be included in `pre_include.h`
can be [found here](https://github.com/irishgreencitrus/raylib.v/blob/main/include/pre.h)
This is an advanced feature, and will not be necessary
outside of very specific cases with C interop,
meaning it could cause more issues than it solves.
The `#postinclude` directive on the other hand is useful for allowing the integration
of frameworks like SDL3 or Sokol, that insist on having callbacks in your code, instead
of behaving like ordinary libraries, and allowing you to decide when to call them.
NOTE: these are advanced features, and will not be necessary outside of very specific cases
with C interop. Other than those, using them could cause more issues than it solves.
Consider it last resort!
Consider using them as a last resort!
## Other V Features
Expand Down
4 changes: 2 additions & 2 deletions vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -2612,7 +2612,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
return
}
match node.kind {
'include', 'insert', 'preinclude' {
'include', 'insert', 'preinclude', 'postinclude' {
original_flag := node.main
mut flag := node.main
if flag.contains('@VROOT') {
Expand Down Expand Up @@ -2655,7 +2655,7 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
node.main = d
}
flag_no_comment := flag.all_before('//').trim_space()
if node.kind == 'include' || node.kind == 'preinclude' {
if node.kind in ['include', 'preinclude', 'postinclude'] {
if !((flag_no_comment.starts_with('"') && flag_no_comment.ends_with('"'))
|| (flag_no_comment.starts_with('<') && flag_no_comment.ends_with('>'))) {
c.error('including C files should use either `"header_file.h"` or `<header_file.h>` quoting',
Expand Down
55 changes: 32 additions & 23 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ mut:
// line_nr int
cheaders strings.Builder
preincludes strings.Builder // allows includes to go before `definitions`
postincludes strings.Builder // allows includes to go after all the rest of the code generation
includes strings.Builder // all C #includes required by V modules
typedefs strings.Builder
enum_typedefs strings.Builder // enum types
Expand Down Expand Up @@ -300,6 +301,7 @@ pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) GenO
cheaders: strings.new_builder(15000)
includes: strings.new_builder(100)
preincludes: strings.new_builder(100)
postincludes: strings.new_builder(100)
typedefs: strings.new_builder(100)
enum_typedefs: strings.new_builder(100)
type_definitions: strings.new_builder(100)
Expand Down Expand Up @@ -387,6 +389,7 @@ pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) GenO
global_g.out.write(g.out) or { panic(err) }
global_g.cheaders.write(g.cheaders) or { panic(err) }
global_g.preincludes.write(g.preincludes) or { panic(err) }
global_g.postincludes.write(g.postincludes) or { panic(err) }
global_g.includes.write(g.includes) or { panic(err) }
global_g.typedefs.write(g.typedefs) or { panic(err) }
global_g.type_definitions.write(g.type_definitions) or { panic(err) }
Expand Down Expand Up @@ -567,7 +570,6 @@ pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) GenO
}
b.write_string2('\n// V comptime_definitions:\n', g.comptime_definitions.str())
b.write_string2('\n// V typedefs:\n', g.typedefs.str())
b.write_string2('\n // V preincludes:\n', g.preincludes.str())
b.write_string2('\n// V cheaders:', g.cheaders.str())
if g.pcs_declarations.len > 0 {
b.write_string2('\n// V profile counters:\n', g.pcs_declarations.str())
Expand Down Expand Up @@ -741,6 +743,7 @@ pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) GenO
extern_out_str := g.extern_out.str()
b.write_string(out_str)
b.writeln('// THE END.')
b.write_string2('\n // V postincludes:\n', g.postincludes.str())
util.timing_measure('cgen common')
$if trace_all_generic_fn_keys ? {
gkeys := g.table.fn_generic_types.keys()
Expand Down Expand Up @@ -5436,6 +5439,21 @@ fn (mut g Gen) gen_option_error(target_type ast.Type, expr ast.Expr) {
g.write(', .data={EMPTY_STRUCT_INITIALIZATION} }')
}

fn (mut g Gen) hash_stmt_guarded_include(node ast.HashStmt) string {
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 := get_guarded_include_text(node.main, missing_message)
if node.main == '<errno.h>' {
// fails with musl-gcc and msvc; but an unguarded include works:
guarded_include = '#include ${node.main}'
}
return guarded_include
}

fn (mut g Gen) hash_stmt(node ast.HashStmt) {
line_nr := node.pos.line_nr + 1
mut ct_condition := ''
Expand All @@ -5451,17 +5469,7 @@ fn (mut g Gen) hash_stmt(node ast.HashStmt) {
}
// #include etc
if node.kind == 'include' {
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 := get_guarded_include_text(node.main, missing_message)
if node.main == '<errno.h>' {
// fails with musl-gcc and msvc; but an unguarded include works:
guarded_include = '#include ${node.main}'
}
guarded_include := g.hash_stmt_guarded_include(node)
if node.main.contains('.m') {
g.definitions.writeln('')
if ct_condition != '' {
Expand All @@ -5486,17 +5494,7 @@ fn (mut g Gen) hash_stmt(node ast.HashStmt) {
}
}
} else if node.kind == 'preinclude' {
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 := get_guarded_include_text(node.main, missing_message)
if node.main == '<errno.h>' {
// fails with musl-gcc and msvc; but an unguarded include works:
guarded_include = '#include ${node.main}'
}
guarded_include := g.hash_stmt_guarded_include(node)
if node.main.contains('.m') {
// Might need to support '#preinclude' for .m files as well but for the moment
// this does the same as '#include' for them
Expand All @@ -5522,6 +5520,17 @@ fn (mut g Gen) hash_stmt(node ast.HashStmt) {
g.preincludes.writeln('#endif // \$if ${ct_condition}')
}
}
} else if node.kind == 'postinclude' {
guarded_include := g.hash_stmt_guarded_include(node)
g.postincludes.writeln('')
if ct_condition != '' {
g.postincludes.writeln('#if ${ct_condition}')
}
g.postincludes.writeln('// added by module `${node.mod}`, file: ${os.file_name(node.source_file)}:${line_nr}:')
g.postincludes.writeln(guarded_include)
if ct_condition != '' {
g.postincludes.writeln('#endif // \$if ${ct_condition}')
}
} else if node.kind == 'insert' {
if ct_condition != '' {
g.includes.writeln('#if ${ct_condition}')
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/gen/native/stmt.c.v
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
}

match node.kind {
'include', 'preinclude', 'define', 'insert' {
'include', 'preinclude', 'postinclude', 'define', 'insert' {
g.v_error('#${node.kind} is not supported with the native backend',
node.pos)
}
Expand Down

0 comments on commit d82757a

Please sign in to comment.