@@ -123,6 +123,36 @@ declare -g -a CCACHE_PASSTHROUGH_VARS=(
123123 CCACHE_PCH_EXTSUM
124124)
125125
126+ # Format host:port, wrapping IPv6 addresses in brackets for URL compatibility (RFC 2732)
127+ function ccache_format_host_port() {
128+ local host=" $1 " port=" $2 "
129+ if [[ " ${host} " == * :* ]]; then
130+ echo " [${host} ]:${port} "
131+ else
132+ echo " ${host} :${port} "
133+ fi
134+ }
135+
136+ # Extract hostname from CCACHE_REMOTE_STORAGE URL (strips scheme, userinfo, port, path)
137+ function ccache_extract_url_host() {
138+ local url=" $1 "
139+ local after_scheme=" ${url#*:// } "
140+ # Strip userinfo if present
141+ if [[ " ${after_scheme} " == * @* ]]; then
142+ after_scheme=" ${after_scheme##*@ } "
143+ fi
144+ local host
145+ # Handle bracketed IPv6: [addr]:port
146+ if [[ " ${after_scheme} " == \[ * ]]; then
147+ host=" ${after_scheme# \[ } "
148+ host=" ${host%% \] * } "
149+ else
150+ # Strip port, path, and ccache attributes
151+ host=" ${after_scheme%% [:\/|]* } "
152+ fi
153+ echo " ${host} "
154+ }
155+
126156# Discover ccache remote storage via DNS-SD (mDNS/Avahi) or DNS SRV records.
127157# Looks for _ccache._tcp services with TXT records: type=http|redis, path=/...
128158# Prefers Redis over HTTP when multiple services are found.
@@ -136,6 +166,7 @@ function ccache_discover_remote_storage() {
136166 # Parse resolved lines: =;IFACE;PROTO;NAME;TYPE;DOMAIN;HOSTNAME;ADDRESS;PORT;"txt"...
137167 # Prefer IPv4 (proto=IPv4), prefer type=redis over type=http
138168 local redis_url=" " http_url=" "
169+ local redis_host=" " redis_host_ip=" " http_host=" " http_host_ip=" "
139170 while IFS=' ;' read -r status iface proto name stype domain hostname address port txt_rest; do
140171 [[ " ${status} " == " =" && " ${proto} " == " IPv4" ]] || continue
141172 local svc_type=" " svc_path=" "
@@ -146,19 +177,31 @@ function ccache_discover_remote_storage() {
146177 if [[ " ${txt_rest} " =~ \" path= ([^\" ]+)\" ]]; then
147178 svc_path=" ${BASH_REMATCH[1]} "
148179 fi
180+ # Use hostname for URL (Docker --add-host resolves it), fall back to address
181+ local svc_host=" ${hostname% .local} "
182+ svc_host=" ${svc_host% .} "
183+ [[ -z " ${svc_host} " ]] && svc_host=" ${address} "
149184 if [[ " ${svc_type} " == " redis" ]]; then
150- redis_url=" redis://${address} :${port} |connect-timeout=${CCACHE_REDIS_CONNECT_TIMEOUT} "
185+ redis_url=" redis://${svc_host} :${port} |connect-timeout=${CCACHE_REDIS_CONNECT_TIMEOUT} "
186+ redis_host=" ${svc_host} "
187+ redis_host_ip=" ${address} "
151188 elif [[ " ${svc_type} " == " http" ]]; then
152- http_url=" http://${address} :${port}${svc_path} "
189+ http_url=" http://${svc_host} :${port}${svc_path} "
190+ http_host=" ${svc_host} "
191+ http_host_ip=" ${address} "
153192 fi
154193 done <<< " ${browse_output}"
155- # Redis preferred over HTTP
194+ # Redis preferred over HTTP; set hostname->IP mapping for Docker --add-host
156195 if [[ -n " ${redis_url} " ]]; then
157196 export CCACHE_REMOTE_STORAGE=" ${redis_url} "
197+ declare -g CCACHE_REMOTE_HOST=" ${redis_host} "
198+ declare -g CCACHE_REMOTE_HOST_IP=" ${redis_host_ip} "
158199 display_alert " DNS-SD: discovered Redis ccache" " $( ccache_mask_storage_url " ${CCACHE_REMOTE_STORAGE} " ) " " info"
159200 return 0
160201 elif [[ -n " ${http_url} " ]]; then
161202 export CCACHE_REMOTE_STORAGE=" ${http_url} "
203+ declare -g CCACHE_REMOTE_HOST=" ${http_host} "
204+ declare -g CCACHE_REMOTE_HOST_IP=" ${http_host_ip} "
162205 display_alert " DNS-SD: discovered HTTP ccache" " $( ccache_mask_storage_url " ${CCACHE_REMOTE_STORAGE} " ) " " info"
163206 return 0
164207 fi
@@ -184,10 +227,12 @@ function ccache_discover_remote_storage() {
184227 if [[ " ${txt_output} " =~ path= ([^\" [:space:]]+) ]]; then
185228 svc_path=" ${BASH_REMATCH[1]} "
186229 fi
230+ local host_port
231+ host_port=$( ccache_format_host_port " ${srv_host} " " ${srv_port} " )
187232 if [[ " ${svc_type} " == " http" ]]; then
188- export CCACHE_REMOTE_STORAGE=" http://${srv_host} : ${srv_port }${svc_path}"
233+ export CCACHE_REMOTE_STORAGE=" http://${host_port }${svc_path} "
189234 else
190- export CCACHE_REMOTE_STORAGE=" redis://${srv_host} : ${srv_port } |connect-timeout=${CCACHE_REDIS_CONNECT_TIMEOUT} "
235+ export CCACHE_REMOTE_STORAGE=" redis://${host_port } |connect-timeout=${CCACHE_REDIS_CONNECT_TIMEOUT} "
191236 fi
192237 display_alert " DNS SRV: discovered ccache" " $( ccache_mask_storage_url " ${CCACHE_REMOTE_STORAGE} " ) " " info"
193238 return 0
@@ -199,7 +244,9 @@ function ccache_discover_remote_storage() {
199244 local ccache_ip
200245 ccache_ip=$( getent hosts ccache.local 2> /dev/null | awk ' {print $1; exit}' || true)
201246 if [[ -n " ${ccache_ip} " ]]; then
202- export CCACHE_REMOTE_STORAGE=" redis://${ccache_ip} :6379|connect-timeout=${CCACHE_REDIS_CONNECT_TIMEOUT} "
247+ local host_port
248+ host_port=$( ccache_format_host_port " ${ccache_ip} " " 6379" )
249+ export CCACHE_REMOTE_STORAGE=" redis://${host_port} |connect-timeout=${CCACHE_REDIS_CONNECT_TIMEOUT} "
203250 display_alert " mDNS: discovered ccache" " $( ccache_mask_storage_url " ${CCACHE_REMOTE_STORAGE} " ) " " info"
204251 return 0
205252 fi
@@ -211,12 +258,15 @@ function ccache_discover_remote_storage() {
211258function ccache_get_redis_stats() {
212259 local ip=" $1 "
213260 local port=" ${2:- 6379} "
261+ local password=" $3 "
214262 local stats=" "
215263
216264 if command -v redis-cli & > /dev/null; then
265+ local auth_args=()
266+ [[ -n " ${password} " ]] && auth_args+=(-a " ${password} " --no-auth-warning)
217267 local keys mem
218- keys=$( timeout 2 redis-cli -h " $ip " -p " $port " DBSIZE 2> /dev/null | grep -oE ' [0-9]+' || true)
219- mem=$( timeout 2 redis-cli -h " $ip " -p " $port " INFO memory 2> /dev/null | grep " used_memory_human" | cut -d: -f2 | tr -d ' [:space:]' || true)
268+ keys=$( timeout 2 redis-cli -h " $ip " -p " $port " " ${auth_args[@]} " DBSIZE 2> /dev/null | grep -oE ' [0-9]+' || true)
269+ mem=$( timeout 2 redis-cli -h " $ip " -p " $port " " ${auth_args[@]} " INFO memory 2> /dev/null | grep " used_memory_human" | cut -d: -f2 | tr -d ' [:space:]' || true)
220270 if [[ -n " $keys " ]]; then
221271 stats=" keys=${keys:- 0} , mem=${mem:- ?} "
222272 fi
@@ -242,10 +292,30 @@ function ccache_get_http_stats() {
242292}
243293
244294# Query remote storage stats based on URL scheme (redis:// or http://)
295+ # Parses userinfo (user:pass@) from Redis URLs to pass credentials to redis-cli
245296function ccache_get_remote_stats() {
246297 local url=" $1 "
247- if [[ " ${url} " =~ ^redis://([^:/| ]+):? ([0-9]* ) ]]; then
248- ccache_get_redis_stats " ${BASH_REMATCH[1]} " " ${BASH_REMATCH[2]:- 6379} "
298+ if [[ " ${url} " =~ ^redis:// ]]; then
299+ local password=" " host=" " port=" 6379"
300+ # Strip scheme and attributes
301+ local authority=" ${url# redis:// } "
302+ authority=" ${authority%% |* } "
303+ # Extract password from userinfo (before last @)
304+ if [[ " ${authority} " =~ ^(.+)@ (.+)$ ]]; then
305+ local userinfo=" ${BASH_REMATCH[1]} "
306+ authority=" ${BASH_REMATCH[2]} "
307+ # password is after : in userinfo (user:pass or just :pass)
308+ [[ " ${userinfo} " == * :* ]] && password=" ${userinfo#*: } "
309+ fi
310+ # Parse host:port (IPv6 in brackets or plain)
311+ if [[ " ${authority} " =~ ^\[ ([^]]+)\] :? ([0-9]* ) ]]; then
312+ host=" ${BASH_REMATCH[1]} "
313+ [[ -n " ${BASH_REMATCH[2]} " ]] && port=" ${BASH_REMATCH[2]} "
314+ elif [[ " ${authority} " =~ ^([^:]+):? ([0-9]* ) ]]; then
315+ host=" ${BASH_REMATCH[1]} "
316+ [[ -n " ${BASH_REMATCH[2]} " ]] && port=" ${BASH_REMATCH[2]} "
317+ fi
318+ [[ -n " ${host} " ]] && ccache_get_redis_stats " ${host} " " ${port} " " ${password} "
249319 elif [[ " ${url} " =~ ^https? :// ]]; then
250320 # Strip ccache attributes after | for the URL
251321 ccache_get_http_stats " ${url%% |* } "
@@ -283,9 +353,10 @@ function ccache_validate_storage_url() {
283353
284354# This runs on the HOST just before Docker container is launched.
285355# Resolves 'ccache.local' via mDNS (requires Avahi on server publishing this hostname
286- # with: avahi-publish-address -R ccache.local <IP>) and passes the resolved IP
287- # to Docker container via CCACHE_REMOTE_STORAGE environment variable.
288- # mDNS resolution doesn't work inside Docker, so we must resolve on host.
356+ # Docker hook: resolve hostnames and handle loopback for container access.
357+ # mDNS/local DNS may not work inside Docker, so we resolve on host and
358+ # pass the mapping via --add-host. Loopback addresses are rewritten to
359+ # host.docker.internal.
289360function host_pre_docker_launch__setup_remote_ccache() {
290361 if [[ -n " ${CCACHE_REMOTE_STORAGE} " ]]; then
291362 ccache_validate_storage_url " ${CCACHE_REMOTE_STORAGE} " || return 1
@@ -303,6 +374,37 @@ function host_pre_docker_launch__setup_remote_ccache() {
303374 fi
304375 fi
305376
377+ # Ensure hostname in CCACHE_REMOTE_STORAGE is resolvable inside Docker.
378+ # Docker containers may not have access to host mDNS/local DNS.
379+ if [[ -n " ${CCACHE_REMOTE_STORAGE} " ]]; then
380+ local _host
381+ _host=$( ccache_extract_url_host " ${CCACHE_REMOTE_STORAGE} " )
382+ if [[ -n " ${_host} " ]]; then
383+ # Loopback addresses: rewrite to host.docker.internal
384+ if [[ " ${_host} " == " localhost" || " ${_host} " == " 127.0.0.1" || " ${_host} " == " ::1" ]]; then
385+ CCACHE_REMOTE_STORAGE=" ${CCACHE_REMOTE_STORAGE// localhost/ host.docker.internal} "
386+ CCACHE_REMOTE_STORAGE=" ${CCACHE_REMOTE_STORAGE// 127.0.0.1/ host.docker.internal} "
387+ CCACHE_REMOTE_STORAGE=" ${CCACHE_REMOTE_STORAGE// \[ :: 1\] / host.docker.internal} "
388+ DOCKER_EXTRA_ARGS+=(" --add-host=host.docker.internal:host-gateway" )
389+ display_alert " Rewriting loopback URL for Docker" " $( ccache_mask_storage_url " ${CCACHE_REMOTE_STORAGE} " ) " " info"
390+ # Hostname (not IP): resolve on host and pass via --add-host
391+ elif [[ " ${_host} " =~ [a-zA-Z] ]]; then
392+ local _resolved_ip=" ${CCACHE_REMOTE_HOST_IP:- } "
393+ # If not from discovery, resolve now; prefer IPv4 (Docker bridge often lacks IPv6)
394+ if [[ -z " ${_resolved_ip} " || " ${CCACHE_REMOTE_HOST} " != " ${_host} " ]]; then
395+ _resolved_ip=$( getent ahostsv4 " ${_host} " 2> /dev/null | awk ' {print $1; exit}' || true)
396+ [[ -z " ${_resolved_ip} " ]] && _resolved_ip=$( getent hosts " ${_host} " 2> /dev/null | awk ' {print $1; exit}' || true)
397+ fi
398+ if [[ -n " ${_resolved_ip} " ]]; then
399+ DOCKER_EXTRA_ARGS+=(" --add-host=${_host} :${_resolved_ip} " )
400+ display_alert " Docker --add-host" " ${_host} :${_resolved_ip} " " info"
401+ else
402+ display_alert " Cannot resolve hostname for Docker" " ${_host} " " wrn"
403+ fi
404+ fi
405+ fi
406+ fi
407+
306408 # Pass all set CCACHE_* variables to Docker
307409 local var val
308410 for var in " ${CCACHE_PASSTHROUGH_VARS[@]} " ; do
@@ -333,8 +435,11 @@ function ccache_post_compilation__show_remote_stats() {
333435
334436# This runs inside Docker (or native build) during configuration
335437function extension_prepare_config__setup_remote_ccache() {
336- # Enable ccache
438+ # Enable ccache with a consistent cache directory ($SRC/cache/ccache).
439+ # PRIVATE_CCACHE ensures the same CCACHE_DIR is used in native and Docker builds,
440+ # avoiding fragmented caches in /root/.cache/ccache vs $SRC/cache/ccache.
337441 declare -g USE_CCACHE=yes
442+ declare -g PRIVATE_CCACHE=yes
338443
339444 # If CCACHE_REMOTE_STORAGE was passed from host (via Docker env), it's already set
340445 if [[ -n " ${CCACHE_REMOTE_STORAGE} " ]]; then
0 commit comments