Some languages have the tooling and features to make cross-compiling less painful. Rust, for instance, offers cross-compiling as semi-native functionality in its toolchain. But you’ll still need to bring in some additional bits to complete the experience—for example, a proper linker.
One big problem with cross-platform compiling is how asymmetrical it can be. If you’re a macOS user, it’s easy to set up and maintain Windows or Linux virtual machines on the Mac. If you use Linux or Windows, it’s harder to emulate macOS on those platforms. Not impossible, just more difficult—the biggest reason being the legal issues, as macOS’s EULA does not allow it to be used on non-Apple hardware. The easiest workaround (though hardly the cheapest) is to simply buy a separate Macintosh system and use that. Another option is to use tools like osxcross to perform cross-compilation on a Linux, FreeBSD, or OpenBSD system.
Another common option, one most in line with modern software delivery methods, is to use a system like GitHub Actions (via GitHub-hosted runners) or Azure Pipelines to build your software on any of their supported target platforms. (Both GitHub and Azure support macOS.) The downside is paying for the use of the service, but if you’re already invested in either platform, it’s often the most economical and least messy approach. Plus, it keeps the burden of system maintenance out of your hands.