Docs/Tools & reference

Cross compiling

Build an executable for another operating system or CPU from the machine you have.

On this page

A build normally produces an executable for the machine it runs on. With --target, the same source builds an executable for another operating system or CPU, without installing a toolchain for it: the linker and the libraries each platform needs ship with Valk.

Choose a target#

Name the platform the executable is for:

Terminal
valk build src -o app --target win-x64
Target Platform
linux-x64 Linux on x86-64
linux-arm64 Linux on 64-bit ARM, such as a Raspberry Pi or an AWS Graviton server
macos-x64 macOS on Intel
macos-arm64 macOS on Apple silicon
win-x64 Windows on x86-64

A Windows target adds .exe to the output name, so the command above writes app.exe. The executable cannot run on the build machine; copy it to a machine of that platform, or build in a CI job for each platform you ship.

Check code for every platform#

A lint compiles without linking, so it works for any target without extra setup:

Terminal
valk build src --lint --target macos-arm64

Run it for each target your project supports to catch code that only compiles on one platform.

Write platform-specific code#

OS and ARCH are set for the target being built, not for the build machine. OS is linux, macos or win; ARCH is x64 or arm64.

Valk
fn config_dir() String {
    #if OS == "win"
    return "C:/ProgramData/app"
    #elif OS == "macos"
    return "/Library/Application Support/app"
    #else
    return "/etc/app"
    #end
}

fn main() {
    println(config_dir())
}

Code in a branch that is not selected is never compiled, so it may call functions that exist on one platform only.

Add OpenSSL for the target#

A Valk release ships OpenSSL for its own platform only. valk.net, valk.http and valk.crypto link it, and so does every package built on them, such as a database driver or an HTTP client. Cross compiling such a program stops with a message that names the package to add:

Terminal
vman install github.com/valk-lang/openssl-win-x64 --libs

There is one package per target: openssl-linux-x64, openssl-linux-arm64, openssl-macos-x64, openssl-macos-arm64 and openssl-win-x64. Each holds only the libraries. --libs records that the project allows linking libraries from this dependency; without that flag the build refuses, because the libraries are code you never read. The entry in valk.json looks like this:

JSON
{
    "dependencies": {
        "openssl_win_x64": {
            "src": "github.com/valk-lang/openssl-win-x64",
            "version": "3.5.x",
            "libs": true
        }
    }
}

A program that does not use these namespaces needs nothing extra for any target.

A C library named with -l or a link declaration must be built for the target. Put it in libs/<target>/ next to valk.json, for example libs/win-x64/foo.lib, or pass its directory with -L. A build for the machine itself also searches the system's library directories; a cross build does not.

--static links the C runtime and the libraries into the executable, so a Linux build runs on machines that lack them. Valk's own releases are built this way.