#!/usr/bin/env v import os import rand import strings /* This is a C bindings generator for netio module. This script uses the C preprocessor (`cc`) to extract macros using programs from `cincludes` and generates a V module file with constants. The C defines (`#define`) are parsed and used to generate a temporary V program (`vprogram`), which generates the final V module file. The goal is for the final V module file to contain only the values of C constants, rather than calling C code directly. */ const cincludes = { 'Linux': ' |#include |#include |#include ' 'FreeBSD': ' |#include |#include |#include ' } const typemap = { 'SocketType': ['SOCK_'] 'SocketLevel': ['SOL_', 'IPPROTO_'] 'SocketOption': ['SO_', 'IP_', 'IPV6_'] 'AddrFamily': ['AF_'] 'AddrInfoFlag': ['AI_'] 'NameInfoFlag': ['NI_'] 'MsgFlag': ['MSG_'] } const vprogram = r'module main %includes fn main() { // Generate the final .v file println("module netio\n") println("/*") println("\tThis file is generated by make.vsh, DO NOT EDIT.") println("*/\n") for k, v in cdefs { println("pub const ${k} = ${v.vtype}(${v.value})") } } struct CDef { vtype string value isize } ' const cc = 'cc' const cflags = os.getenv('CFLAGS') fn main() { cprogram := cincludes[os.uname().sysname] or { panic('platform is not yet supported') }.strip_margin().trim_space() cc_cmd := "echo '${cprogram}' | ${cc} -x c - -E -dM ${cflags} | sort -k 2 -V" cc_out := os.execute_or_panic(cc_cmd) mut buf := strings.new_builder(1024) buf.writeln(vprogram.replace('%includes', cprogram)) buf.writeln('const cdefs = {') for line in cc_out.output.split_into_lines() { for vtype, cdef_prefixes in typemap { for cdef_prefix in cdef_prefixes { if line.starts_with('#define ${cdef_prefix}') { cconst_name := line.fields()[1] if cconst_name.contains('(') { continue // skip non-constant macros } buf.writeln("'${cconst_name.to_lower_ascii()}': CDef{'${vtype}', C.${cconst_name}}") } } } } buf.write_byte(`}`) vfile_path := os.join_path_single(os.temp_dir(), rand.string(10) + '.c.v') os.write_file(vfile_path, buf.str())! os.execute_or_panic('v -Wfatal-errors -W -N ${vfile_path}') os.execute_or_panic('v fmt -w ${vfile_path}') v_out := os.execute_or_panic('${vfile_path.replace('.c.v', '')}') print(v_out.output) flush_stdout() os.rm(vfile_path)! }