Skip to content

Commit 38e4513

Browse files
committed
feat: migrate compiler wrappers from shell scripts to native C shims
1 parent 8a6ff30 commit 38e4513

1 file changed

Lines changed: 163 additions & 58 deletions

File tree

zig.toolchain.cmake

Lines changed: 163 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ find_program(ZIG_COMPILER zig)
1010
if(NOT ZIG_COMPILER)
1111
message(FATAL_ERROR "Zig Toolchain: Zig compiler not found. Please install Zig and ensure it is in your PATH.")
1212
endif()
13+
string(REPLACE "\\" "\\\\" _ZIG_COMPILER_EXE "${ZIG_COMPILER}")
14+
1315
execute_process(
1416
COMMAND "${ZIG_COMPILER}" version
1517
OUTPUT_VARIABLE ZIG_COMPILER_VERSION
@@ -48,104 +50,207 @@ if(NOT ZIG_TARGET)
4850

4951
set(ZIG_TARGET "${ZIG_ARCH}-${ZIG_OS}-${ZIG_ABI}")
5052
else()
51-
if(NOT ZIG_TARGET MATCHES "^([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)(-([a-zA-Z0-9_.]+))?$")
52-
message(FATAL_ERROR "Zig Toolchain: ZIG_TARGET is invalid. Please specify it manually using -DZIG_TARGET=<arch>-<os>[-<abi>]")
53+
if(NOT ZIG_TARGET MATCHES "^([^-]+)-([^-]+)(-(.+))?$")
54+
message(FATAL_ERROR "Zig Toolchain: ZIG_TARGET '${ZIG_TARGET}' is invalid. Expected format: <arch>-<os>[-<abi>]")
5355
endif()
5456
set(ZIG_ARCH ${CMAKE_MATCH_1})
5557
set(ZIG_OS ${CMAKE_MATCH_2})
56-
if(CMAKE_MATCH_4)
57-
set(ZIG_ABI ${CMAKE_MATCH_4})
58-
else()
59-
set(ZIG_ABI "none")
60-
endif()
6158
endif()
6259

6360
# Dummy version satisfies CMake's cross-compilation requirements without affecting Zig's behavior
6461
set(CMAKE_SYSTEM_VERSION 1)
6562
set(CMAKE_SYSTEM_PROCESSOR ${ZIG_ARCH})
63+
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
64+
6665
if(ZIG_OS STREQUAL "linux")
6766
set(CMAKE_SYSTEM_NAME "Linux")
6867
elseif(ZIG_OS STREQUAL "windows")
6968
set(CMAKE_SYSTEM_NAME "Windows")
7069
elseif(ZIG_OS STREQUAL "macos")
7170
set(CMAKE_SYSTEM_NAME "Darwin")
7271
else()
73-
message(FATAL_ERROR "Unknown OS: ${ZIG_OS}")
72+
set(CMAKE_SYSTEM_NAME ${ZIG_OS})
73+
message(WARNING "Unknown OS: ${ZIG_OS}")
7474
endif()
7575
message(STATUS "Zig Toolchain: v${ZIG_COMPILER_VERSION}${ZIG_TARGET}")
7676

77+
option(ZIG_USE_CCACHE "Enable ccache optimization for Zig toolchain" OFF)
78+
if(ZIG_USE_CCACHE)
79+
find_program(ZIG_CCACHE_EXECUTABLE ccache)
80+
if(ZIG_CCACHE_EXECUTABLE)
81+
message(STATUS "Zig Toolchain: ccache enabled at ${ZIG_CCACHE_EXECUTABLE}")
82+
else()
83+
message(WARNING "Zig Toolchain: ZIG_USE_CCACHE is ON but 'ccache' was not found in PATH.")
84+
endif()
85+
endif()
86+
if(ZIG_USE_CCACHE AND ZIG_CCACHE_EXECUTABLE)
87+
string(REPLACE "\\" "\\\\" _ZIG_CCACHE_EXE "${ZIG_CCACHE_EXECUTABLE}")
88+
else()
89+
set(_ZIG_CCACHE_EXE "")
90+
endif()
91+
7792
set(ZIG_MCPU "" CACHE STRING "Target CPU (e.g. 'baseline', 'native', 'cortex_a53'). See: zig targets")
7893
set(ZIG_MCPU_FEATURES "" CACHE STRING "CPU feature modifiers appended directly to -mcpu=<cpu>, e.g. '+avx2-sse4_1'. Each feature must start with + or -.")
7994
set(ZIG_COMPILER_FLAGS "" CACHE STRING "Additional compilation flags")
8095

81-
set(ZIG_WRAPPER_ARGS "")
96+
if(ZIG_MCPU_FEATURES AND NOT ZIG_MCPU)
97+
message(WARNING "Zig Toolchain: ZIG_MCPU_FEATURES='${ZIG_MCPU_FEATURES}' is set but ZIG_MCPU is empty.")
98+
endif()
99+
set(_ZIG_EXTRA_FLAGS_LIST "")
82100
if(ZIG_MCPU)
83-
string(APPEND ZIG_WRAPPER_ARGS " -mcpu=${ZIG_MCPU}")
84-
if(ZIG_MCPU_FEATURES)
85-
string(APPEND ZIG_WRAPPER_ARGS "${ZIG_MCPU_FEATURES}")
86-
endif()
101+
list(APPEND _ZIG_EXTRA_FLAGS_LIST "-mcpu=${ZIG_MCPU}${ZIG_MCPU_FEATURES}")
87102
endif()
88103
if(ZIG_COMPILER_FLAGS)
89-
string(APPEND ZIG_WRAPPER_ARGS " ${ZIG_COMPILER_FLAGS}")
104+
separate_arguments(_ZIG_COMPILER_FLAGS NATIVE_COMMAND "${ZIG_COMPILER_FLAGS}")
105+
list(APPEND _ZIG_EXTRA_FLAGS_LIST ${_ZIG_COMPILER_FLAGS})
90106
endif()
91-
string(STRIP "${ZIG_WRAPPER_ARGS}" ZIG_WRAPPER_ARGS)
92-
if(ZIG_WRAPPER_ARGS)
93-
message(STATUS "Zig Toolchain: Compiler flags → ${ZIG_WRAPPER_ARGS}")
107+
108+
set(_ZIG_COMPILER_INJECTED_FLAGS "-target" "${ZIG_TARGET}" ${_ZIG_EXTRA_FLAGS_LIST})
109+
set(_ZIG_INJECTED_C_CODE "")
110+
foreach(_arg IN LISTS _ZIG_COMPILER_INJECTED_FLAGS)
111+
string(REPLACE "\\" "\\\\" _arg_escaped "${_arg}")
112+
string(REPLACE "\"" "\\\"" _arg_escaped "${_arg_escaped}")
113+
string(APPEND _ZIG_INJECTED_C_CODE "\"${_arg_escaped}\", ")
114+
endforeach()
115+
116+
if(CMAKE_HOST_WIN32)
117+
set(_ZIG_WRAPPER_EXT ".exe")
118+
set(_ZIG_IS_WIN32 1)
119+
else()
120+
set(_ZIG_WRAPPER_EXT "")
121+
set(_ZIG_IS_WIN32 0)
94122
endif()
95123

96-
option(ZIG_USE_CCACHE "Enable ccache optimization for Zig toolchain" OFF)
97-
set(ZIG_CC_PREFIX "")
98-
if(ZIG_USE_CCACHE)
99-
find_program(CCACHE_TOOL ccache)
100-
if(CCACHE_TOOL)
101-
set(ZIG_CC_PREFIX "\"${CCACHE_TOOL}\" ")
102-
message(STATUS "Zig Toolchain: ccache enabled at ${CCACHE_TOOL}")
103-
else()
104-
message(WARNING "Zig Toolchain: ZIG_USE_CCACHE is ON but 'ccache' was not found in PATH.")
105-
endif()
124+
if(CMAKE_HOST_APPLE)
125+
set(_ZIG_SHIM_STRIP_FLAG "")
126+
else()
127+
set(_ZIG_SHIM_STRIP_FLAG "-s")
106128
endif()
107129

108-
# Generate wrapper scripts to inject -target flag and ccache prefix transparently
109130
set(ZIG_SHIMS_DIR "${CMAKE_BINARY_DIR}/.zig-shims")
110131
file(MAKE_DIRECTORY "${ZIG_SHIMS_DIR}")
111-
if(CMAKE_HOST_WIN32)
112-
set(EXT ".cmd")
113-
set(HEADER "@echo off")
114-
set(ARGS "%*")
115-
else()
116-
set(EXT "")
117-
set(HEADER "#!/bin/sh")
118-
set(ARGS "\"$@\"")
119-
endif()
120132

121-
file(WRITE "${ZIG_SHIMS_DIR}/zig-cc${EXT}" "${HEADER}\n${ZIG_CC_PREFIX}\"${ZIG_COMPILER}\" cc -target ${ZIG_TARGET} ${ZIG_WRAPPER_ARGS} ${ARGS}\n")
122-
file(WRITE "${ZIG_SHIMS_DIR}/zig-c++${EXT}" "${HEADER}\n${ZIG_CC_PREFIX}\"${ZIG_COMPILER}\" c++ -target ${ZIG_TARGET} ${ZIG_WRAPPER_ARGS} ${ARGS}\n")
123-
file(WRITE "${ZIG_SHIMS_DIR}/zig-ar${EXT}" "${HEADER}\n\"${ZIG_COMPILER}\" ar ${ARGS}\n")
124-
file(WRITE "${ZIG_SHIMS_DIR}/zig-rc${EXT}" "${HEADER}\n\"${ZIG_COMPILER}\" rc ${ARGS}\n")
125-
file(WRITE "${ZIG_SHIMS_DIR}/zig-ranlib${EXT}" "${HEADER}\n\"${ZIG_COMPILER}\" ranlib ${ARGS}\n")
126-
if(NOT CMAKE_HOST_WIN32)
127-
execute_process(COMMAND chmod +x
128-
"${ZIG_SHIMS_DIR}/zig-cc${EXT}"
129-
"${ZIG_SHIMS_DIR}/zig-c++${EXT}"
130-
"${ZIG_SHIMS_DIR}/zig-ar${EXT}"
131-
"${ZIG_SHIMS_DIR}/zig-rc${EXT}"
132-
"${ZIG_SHIMS_DIR}/zig-ranlib${EXT}"
133+
function(generate_shim_binary TOOL_NAME ZIG_SUBCOMMAND INJECT_FLAGS USE_CCACHE)
134+
set(WRAPPER_SOURCE "${ZIG_SHIMS_DIR}/${TOOL_NAME}.c")
135+
set(WRAPPER_EXE "${ZIG_SHIMS_DIR}/${TOOL_NAME}${_ZIG_WRAPPER_EXT}")
136+
set(WRAPPER_HASH "${ZIG_SHIMS_DIR}/${TOOL_NAME}.hash")
137+
138+
if(${INJECT_FLAGS})
139+
set(WRAPPER_INJECT_CODE "${_ZIG_INJECTED_C_CODE}")
140+
else()
141+
set(WRAPPER_INJECT_CODE "")
142+
endif()
143+
144+
if(${USE_CCACHE} AND _ZIG_CCACHE_EXE)
145+
set(WRAPPER_USE_CCACHE 1)
146+
else()
147+
set(WRAPPER_USE_CCACHE 0)
148+
endif()
149+
150+
set(C_CODE_CONTENT "
151+
#include <stddef.h>
152+
#include <stdio.h>
153+
#include <stdlib.h>
154+
#include <string.h>
155+
156+
#if ${_ZIG_IS_WIN32}
157+
#include <process.h>
158+
#else
159+
#include <unistd.h>
160+
#endif
161+
162+
#define ZIG_EXE \"${_ZIG_COMPILER_EXE}\"
163+
#define ZIG_CMD \"${ZIG_SUBCOMMAND}\"
164+
#define CCACHE_EXE \"${_ZIG_CCACHE_EXE}\"
165+
#define USE_CCACHE ${WRAPPER_USE_CCACHE}
166+
167+
int main(int argc, char** argv) {
168+
const char* ijlist[] = {${WRAPPER_INJECT_CODE} NULL};
169+
const int ijcount = (int)(sizeof(ijlist) / sizeof(char*)) - 1;
170+
const int exargc = USE_CCACHE + 2 + ijcount + (argc - 1);
171+
const char** exargv = (const char**)malloc(sizeof(char*) * (size_t)(exargc + 1));
172+
173+
if (!exargv) {
174+
fprintf(stderr, \"malloc failed\\n\");
175+
return 1;
176+
}
177+
178+
const char **p = exargv;
179+
if (USE_CCACHE) { *p++ = CCACHE_EXE; }
180+
*p++ = ZIG_EXE;
181+
*p++ = ZIG_CMD;
182+
for (int i = 0; i < ijcount; ++i) { *p++ = ijlist[i]; }
183+
for (int i = 1; i < argc; ++i) { *p++ = argv[i]; }
184+
*p = NULL;
185+
186+
#if ${_ZIG_IS_WIN32}
187+
intptr_t ret = _spawnvp(_P_WAIT, exargv[0], (const char* const*)exargv);
188+
free(exargv);
189+
return (int)ret;
190+
#else
191+
execvp(exargv[0], (char* const*)exargv);
192+
perror(\"execvp failed\");
193+
free(exargv);
194+
return 1;
195+
#endif
196+
}")
197+
198+
string(MD5 _new_hash "${C_CODE_CONTENT}")
199+
if(EXISTS "${WRAPPER_HASH}" AND EXISTS "${WRAPPER_EXE}")
200+
file(READ "${WRAPPER_HASH}" _old_hash)
201+
string(STRIP "${_old_hash}" _old_hash)
202+
if(_old_hash STREQUAL _new_hash)
203+
return()
204+
endif()
205+
endif()
206+
207+
file(WRITE "${WRAPPER_SOURCE}" "${C_CODE_CONTENT}")
208+
message(STATUS "Zig Toolchain: Compiling native wrapper for '${TOOL_NAME}'...")
209+
210+
execute_process(
211+
COMMAND "${ZIG_COMPILER}" cc -O2 ${_ZIG_SHIM_STRIP_FLAG} "${WRAPPER_SOURCE}" -o "${WRAPPER_EXE}"
212+
RESULT_VARIABLE COMPILE_RESULT
213+
ERROR_VARIABLE COMPILE_STDERR
214+
OUTPUT_QUIET
133215
)
134-
endif()
216+
if(NOT COMPILE_RESULT EQUAL 0)
217+
message(FATAL_ERROR
218+
"Zig Toolchain: Failed to compile wrapper executable for '${TOOL_NAME}'.\n"
219+
"${COMPILE_STDERR}")
220+
endif()
221+
222+
if(NOT CMAKE_HOST_WIN32)
223+
execute_process(COMMAND chmod +x "${WRAPPER_EXE}" OUTPUT_QUIET)
224+
endif()
225+
226+
file(WRITE "${WRAPPER_HASH}" "${_new_hash}")
227+
endfunction()
228+
229+
generate_shim_binary("zig-cc" "cc" TRUE TRUE)
230+
generate_shim_binary("zig-c++" "c++" TRUE TRUE)
231+
generate_shim_binary("zig-ar" "ar" FALSE FALSE)
232+
generate_shim_binary("zig-rc" "rc" FALSE FALSE)
233+
generate_shim_binary("zig-ranlib" "ranlib" FALSE FALSE)
234+
generate_shim_binary("zig-nm" "nm" FALSE FALSE)
235+
generate_shim_binary("zig-objcopy" "objcopy" FALSE FALSE)
236+
generate_shim_binary("zig-strip" "strip" FALSE FALSE)
135237

136-
set(CMAKE_C_COMPILER "${ZIG_SHIMS_DIR}/zig-cc${EXT}")
137-
set(CMAKE_CXX_COMPILER "${ZIG_SHIMS_DIR}/zig-c++${EXT}")
138-
set(CMAKE_AR "${ZIG_SHIMS_DIR}/zig-ar${EXT}" CACHE FILEPATH "Archiver" FORCE)
139-
set(CMAKE_RANLIB "${ZIG_SHIMS_DIR}/zig-ranlib${EXT}" CACHE FILEPATH "Ranlib" FORCE)
238+
set(CMAKE_C_COMPILER "${ZIG_SHIMS_DIR}/zig-cc${_ZIG_WRAPPER_EXT}")
239+
set(CMAKE_CXX_COMPILER "${ZIG_SHIMS_DIR}/zig-c++${_ZIG_WRAPPER_EXT}")
240+
set(CMAKE_AR "${ZIG_SHIMS_DIR}/zig-ar${_ZIG_WRAPPER_EXT}" CACHE FILEPATH "Archiver" FORCE)
241+
set(CMAKE_RANLIB "${ZIG_SHIMS_DIR}/zig-ranlib${_ZIG_WRAPPER_EXT}" CACHE FILEPATH "Ranlib" FORCE)
242+
set(CMAKE_NM "${ZIG_SHIMS_DIR}/zig-nm${_ZIG_WRAPPER_EXT}" CACHE FILEPATH "NM" FORCE)
243+
set(CMAKE_OBJCOPY "${ZIG_SHIMS_DIR}/zig-objcopy${_ZIG_WRAPPER_EXT}" CACHE FILEPATH "Objcopy" FORCE)
244+
set(CMAKE_STRIP "${ZIG_SHIMS_DIR}/zig-strip${_ZIG_WRAPPER_EXT}" CACHE FILEPATH "Strip" FORCE)
140245

141246
if(CMAKE_HOST_WIN32)
142-
# unsupported linker arg: --dependency-file
143-
set(CMAKE_C_LINKER_DEPFILE_SUPPORTED FALSE)
144-
set(CMAKE_CXX_LINKER_DEPFILE_SUPPORTED FALSE)
247+
# unsupported linker arg: --dependency-file. see https://github.com/ziglang/zig/issues/22213
248+
set(CMAKE_C_LINKER_DEPFILE_SUPPORTED FALSE)
249+
set(CMAKE_CXX_LINKER_DEPFILE_SUPPORTED FALSE)
145250
endif()
146251

147252
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
148-
set(CMAKE_RC_COMPILER "${ZIG_SHIMS_DIR}/zig-rc${EXT}" CACHE FILEPATH "Resource Compiler" FORCE)
253+
set(CMAKE_RC_COMPILER "${ZIG_SHIMS_DIR}/zig-rc${_ZIG_WRAPPER_EXT}" CACHE FILEPATH "Resource Compiler" FORCE)
149254
# Explicitly specify MSVC syntax because zig rc only supports this format
150255
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> /fo <OBJECT> <SOURCE>")
151256
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")

0 commit comments

Comments
 (0)