From d82757abd9762cbe75c882f18ee00d9ebf1e3d56 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Wed, 12 Feb 2025 01:18:10 +0200 Subject: [PATCH] checker,cgen: add support for a `#postinclude` directive --- doc/docs.md | 22 ++++++++++----- vlib/v/checker/checker.v | 4 +-- vlib/v/gen/c/cgen.v | 55 ++++++++++++++++++++++---------------- vlib/v/gen/native/stmt.c.v | 2 +- 4 files changed, 51 insertions(+), 32 deletions(-) diff --git a/doc/docs.md b/doc/docs.md index 4a2ae15397d4d2..8bd30ba41b702f 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -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 diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index cce4e04cd8a213..26c656d6ec2d82 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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') { @@ -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 `` quoting', diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 3b5a734f37a9a6..47588306d83a25 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -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 @@ -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) @@ -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) } @@ -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()) @@ -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() @@ -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 == '' { + // 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 := '' @@ -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 == '' { - // 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 != '' { @@ -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 == '' { - // 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 @@ -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}') diff --git a/vlib/v/gen/native/stmt.c.v b/vlib/v/gen/native/stmt.c.v index 8022c9266043b2..910b0e970e10af 100644 --- a/vlib/v/gen/native/stmt.c.v +++ b/vlib/v/gen/native/stmt.c.v @@ -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) }