# # Singe 3 # Copyright (C) 2006-2026 Scott Duensing # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public Licens # as published by the Free Software Foundation; either version 3 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # # The one place the zig version lives. Toolchain files include this and name a target; the # compiler, archiver and resource compiler are small wrapper scripts so every tool that wants a # single program name (FFmpeg's configure, OpenSSL's, CMake itself) gets one. # Pinned release, fetched into .builddir/toolchains on first use and checked against its hash. set(ZIG_VERSION 0.15.2) set(ZIG_SHA256 02aa270f183da276e5b5920b1dac44a63f1a49e55050ebde3aecc9eb82f93239) set(ZIG_NAME zig-x86_64-linux-${ZIG_VERSION}) set(ZIG_URL https://ziglang.org/download/${ZIG_VERSION}/${ZIG_NAME}.tar.xz) get_filename_component(ZIG_TOOLCHAINS ${CMAKE_CURRENT_LIST_DIR}/../../.builddir/toolchains ABSOLUTE) set(ZIG_ROOT ${ZIG_TOOLCHAINS}/${ZIG_NAME}) set(ZIG_EXECUTABLE ${ZIG_ROOT}/zig) if(NOT EXISTS ${ZIG_EXECUTABLE}) message(STATUS "Downloading zig ${ZIG_VERSION} from ${ZIG_URL}") file(MAKE_DIRECTORY ${ZIG_TOOLCHAINS}) file(DOWNLOAD ${ZIG_URL} ${ZIG_TOOLCHAINS}/${ZIG_NAME}.tar.xz EXPECTED_HASH SHA256=${ZIG_SHA256} STATUS status) list(GET status 0 code) if(NOT code EQUAL 0) message(FATAL_ERROR "zig download failed: ${status}") endif() file(ARCHIVE_EXTRACT INPUT ${ZIG_TOOLCHAINS}/${ZIG_NAME}.tar.xz DESTINATION ${ZIG_TOOLCHAINS}) file(REMOVE ${ZIG_TOOLCHAINS}/${ZIG_NAME}.tar.xz) if(NOT EXISTS ${ZIG_EXECUTABLE}) message(FATAL_ERROR "zig ${ZIG_VERSION} did not unpack to ${ZIG_ROOT}.") endif() endif() # Writes the wrapper scripts for a target into the build tree and points CMake at them. extra is # appended to every compiler invocation (a target's include and library directories). function(singeZigToolchain target extra) set(dir ${CMAKE_BINARY_DIR}/zig-${target}) file(MAKE_DIRECTORY ${dir}) # The same triple without its glibc version. zig cc refuses the versioned form when it is only # preprocessing -- "version '.2.28' in target triple 'x86_64-unknown-linux-gnu.2.28' is invalid" # -- and asking a compiler for its predefined macros that way is exactly how Meson identifies # one, so dav1d would not configure. Preprocessing generates no code, so the version it loses # there cannot reach anything built. string(REGEX REPLACE "\\.[0-9].*$" "" targetBase "${target}") # The compiler wrappers drop two flags FFmpeg's configure adds to every test link and zig's linker # driver refuses: binutils' --pic-executable ASLR workaround on Windows and Apple's # -dynamic,-search_paths_first on macOS. They also add # -fno-sanitize=undefined: zig cc instruments for UBSan by default and expects its runtime at # link time; a release build wants neither. # "zig cc -Wl,--version" prints zig's own banner rather than the linker's, and Meson reads that # answer to decide which linker it is driving. The linker really is LLD, and asking it directly # says so in the words Meson knows, so the probe is passed straight through to it. set(version "[[ $a == -Wl,--version ]] && exec \"${ZIG_EXECUTABLE}\" ld.lld --version;") set(filter "for a in \"$@\"; do ${version} [[ $a == -Wl,--pic-executable* || $a == -Wl,-dynamic* ]] || args+=(\"$a\"); [[ $a == -E ]] && target=\"-target ${targetBase}\"; done") # extra goes after the caller's arguments so the caller's -L directories are searched first. # ld.lld and objcopy are here for vendored projects that shell out to a linker or a strip of # their own rather than going through the compiler driver: Rockchip's MPP merges its archives # with "ld -r" and then strips the result, and without these it would reach for the host's. foreach(tool "cc;$target -fno-sanitize=undefined;${extra};yes" "c++;$target -fno-sanitize=undefined;${extra};yes" "ar;;;no" "ranlib;;;no" "rc;;;no" "ld.lld;;;no") list(GET tool 0 name) list(GET tool 1 args) list(GET tool 2 after) list(GET tool 3 compiler) if(compiler STREQUAL "yes") file(WRITE ${dir}/zig-${name} "#!/bin/bash\nargs=()\ntarget=\"-target ${target}\"\n${filter}\nexec \"${ZIG_EXECUTABLE}\" ${name} ${args} \"$\{args[@]}\" ${after}\n") else() file(WRITE ${dir}/zig-${name} "#!/bin/bash\nargs=()\n${filter}\nexec \"${ZIG_EXECUTABLE}\" ${name} ${args} \"$\{args[@]}\" ${after}\n") endif() file(CHMOD ${dir}/zig-${name} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) endforeach() set(CMAKE_C_COMPILER ${dir}/zig-cc CACHE FILEPATH "C compiler" FORCE) set(CMAKE_CXX_COMPILER ${dir}/zig-c++ CACHE FILEPATH "C++ compiler" FORCE) set(CMAKE_AR ${dir}/zig-ar CACHE FILEPATH "archiver" FORCE) set(CMAKE_RANLIB ${dir}/zig-ranlib CACHE FILEPATH "ranlib" FORCE) set(CMAKE_RC_COMPILER ${dir}/zig-rc CACHE FILEPATH "resource compiler" FORCE) set(CMAKE_LINKER ${dir}/zig-ld.lld CACHE FILEPATH "linker" FORCE) set(SINGE_ZIG_TARGET ${target} CACHE STRING "zig target triple" FORCE) endfunction()