Conditional compilation
The goal of this feature, is to tell V to not compile a function, and all its calls, in the final executable, if a provided custom flag is not passed.
V will still type check the function and all its calls, even if they will not be present in the final executable, due to the passed -d flags.
In order to see it in action, run the following example with v run example.v
once,
and then a second time with v -d trace_logs example.v
:
Conditional compilation, based on custom flags, can also be used to produce slightly different executables, which share the majority of the same code, but where some of the logic, is needed only some of the time, for example a network server/client program can be written like so:
To generate a client.exe
executable do: v -d as_client -o client.exe .
To generate a server.exe
executable do: v -d as_server -o server.exe .
Compile time pseudo variables
V also gives your code access to a set of pseudo string variables, that are substituted at compile time:
@FN
=> replaced with the name of the current V function.@METHOD
=> replaced with ReceiverType.MethodName.@MOD
=> replaced with the name of the current V module.@STRUCT
=> replaced with the name of the current V struct.@FILE
=> replaced with the absolute path of the V source file.@LINE
=> replaced with the V line number where it appears (as a string).@FILE_LINE
=> like@FILE:@LINE
, but the file part is a relative path.@LOCATION
=> file, line and name of the current type + method; suitable for logging.@COLUMN
=> replaced with the column where it appears (as a string).@VEXE
=> replaced with the path to the V compiler.@VEXEROOT
=> will be substituted with the folder, where the V executable is (as a string).@VHASH
=> replaced with the shortened commit hash of the V compiler (as a string).@VCURRENTHASH
=> Similar to@VHASH
, but changes when the compiler is recompiled on a different commit (after local modifications, or after using git bisect etc).@VMOD_FILE
=> replaced with the contents of the nearest v.mod file (as a string).@VMODHASH
=> is replaced by the shortened commit hash, derived from the .git directory next to the nearest v.mod file (as a string).@VMODROOT
=> will be substituted with the folder, where the nearest v.mod file is (as a string).@BUILD_DATE
=> replaced with the build date, for example '2024-09-13' .@BUILD_TIME
=> replaced with the build time, for example '12:32:07' .@BUILD_TIMESTAMP
=> replaced with the build timestamp, for example '1726219885' . Note:@BUILD_DATE
,@BUILD_TIME
,@BUILD_TIMESTAMP
represent times in the UTC timezone. By default, they are based on the current time of the compilation/build. They can be overriden by setting the environment variableSOURCE_DATE_EPOCH
. That is also useful while making releases, since you can use the equivalent of this in your build system/script:export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) ;
, and then use@BUILD_DATE
etc., inside your program, when you for example print your version information to users. See also https://reproducible-builds.org/docs/source-date-epoch/ .
The compile time pseudo variables allow you to do the following example, which is useful while debugging/logging/tracing your code:
Another example, is if you want to embed the version/name from v.mod inside your executable:
A program that prints its own source code (a quine):
A program that prints the time when it was built:
[!NOTE] you can have arbitrary source code in the file, without problems, since the full file will be embedded into the executable, produced by compiling it. Also note that printing is done with
println
, to not add another new line, missing in the source code.
Compile time reflection
$
is used as a prefix for compile time (also referred to as 'comptime') operations.
Having built-in JSON support is nice, but V also allows you to create efficient
serializers for any data format. V has compile time if
and for
constructs:
.fields
You can iterate over struct fields using .fields
, it also works with generic types
(e.g. T.fields
) and generic arguments (e.g. param.fields
where fn gen[T](param T) {
).
.values
You can read Enum values and their attributes.
.attributes
You can read Struct attributes.
.variants
You can read variant types from Sum type.
.methods
You can retrieve information about struct methods.
.params
You can retrieve information about struct method params.
See examples/compiletime/reflection.v
for a more complete example.
Compile time code
$if
condition
If you want an if
to be evaluated at compile time it must be prefixed with a $
sign.
Right now it can be used to detect an OS, compiler, platform or compilation options.
$if debug
is a special option like $if windows
or $if x32
, it's enabled if the program
is compiled with v -g
or v -cg
.
If you're using a custom ifdef, then you do need $if option ? {}
and compile withv -d option
.
Full list of builtin options:
OS | Compilers | Platforms | Other |
---|---|---|---|
windows , linux , macos |
gcc , tinyc |
amd64 , arm64 , aarch64 |
debug , prod , test |
darwin , ios , bsd |
clang , mingw |
i386 , arm32 |
js , glibc , prealloc |
freebsd , openbsd , netbsd |
msvc |
rv64 , rv32 |
no_bounds_checking , freestanding |
android , mach , dragonfly |
cplusplus |
x64 , x32 |
no_segfault_handler , no_backtrace |
gnu , hpux , haiku , qnx |
little_endian , big_endian |
no_main , fast_math , apk , threads |
|
solaris , termux |
js_node , js_browser , js_freestanding |
||
serenity , vinix , plan9 |
interpreter , es5 , profile , wasm32 |
||
wasm32_emscripten , wasm32_wasi |
|||
native , autofree |
$embed_file
V can embed arbitrary files into the executable with the $embed_file(<path>)
compile time call. Paths can be absolute or relative to the source file.
Note that by default, using $embed_file(file)
, will always embed the whole content
of the file, but you can modify that behaviour by passing: -d embed_only_metadata
when compiling your program. In that case, the file will not be embedded. Instead,
it will be loaded the first time your program calls embedded_file.data()
at runtime,
making it easier to change in external editor programs, without needing to recompile
your program.
Embedding a file inside your executable, will increase its size, but
it will make it more self contained and thus easier to distribute.
When that happens (the default), embedded_file.data()
will cause no IO,
and it will always return the same data.
$embed_file
supports compression of the embedded file when compiling with -prod
.
Currently only one compression type is supported: zlib
.
Note: compressing binary assets like png or zip files, usually will not gain you much, and in some cases may even take more space in the final executable, since they are already compressed.
$embed_file
returns
EmbedFileData
which could be used to obtain the file contents as string
or []u8
.
$tmpl
for embedding and parsing V template files
V has a simple template language for text and html templates, and they can easily
be embedded via $tmpl('path/to/template.txt')
:
1.txt:
name: @name
age: @age
numbers: @numbers
@for number in numbers
@number
@end
output:
name: Peter
age: 25
numbers: [1, 2, 3]
1
2
3
See more details
$env
V can bring in values at compile time from environment variables.
$env('ENV_VAR')
can also be used in top-level #flag
and #include
statements:
#flag linux -I $env('JAVA_HOME')/include
.
$d
V can bring in values at compile time from -d ident=value
flag defines, passed on
the command line to the compiler. You can also pass -d ident
, which will have the
same meaning as passing -d ident=true
.
To get the value in your code, use: $d('ident', default)
, where default
can be false
for booleans, 0
or 123
for i64 numbers, 0.0
or 113.0
for f64 numbers, 'a string'
for strings.
When a flag is not provided via the command line, $d()
will return the default
value provided as the second argument.
Running the above with v run .
will output:
V
1024
Running the above with v -d my_i64=4096 -d my_string="V rocks" run .
will output:
V rocks
4096
Here is an example of how to use the default values, which have to be pure literals:
$d('ident','value')
can also be used in top-level statements like #flag
and #include
:
#flag linux -I $d('my_include','/usr')/include
. The default value for $d
when used in these
statements should be literal string
s.
$d('ident', false)
can also be used inside $if $d('ident', false) {
statements,
granting you the ability to selectively turn on/off certain sections of code, at compile
time, without modifying your source code, or keeping different versions of it.
$compile_error
and $compile_warn
These two comptime functions are very useful for displaying custom errors/warnings during compile time.
Both receive as their only argument a string literal that contains the message to display:
Compile time types
Compile time types group multiple types into a general higher-level type. This is useful in
functions with generic parameters, where the input type must have a specific property, for example
the .len
attribute in arrays.
V supports the following compile time types:
$alias
=> matches Type aliases.$array
=> matches Arrays and Fixed Size Arrays.$array_dynamic
=> matches Arrays, but not Fixed Size Arrays.$array_fixed
=> matches Fixed Size Arrays, but not Arrays$enum
=> matches Enums.$float
=> matchesf32
,f64
and float literals.$function
=> matches Function Types.$int
=> matchesint
,i8
,i16
,i32
,i64
,u8
,u16
,u32
,u64
,isize
,usize
and integer literals.$interface
=> matches Interfaces.$map
=> matches Maps.$option
=> matches Option Types.$struct
=> matches Structs.$sumtype
=> matches Sum Types.$string
=> matches Strings.
Environment specific files
If a file has an environment-specific suffix, it will only be compiled for that environment.
.js.v
=> will be used only by the JS backend. These files can contain JS. code..c.v
=> will be used only by the C backend. These files can contain C. code..native.v
=> will be used only by V's native backend._nix.c.v
=> will be used only on Unix systems (non Windows)._${os}.c.v
=> will be used only on the specificos
system. For example,_windows.c.v
will be used only when compiling on Windows, or with-os windows
._default.c.v
=> will be used only if there is NOT a more specific platform file. For example, if you have bothfile_linux.c.v
andfile_default.c.v
, and you are compiling for linux, then onlyfile_linux.c.v
will be used, andfile_default.c.v
will be ignored.
Here is a more complete example:
main.v
:
main_default.c.v
:
main_linux.c.v
:
main_windows.c.v
:
With the example above:
when you compile for Windows, you will get
Hello windows
when you compile for Linux, you will get
Hello linux
when you compile for any other platform, you will get the non specific
Hello world
message._d_customflag.v
=> will be used only if you pass-d customflag
to V. That corresponds to$if customflag ? {}
, but for a whole file, not just a single block.customflag
should be a snake_case identifier, it can not contain arbitrary characters (only lower case latin letters + numbers +_
).Note
A combinatorial
_d_customflag_linux.c.v
postfix will not work. If you do need a custom flag file, that has platform dependent code, use the postfix_d_customflag.v
, and then use platform dependent compile time conditional blocks inside it, i.e.$if linux {}
etc._notd_customflag.v
=> similar to _d_customflag.v, but will be used only if you do NOT pass-d customflag
to V.
See also Cross Compilation.