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 .
On macOS, -os linux with the default C compiler or Clang uses the bundled amd64 Linux
sysroot. V automatically enables $if cross_compile ? for this toolchain so modules such as
db.pg select the bundled Linux headers and compatibility sources.
[!NOTE] Cross-compiling a Windows binary on a Linux or macOS machine requires the GNU C compiler for MinGW-w64 (targeting Win64) to first be installed. Without
-archand-cc,-os windowsbuilds an amd64 executable withx86_64-w64-mingw32-gcc, also on an arm64 machine.
For Ubuntu/Debian-based distributions:
sudo apt install gcc-mingw-w64-x86-64
For Arch based distributions:
sudo pacman -S mingw-w64-gcc
For macOS with Homebrew:
brew install mingw-w64
(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. Where such a value has to follow the target, select it with statement-level$ifs in a function used under$if cross ?, asvlib/builtindoes formin_int,max_intand the closure thunks. - 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 literal ranges. They are checked for the generating target, so an
intliteral that only fits 64 bits is accepted, and truncated when the output is compiled for a 32-bit target. Usei64for such values.
The pointer width itself is left to the C compiler. V's int is spelled
__v_int, a typedef of i64 or i32 that the output defines from the
compiler's target, and the map callbacks for int and pointer keys and the
default thread stack size follow that width as well.
The compiler's own snapshot (v -cross cmd/v, which vc/v.c is) writes the libc
declarations it needs itself, with the layouts of 64-bit targets. On 32-bit
targets the C libraries disagree on those layouts (glibc and musl differ in
time_t, off_t and struct stat), so the snapshot also carries a variant that
includes the system headers. The C compiler picks it on 32-bit targets; compile
with -D__V_HEADERLESS_LIBC=0 to use it on a 64-bit one too.