Cross compilation
Cross compilation is supported for Windows, Linux and FreeBSD.
To cross compile your project simply run:
v -os windows .
or
v -os linux .
or
v -os freebsd .
[!NOTE] Cross-compiling a Windows binary on a Linux machine requires the GNU C compiler for MinGW-w64 (targeting Win64) to first be installed.
For Ubuntu/Debian-based distributions:
sudo apt install gcc-mingw-w64-x86-64
For Arch based distributions:
sudo pacman -S mingw-w64-gcc
(Cross compiling for macOS is temporarily not possible.)
If you don't have any C dependencies, that's all you need to do. This works even
when compiling GUI apps using the ui module or graphical apps using gg.
If you need a custom cross compiler, pass -cc <compiler> for one build, or set
VCROSS_COMPILER_NAME in your environment.
Example:
v -os linux -cc cosmocc .
You will need to install Clang, LLD linker, and download a zip file with libraries and include files for Windows and Linux. V will provide you with a link.
Portable C output (-os cross)
-os cross is not a platform. It asks for portable C,
i.e. C that is not tied to one OS, architecture or C compiler, so that a single
generated file can be compiled on any of them. It is how V's own bootstrap
snapshot vc/v.c is produced, and it only makes sense with -o file.c:
v -os cross -o /tmp/v.c cmd/v
cc -o v_from_c /tmp/v.c -lm -lpthread
The -cross flag asks for the same output, but as a modifier that combines with
an explicit target, which is how the Windows bootstrap snapshot is produced:
v -cross -os windows -cc msvc -o /tmp/v_win.c cmd/v
Either spelling also turns on the cross and no_backtrace custom defines, so
that the $if cross ? guards in the standard library select their portable path
instead of a platform syscall.
In this mode V does not decide a target-dependent $if while generating. It
keeps every branch and emits the condition as a C preprocessor guard, leaving
the choice to whichever C compiler builds the file:
becomes
Conditions that do not depend on the target - $if prealloc, $if debug, -d
values - are still resolved while generating, exactly as in an ordinary build.
#includes written inside a $if, or carrying a target prefix such as
#include linux <sys/timerfd.h>, are guarded the same way, and headers or C
sources shipped alongside your code are embedded into the output instead of
being referenced by a path that will not exist on the machine that compiles it.
What is not portable, and is therefore decided while generating, for the host V runs on:
- A
$ifused as an expression. Its branches may have different types - for exampleclosure_thunkinvlib/builtin/closureis a differently sized fixed array per architecture - which no guard around an expression can express. - A
$ifat file scope holding declarations. A function, type, constant or global cannot be wrapped in#ifby the backend, so only the host's branch is emitted. Directives (#include,#flag) written at file scope are kept from every branch and guarded, which is what makes the headers portable. - Environment specific files. A module split into
x_linux.c.vandx_darwin.c.vcontributes only the generating host's variant, so generate the portable C on the platform whose variants are the portable ones. - The pointer width. V's
int, the type layouts and the literal ranges are baked for the generating target, so the output carries a check that fails the build with a clear#errorwhen it is compiled for a different width. The output stays portable across targets of the same width.