Seregon/zftpd

Zero-copy FTP/HTTP Daemon compatible with all POSIX systems

C/11.0 KB/No license
Makefile
zftpd / Makefile
1# Multi-Platform FTP Server - Makefile
2# Supports: Linux, macOS, PS3, PS4, PS5
3# Standards: MISRA C:2012, CERT C, ISO C11
4 
5# Project information
6PROJECT := zftpd
7VERSION := $(shell grep -E 'define[[:space:]]+RELEASE_VERSION' include/ftp_config.h | head -n 1 | cut -d'"' -f2)
8ifeq ($(strip $(VERSION)),)
9VERSION := 0.0.0
10endif
11ARTIFACT_PREFIX ?= zftpd
12HOST_ARCH := $(shell uname -m)
13ifeq ($(TARGET),macos)
14PLATFORM_TAG := macos-$(HOST_ARCH)
15else ifeq ($(TARGET),linux)
16PLATFORM_TAG := linux-$(HOST_ARCH)
17else
18PLATFORM_TAG := $(TARGET)
19endif
20 
21# Host OS detection (for toolchain/linker compatibility)
22HOST_OS := $(shell uname -s)
23 
24# Target platform (default detection)
25ifeq ($(HOST_OS),Darwin)
26TARGET ?= macos
27else
28TARGET ?= linux
29endif
30# Normalize target to lowercase so TARGET=PS5/PS4 still matches rules
31TARGET := $(shell echo $(TARGET) | tr '[:upper:]' '[:lower:]')
32 
33# Build configuration
34BUILD_TYPE ?= release
35# Valid values: debug, release
36 
37PS4_PAYLOAD_SDK ?= $(abspath external/ps4-payload-sdk)
38PS4_HOST ?= ps4
39PS4_PORT ?= 9021
40PS5_PAYLOAD_SDK ?= $(abspath external/ps5-payload-sdk)
41PS5_HOST ?= ps5
42PS5_PORT ?= 9021
43 
44#============================================================================
45# PLATFORM DETECTION AND CONFIGURATION
46#============================================================================
47 
48# Compiler selection
49ifeq ($(TARGET),ps3)
50 CC := ppu-gcc
51 PLATFORM_DEFS := -DPLATFORM_PS3 -DPS3
52 PLATFORM_LIBS := -lnet
53 PLATFORM_LDFLAGS :=
54endif
55 
56ifeq ($(TARGET),ps4)
57 ORBIS_LLVM_CONFIG := $(LLVM_CONFIG)
58 ifeq ($(ORBIS_LLVM_CONFIG),)
59 ORBIS_LLVM_CONFIG := $(shell command -v llvm-config-21 2>/dev/null || command -v llvm-config-20 2>/dev/null || command -v llvm-config-19 2>/dev/null || command -v llvm-config-18 2>/dev/null || command -v llvm-config-17 2>/dev/null || command -v llvm-config-16 2>/dev/null || command -v llvm-config-15 2>/dev/null || command -v llvm-config 2>/dev/null)
60 endif
61 ifeq ($(ORBIS_LLVM_CONFIG),)
62 ORBIS_LLVM_CONFIG := $(shell if command -v brew >/dev/null 2>&1; then p=$$(brew --prefix llvm 2>/dev/null); if [ -x "$$p/bin/llvm-config" ]; then echo "$$p/bin/llvm-config"; fi; fi)
63 endif
64 ifeq ($(ORBIS_LLVM_CONFIG),)
65 $(error llvm-config non trovato: installa LLVM (es. brew install llvm) e assicurati che llvm-config sia nel PATH oppure passa LLVM_CONFIG=/percorso/a/llvm-config)
66 endif
67 export LLVM_CONFIG := $(ORBIS_LLVM_CONFIG)
68 include $(PS4_PAYLOAD_SDK)/toolchain/orbis.mk
69 PLATFORM_DEFS := -DPLATFORM_PS4 -DPS4
70 PLATFORM_LIBS := -lkernel -lpthread
71 PLATFORM_LDFLAGS :=
72endif
73 
74ifeq ($(TARGET),ps5)
75 PROSPERO_LLVM_CONFIG := $(LLVM_CONFIG)
76 ifeq ($(PROSPERO_LLVM_CONFIG),)
77 PROSPERO_LLVM_CONFIG := $(shell command -v llvm-config-21 2>/dev/null || command -v llvm-config-20 2>/dev/null || command -v llvm-config-19 2>/dev/null || command -v llvm-config-18 2>/dev/null || command -v llvm-config-17 2>/dev/null || command -v llvm-config-16 2>/dev/null || command -v llvm-config-15 2>/dev/null || command -v llvm-config 2>/dev/null)
78 endif
79 ifeq ($(PROSPERO_LLVM_CONFIG),)
80 PROSPERO_LLVM_CONFIG := $(shell if command -v brew >/dev/null 2>&1; then p=$$(brew --prefix llvm 2>/dev/null); if [ -x "$$p/bin/llvm-config" ]; then echo "$$p/bin/llvm-config"; fi; fi)
81 endif
82 ifeq ($(PROSPERO_LLVM_CONFIG),)
83 $(error llvm-config non trovato: installa LLVM (es. brew install llvm) e assicurati che llvm-config sia nel PATH oppure passa LLVM_CONFIG=/percorso/a/llvm-config)
84 endif
85 export LLVM_CONFIG := $(PROSPERO_LLVM_CONFIG)
86 include $(PS5_PAYLOAD_SDK)/toolchain/prospero.mk
87 PLATFORM_DEFS := -DPLATFORM_PS5 -DPS5 -D__PROSPERO__
88 PLATFORM_LIBS := -lkernel -lpthread -lSceNotification
89 PLATFORM_LDFLAGS :=
90endif
91 
92ifeq ($(TARGET),linux)
93 CC := gcc
94 PLATFORM_DEFS := -DPLATFORM_LINUX -D_GNU_SOURCE
95 PLATFORM_LIBS := -lpthread
96 ifeq ($(HOST_OS),Linux)
97 PLATFORM_LDFLAGS := -Wl,-z,relro -Wl,-z,now
98 else
99 PLATFORM_LDFLAGS :=
100 endif
101endif
102 
103ifeq ($(TARGET),macos)
104 CC := clang
105 PLATFORM_DEFS := -DPLATFORM_MACOS -D_DARWIN_C_SOURCE
106 PLATFORM_LIBS := -lpthread
107 PLATFORM_LDFLAGS :=
108endif
109 
110# Default to GCC if no target matched
111CC ?= gcc
112PLATFORM_LIBS ?= -lpthread
113 
114#============================================================================
115# COMPILER FLAGS (SAFETY-CRITICAL STANDARDS)
116#============================================================================
117 
118# C Standard
119CFLAGS := -std=c11
120 
121# Warning flags (comprehensive)
122CFLAGS += -Wall -Wextra -Wpedantic
123CFLAGS += -Wformat=2 -Wformat-security
124CFLAGS += -Wnull-dereference
125CFLAGS += -Wstack-protector
126CFLAGS += -Wstrict-overflow=5
127CFLAGS += -Warray-bounds
128CFLAGS += -Wcast-align
129CFLAGS += -Wcast-qual
130CFLAGS += -Wconversion
131CFLAGS += -Wsign-conversion
132CFLAGS += -Wstrict-prototypes
133CFLAGS += -Wmissing-prototypes
134CFLAGS += -Wredundant-decls
135CFLAGS += -Wshadow
136CFLAGS += -Wundef
137CFLAGS += -Wwrite-strings
138 
139# Treat warnings as errors in release builds
140ifeq ($(BUILD_TYPE),release)
141 CFLAGS += -Werror
142endif
143 
144# Toolchain compatibility (Clang on macOS uses different warning set)
145ifeq ($(HOST_OS),Darwin)
146 CFLAGS += -Wno-unknown-warning-option
147endif
148 
149# Security hardening flags
150CFLAGS += -fstack-protector-strong
151CFLAGS += -fPIE
152CFLAGS += -fno-strict-aliasing
153 
154# _FORTIFY_SOURCE is glibc-specific; avoid redef warnings on non-Linux hosts
155ifeq ($(HOST_OS),Linux)
156 CFLAGS += -D_FORTIFY_SOURCE=2
157endif
158 
159# Optimization and debug flags
160ifeq ($(BUILD_TYPE),debug)
161 CFLAGS += -O0 -g3 -DDEBUG -DFTP_DEBUG=1
162 CFLAGS += -fsanitize=address,undefined
163 LDFLAGS += -fsanitize=address,undefined
164else
165 CFLAGS += -O2 -g -DNDEBUG -DFTP_DEBUG=0
166endif
167 
168# Platform-specific flags
169CFLAGS += $(PLATFORM_DEFS)
170 
171# Avoid symbol interposition when injected into host processes (PS4/PS5 payload).
172ifneq ($(filter $(TARGET),ps4 ps5),)
173 CFLAGS += -fvisibility=hidden
174endif
175 
176# Include directories
177CFLAGS += -I./include
178 
179#============================================================================
180# LINKER FLAGS
181#============================================================================
182 
183ifeq ($(HOST_OS),Linux)
184 LDFLAGS += -pie
185endif
186LDFLAGS += $(PLATFORM_LDFLAGS)
187LIBS := $(PLATFORM_LIBS)
188 
189#============================================================================
190# SOURCE FILES
191#============================================================================
192 
193# Source files
194SOURCES := src/pal_network.c
195SOURCES += src/pal_fileio.c
196SOURCES += src/pal_alloc.c
197SOURCES += src/pal_scratch.c
198SOURCES += src/pal_notification.c
199SOURCES += src/pal_filesystem.c
200SOURCES += src/pal_filesystem_psx.c
201SOURCES += src/ftp_path.c
202SOURCES += src/ftp_server.c
203SOURCES += src/ftp_session.c
204SOURCES += src/ftp_protocol.c
205SOURCES += src/ftp_commands.c
206SOURCES += src/ftp_buffer_pool.c
207SOURCES += src/ftp_log.c
208SOURCES += src/ftp_crypto.c
209SOURCES += src/main.c
210 
211# PS5-specific modules
212ifeq ($(TARGET),ps5)
213SOURCES += src/ps5_net_filter.c
214endif
215 
216#============================================================================
217# ZHTTPD (Web File Explorer) — compile-time toggle
218# Enabled by default on consoles (PS4/PS5), disabled on PC
219#============================================================================
220 
221ifneq ($(filter $(TARGET),ps4 ps5),)
222 ENABLE_ZHTTPD ?= 0
223else
224 ENABLE_ZHTTPD ?= 1
225endif
226 
227# Artifact/build variants (e.g., zhttp)
228ifeq ($(ENABLE_ZHTTPD),1)
229 VARIANT_TAG := zhttp
230endif
231 
232# Build output directories (variant-aware)
233BUILD_DIR := build/$(TARGET)/$(BUILD_TYPE)$(if $(VARIANT_TAG),-$(VARIANT_TAG),)
234OBJ_DIR := $(BUILD_DIR)/obj
235DEP_DIR := $(BUILD_DIR)/dep
236BIN_DIR := $(BUILD_DIR)
237 
238ARTIFACT_BASE := $(ARTIFACT_PREFIX)-$(PLATFORM_TAG)$(if $(VARIANT_TAG),-$(VARIANT_TAG),)-v$(VERSION)
239 
240ifeq ($(TARGET),macos)
241OUTPUT_ELF := $(BIN_DIR)/$(ARTIFACT_BASE)
242else
243OUTPUT_ELF := $(BIN_DIR)/$(ARTIFACT_BASE).elf
244endif
245OUTPUT_BIN := $(BIN_DIR)/$(ARTIFACT_BASE).bin
246 
247OBJCOPY ?= objcopy
248STRIP ?= strip
249 
250ifeq ($(ENABLE_ZHTTPD),1)
251 CFLAGS += -DENABLE_ZHTTPD=1
252 CFLAGS += -DENABLE_WEB_UPLOAD=1
253 SOURCES += src/event_loop_kqueue.c
254 SOURCES += src/http_server.c
255 SOURCES += src/http_parser.c
256 SOURCES += src/http_response.c
257 SOURCES += src/http_api.c
258 SOURCES += src/http_csrf.c
259 SOURCES += src/http_resources.c
260 SOURCES += src/exfat_unpacker.c
261 SOURCES += src/pkg_unpacker.c
262endif
263 
264#============================================================================
265# Enable with ENABLE_MCP=1
266#============================================================================
267 
268ifeq ($(ENABLE_MCP),)
269 ifneq ($(filter $(TARGET),ps4 ps5),)
270 ENABLE_MCP ?= 1
271 else
272 ENABLE_MCP ?= 0
273 endif
274endif
275 
276ifeq ($(ENABLE_MCP),1)
277 CFLAGS += -DENABLE_MCP=1
278 CFLAGS += -I./mcp/include
279 CFLAGS += -I./external/sJson-main/src
280 # JSON configuration for sJson
281 CFLAGS += -DJSON_MAX_DEPTH=32
282 CFLAGS += -DJSON_MAX_STRING_LEN=65536
283 CFLAGS += -DJSON_MAX_NODES=16384
284 SOURCES += mcp/src/mcp_protocol.c
285 SOURCES += mcp/src/mcp_server.c
286 SOURCES += mcp/src/mcp_handlers.c
287 SOURCES += external/sJson-main/src/sJson.c
288 SOURCES += src/event_loop_kqueue.c
289 # Execution modules
290 SOURCES += mcp/src/mcp_execution/payload.c
291 SOURCES += mcp/src/mcp_execution/syscall_race.c
292 # Hunter modules (Zero-Day detection)
293 SOURCES += mcp/src/mcp_hunter/process_monitor.c
294 SOURCES += mcp/src/mcp_hunter/vuln_fuzzer.c
295 SOURCES += mcp/src/mcp_hunter/exploit_chain.c
296 SOURCES += mcp/src/mcp_hunter/jit_compiler.c
297 # Additional include path for hunter headers
298 CFLAGS += -I./mcp/src/mcp_hunter
299 $(info [INFO] MCP kernel analysis module enabled (with Zero-Day Hunter))
300endif
301 
302#============================================================================
303# OPTIONAL LIBRARIES — enable with ENABLE_LIBARCHIVE=1 / ENABLE_LIBCURL=1
304#
305# When enabled, the Makefile verifies that the required header is actually
306# available. For desktop (gcc/clang) it uses a compiler probe. For
307# PS4/PS5 cross-compilers the probe fails (missing sysroot headers),
308# so we fall back to a simple file-existence check on bundled headers.
309#============================================================================
310 
311override ENABLE_LIBARCHIVE ?= 0
312override ENABLE_LIBCURL ?= 0
313 
314# ── libarchive detection ──────────────────────────────────────────────────
315ifeq ($(ENABLE_LIBARCHIVE),1)
316 _BUNDLED_ARCHIVE_H := $(wildcard external/libarchive-3.8.6/libarchive/archive.h)
317 ifneq ($(filter $(TARGET),ps4 ps5),)
318 # Cross-compilation: NO prebuilt libarchive static lib for PS4/PS5!
319 # Even if headers are found, we must gracefully disable it.
320 $(info [INFO] libarchive not supported on cross-compile targets — disabling ENABLE_LIBARCHIVE)
321 override ENABLE_LIBARCHIVE := 0
322 else
323 # Desktop: try system headers first, then bundled
324 _HAS_ARCHIVE := $(shell echo '\#include <archive.h>' | $(CC) -xc -fsyntax-only - 2>/dev/null && echo 1 || echo 0)
325 ifeq ($(_HAS_ARCHIVE),1)
326 $(info [INFO] Using system libarchive)
327 else ifneq ($(_BUNDLED_ARCHIVE_H),)
328 $(info [INFO] Using bundled libarchive headers)
329 CFLAGS += -I./external/libarchive-3.8.6/libarchive
330 ifeq ($(wildcard external/libarchive-3.8.6-compiled/.libs/libarchive.a),)
331 LIBS += -larchive
332 else
333 LIBS += external/libarchive-3.8.6-compiled/.libs/libarchive.a
334 endif
335 else
336 $(info [INFO] libarchive headers not found — disabling ENABLE_LIBARCHIVE)
337 override ENABLE_LIBARCHIVE := 0
338 endif
339 endif
340endif
341 
342ifeq ($(ENABLE_LIBARCHIVE),1)
343 CFLAGS += -DENABLE_LIBARCHIVE=1
344endif
345 
346# ── libcurl detection ─────────────────────────────────────────────────────
347ifeq ($(ENABLE_LIBCURL),1)
348 _BUNDLED_CURL_H := $(wildcard external/curl/include/curl/curl.h)
349 ifneq ($(filter $(TARGET),ps4 ps5),)
350 # Cross-compilation: check for bundled curl headers
351 ifneq ($(_BUNDLED_CURL_H),)
352 $(info [INFO] Using bundled libcurl headers (cross-compile))
353 CFLAGS += -I./external/curl/include
354 else
355 $(info [INFO] libcurl headers not found — disabling ENABLE_LIBCURL)
356 override ENABLE_LIBCURL := 0
357 endif
358 else
359 # Desktop: compiler probe
360 _HAS_CURL := $(shell echo '\#include <curl/curl.h>' | $(CC) -xc -fsyntax-only - 2>/dev/null && echo 1 || echo 0)
361 ifneq ($(_HAS_CURL),1)
362 $(info [INFO] libcurl headers not found — disabling ENABLE_LIBCURL)
363 override ENABLE_LIBCURL := 0
364 endif
365 endif
366endif
367 
368ifeq ($(ENABLE_LIBCURL),1)
369 CFLAGS += -DENABLE_LIBCURL=1
370 ifneq ($(filter $(TARGET),ps4 ps5),)
371 SOURCES += src/pal_curl.c
372 else
373 LIBS += -lcurl
374 endif
375endif
376 
377# Object files (handle both src/ and mcp/src/ paths)
378OBJECTS := $(patsubst src/%.c,$(OBJ_DIR)/%.o,$(filter src/%.c,$(SOURCES)))
379OBJECTS += $(patsubst mcp/src/%.c,$(OBJ_DIR)/mcp/%.o,$(filter mcp/src/%.c,$(SOURCES)))
380 
381# FFI Object files
382FFI_SOURCES := ffi/c_core/pal_ffi.c
383FFI_OBJECTS := $(patsubst ffi/%.c,$(OBJ_DIR)/ffi/%.o,$(FFI_SOURCES))
384 
385# Object files without main (for unit tests and ffi library)
386LIB_OBJECTS := $(filter-out $(OBJ_DIR)/main.o,$(OBJECTS))
387 
388# Dependency files
389DEPENDS := $(patsubst $(OBJ_DIR)/%.o,$(DEP_DIR)/%.d,$(filter-out $(OBJ_DIR)/mcp/%.o,$(OBJECTS)))
390DEPENDS += $(patsubst $(OBJ_DIR)/mcp/%.o,$(DEP_DIR)/mcp/%.d,$(filter $(OBJ_DIR)/mcp/%.o,$(OBJECTS)))
391 
392#============================================================================
393# BUILD TARGETS
394#============================================================================
395 
396.PHONY: all clean distclean install test help bin deploy deploy-i deploy-nc doctor-ps4
397.PHONY: all-platforms release-all debug-all ffi ffi-java ffi-rust ffi-python resources
398.PHONY: ps5-hook-blob web-deploy
399 
400# ============================================================================
401# PS5 NET FILTER HOOK — Kernel-safe compilation pipeline
402#
403# The hook functions (src/ps5_net_filter_hook.c) run in ring-0 (kernel mode)
404# and require special compiler flags that differ from the normal build.
405#
406# Pipeline:
407# 1. Compile hook with kernel-safe flags → ps5_net_filter_hook.o
408# 2. Extract .text.hook_connect and .text.hook_sendto sections → .bin
409# 3. Generate C byte-array header → ps5_net_filter_hook_blob.h
410#
411# The blob header is included by ps5_net_filter.c to replace the placeholder
412# byte arrays (g_hook_connect_code[], g_hook_sendto_code[]).
413#
414# Run manually before the PS5 build:
415# make ps5-hook-blob
416# ============================================================================
417 
418ifeq ($(TARGET),ps5)
419 
420HOOK_OBJ := $(OBJ_DIR)/ps5_net_filter_hook.o
421HOOK_BIN := $(OBJ_DIR)/ps5_net_filter_hook.bin
422HOOK_BLOB_H := src/ps5_net_filter_hook_blob.h
423 
424# Kernel-safe compiler flags (MUST differ from normal CFLAGS)
425HOOK_CFLAGS := \
426 -DPS5_HOOK_BUILD \
427 -DPLATFORM_PS5 \
428 -std=c11 \
429 -O2 \
430 -fno-stack-protector \
431 -mno-red-zone \
432 -fPIC \
433 -mcmodel=large \
434 -fno-plt \
435 -fno-common \
436 -fno-builtin \
437 -fno-exceptions \
438 -fomit-frame-pointer \
439 -I include/
440 
441ps5-hook-blob: $(HOOK_BLOB_H)
442 @echo " [BLOB] $< generated ($(shell wc -c < $(HOOK_BIN) 2>/dev/null || echo '?') bytes)"
443 
444$(HOOK_BLOB_H): $(HOOK_BIN)
445 @echo " [XXD] $@"
446 @xxd -i $< > $@
447 
448$(HOOK_BIN): $(HOOK_OBJ)
449 @echo " [OBJCOPY] $@"
450 @$(OBJCOPY) -O binary \
451 --only-section=.text.hook_connect \
452 --only-section=.text.hook_sendto \
453 $< $@
454 
455$(HOOK_OBJ): src/ps5_net_filter_hook.c | $(OBJ_DIR)
456 @echo " [HOOK-CC] $<"
457 @$(CC) $(HOOK_CFLAGS) -c $< -o $@
458 
459endif # TARGET=ps5
460 
461resources:
462 @echo " [GEN] src/http_resources.c"
463 @python3 tools/generate_resources.py > src/http_resources.c
464 
465.DEFAULT_GOAL := all
466 
467# FFI Shared Library output
468ifeq ($(TARGET),macos)
469FFI_OUTPUT := $(BIN_DIR)/libzftpd_ffi.dylib
470FFI_LDFLAGS := -dynamiclib
471else
472FFI_OUTPUT := $(BIN_DIR)/libzftpd_ffi.so
473FFI_LDFLAGS := -shared
474endif
475 
476$(BIN_DIR) $(OBJ_DIR) $(DEP_DIR) $(BUILD_DIR)/tests $(OBJ_DIR)/ffi/c_core $(OBJ_DIR)/mcp:
477 @mkdir -p $@
478 
479ifeq ($(filter $(TARGET),ps4 ps5),)
480all: $(OUTPUT_ELF) $(if $(ffi_langs),ffi)
481else
482all: $(OUTPUT_BIN) $(if $(ffi_langs),ffi)
483endif
484 
485$(PROJECT): all
486 @true
487 
488# Link executable
489$(OUTPUT_ELF): $(OBJECTS) | $(BIN_DIR)
490 @echo " [LD] $@"
491 @mkdir -p $(BIN_DIR)
492 @$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
493 @echo "Build complete: $(PROJECT) ($(TARGET), $(BUILD_TYPE))"
494 
495# FFI Build Targets
496.PHONY: ffi
497ffi: $(FFI_OUTPUT)
498ifneq ($(findstring java,$(ffi_langs)),)
499 @echo " [FFI] Building Java bindings..."
500 @$(MAKE) ffi-java
501endif
502ifneq ($(findstring rust,$(ffi_langs)),)
503 @echo " [FFI] Building Rust bindings..."
504 @$(MAKE) ffi-rust
505endif
506ifneq ($(findstring python,$(ffi_langs)),)
507 @echo " [FFI] Building Python bindings..."
508 @$(MAKE) ffi-python
509endif
510ifneq ($(findstring go,$(ffi_langs)),)
511 @echo " [FFI] Building Go bindings..."
512 @$(MAKE) ffi-go
513endif
514 
515$(FFI_OUTPUT): $(LIB_OBJECTS) $(FFI_OBJECTS) | $(BIN_DIR)
516 @echo " [LD] $@ (Shared Library)"
517 @mkdir -p $(BIN_DIR)
518 @$(CC) $(LDFLAGS) $(FFI_LDFLAGS) -fPIC -o $@ $(LIB_OBJECTS) $(FFI_OBJECTS) $(LIBS)
519 @echo "FFI C-Core built: $@"
520 
521# Build all supported platforms (best-effort: includes only toolchains found on the host).
522TARGETS_ALL ?= $(shell \
523 echo macos; \
524 command -v gcc >/dev/null 2>&1 && echo linux || true; \
525 command -v ppu-gcc >/dev/null 2>&1 && echo ps3 || true; \
526 [ -d external/ps4-payload-sdk ] && echo ps4 || true; \
527 [ -d external/ps5-payload-sdk ] && echo ps5 || true)
528 
529# Build matrix toggles for ZHTTPD variant
530ZHTTP_VARIANTS ?= 0 1
531 
532JAVA_HOME_PATH ?= $(shell /usr/libexec/java_home)
533 
534# Java FFI Target
535.PHONY: ffi-java
536ffi-java: $(FFI_OUTPUT)
537 @echo " [JAVAC] Compiling Java FFI bindings..."
538 @mkdir -p $(BIN_DIR)/ffi/java
539 @javac -J-Xint -d $(BIN_DIR)/ffi/java ffi/java/src/main/java/org/zftpd/ffi/*.java
540 @echo " [JAVAC] Compiling Java FFI tests..."
541 @javac -J-Xint -cp $(BIN_DIR)/ffi/java -d $(BIN_DIR)/ffi/java ffi/java/src/test/java/org/zftpd/ffi/*.java
542 @echo " [CC] Compiling JNI C wrapper..."
543 @$(CC) $(CFLAGS) $(FFI_LDFLAGS) -fPIC \
544 -I"$(JAVA_HOME_PATH)/include" \
545 -I"$(JAVA_HOME_PATH)/include/darwin" \
546 -I"./include" -I"./ffi/c_core" \
547 -o $(BIN_DIR)/libzftpd_ffi_java$(suffix $(FFI_OUTPUT)) \
548 ffi/java/src/main/c/pal_ffi_jni.c \
549 -L$(BIN_DIR) -lzftpd_ffi $(LIBS)
550 @echo " [JAVA] Running FFI tests..."
551 @java -Xint -Djava.library.path=$(BIN_DIR) -cp $(BIN_DIR)/ffi/java org.zftpd.ffi.FfiTests
552 @echo "Java FFI bindings built successfully."
553 
554# Rust FFI Target
555.PHONY: ffi-rust
556ffi-rust: $(FFI_OUTPUT)
557 @echo " [CARGO] Building Rust FFI bindings..."
558 @cd ffi/rust && cargo build --release
559 @echo " [CARGO] Running Rust FFI tests..."
560ifeq ($(TARGET),macos)
561 @cd ffi/rust && DYLD_LIBRARY_PATH=../../build/macos/release cargo test
562else
563 @cd ffi/rust && LD_LIBRARY_PATH=../../build/linux/release cargo test
564endif
565 @echo "Rust FFI bindings built successfully."
566 
567# Python FFI Target
568.PHONY: ffi-python
569ffi-python: $(FFI_OUTPUT)
570 @echo " [PYTHON] Installing dependencies..."
571 @python3 -m pip install -q cffi pytest
572 @echo " [PYTHON] Running Python FFI tests..."
573 @cd ffi/python && PYTHONPATH=. pytest tests/
574 @echo "Python FFI bindings tested successfully."
575 
576# Go FFI Target
577.PHONY: ffi-go
578ffi-go: $(FFI_OUTPUT)
579 @echo " [GO] Compiling and Testing Go FFI bindings..."
580ifeq ($(TARGET),macos)
581 @cd ffi/go/zftpd && DYLD_LIBRARY_PATH=../../../build/macos/release go test -v
582else
583 @cd ffi/go/zftpd && LD_LIBRARY_PATH=../../../build/linux/release go test -v
584endif
585 @echo "Go FFI bindings tested successfully."
586 
587all-platforms: release-all
588 
589release-all:
590 @set -e; \
591 for t in $(TARGETS_ALL); do \
592 echo "==> Building $$t (release)"; \
593 $(MAKE) TARGET=$$t BUILD_TYPE=release clean all; \
594 done
595 
596# Build all platforms in release with/without ZHTTPD (produces ELF and BIN where applicable)
597release-matrix:
598 @set -e; \
599 for t in $(TARGETS_ALL); do \
600 for z in $(ZHTTP_VARIANTS); do \
601 echo "==> Building $$t (release, ENABLE_ZHTTPD=$$z)"; \
602 $(MAKE) TARGET=$$t BUILD_TYPE=release ENABLE_ZHTTPD=$$z \
603 ENABLE_LIBARCHIVE=$(ENABLE_LIBARCHIVE) \
604 ENABLE_LIBCURL=$(ENABLE_LIBCURL) \
605 clean all; \
606 done; \
607 done
608 
609debug-all:
610 @set -e; \
611 for t in $(TARGETS_ALL); do \
612 echo "==> Building $$t (debug)"; \
613 $(MAKE) TARGET=$$t BUILD_TYPE=debug clean all; \
614 done
615# Compile C source files
616$(OBJ_DIR)/%.o: src/%.c | $(OBJ_DIR) $(DEP_DIR)
617 @echo " [CC] $<"
618 @mkdir -p $(dir $@) $(dir $(DEP_DIR)/$*.d)
619 @$(CC) $(CFLAGS) -MMD -MP -MF $(DEP_DIR)/$*.d -MT $@ -c $< -o $@
620 
621# Compile FFI C source files (with -fPIC for shared library)
622$(OBJ_DIR)/ffi/%.o: ffi/%.c | $(OBJ_DIR)/ffi/c_core
623 @echo " [CC] $< (FFI)"
624 @mkdir -p $(dir $@) $(dir $(DEP_DIR)/ffi/$*.d)
625 @$(CC) $(CFLAGS) -fPIC -MMD -MP -MF $(DEP_DIR)/ffi/$*.d -MT $@ -c $< -o $@
626 
627# Compile MCP C source files
628$(OBJ_DIR)/mcp/%.o: mcp/src/%.c | $(OBJ_DIR)/mcp
629 @echo " [CC] $< (MCP)"
630 @mkdir -p $(dir $@) $(dir $(DEP_DIR)/mcp/$*.d)
631 @$(CC) $(CFLAGS) -MMD -MP -MF $(DEP_DIR)/mcp/$*.d -MT $@ -c $< -o $@
632 
633# Include dependency files
634-include $(DEPENDS)
635 
636#============================================================================
637# UTILITY TARGETS
638#============================================================================
639 
640# Clean build artifacts
641clean:
642 @echo "Cleaning build artifacts..."
643 @rm -rf $(BUILD_DIR)
644 @rm -f src/*.o src/*.d
645 @rm -f tests/*.o tests/*.d
646 @rm -f $(PROJECT) $(PROJECT).elf $(PROJECT).bin
647 @rm -f tests/test_size
648 @echo "Clean complete."
649 
650# Deep clean (including configuration)
651distclean: clean
652 @echo "Removing all generated files..."
653 @rm -f *~ core
654 @rm -rf build
655 @echo "Distclean complete."
656 
657# Install (simple copy for now)
658install: $(OUTPUT_ELF)
659 @echo "Installing $(PROJECT)..."
660 @install -D -m 0755 $(OUTPUT_ELF) $(DESTDIR)/usr/local/bin/$(PROJECT)
661 @echo "Install complete."
662 
663# Run static analysis
664analyze:
665 @echo "Running static analysis..."
666 @clang --analyze $(CFLAGS) $(SOURCES)
667 @echo "Analysis complete."
668 
669# Run tests
670TEST_BINS := $(BUILD_DIR)/tests/test_size
671TEST_BINS += $(BUILD_DIR)/tests/test_security
672TEST_BINS += $(BUILD_DIR)/tests/test_path_security
673TEST_BINS += $(BUILD_DIR)/tests/test_buffer_pool
674TEST_BINS += $(BUILD_DIR)/tests/test_scratch
675TEST_BINS += $(BUILD_DIR)/tests/test_alloc
676TEST_BINS += $(BUILD_DIR)/tests/test_mlst_ascii
677TEST_BINS += $(BUILD_DIR)/tests/test_http_query
678TEST_BINS += $(BUILD_DIR)/tests/test_http_confinement
679 
680ifeq ($(filter $(TARGET),linux macos),)
681test: $(OUTPUT_BIN)
682 @echo "Tests skipped for TARGET=$(TARGET)"
683else
684test: $(OUTPUT_ELF) $(OUTPUT_BIN) $(TEST_BINS)
685 @echo "Running tests..."
686 @for t in $(TEST_BINS); do ./$$t; done
687endif
688 
689$(BUILD_DIR)/tests/test_http_query: tests/test_http_query.c $(LIB_OBJECTS) | $(BUILD_DIR)/tests
690 @echo " [CC] $<"
691 @$(CC) $(CFLAGS) -DFTP_AUTH_DELAY=0 -o $@ $< $(LIB_OBJECTS) $(LDFLAGS) $(LIBS)
692 
693$(BUILD_DIR)/tests/%: tests/%.c $(LIB_OBJECTS) | $(BUILD_DIR)/tests
694 @echo " [CC] $<"
695 @$(CC) $(CFLAGS) -DFTP_AUTH_DELAY=0 -o $@ $< $(LIB_OBJECTS) $(LDFLAGS) $(LIBS)
696 
697bin: $(OUTPUT_BIN)
698 
699$(OUTPUT_BIN): $(OUTPUT_ELF) | $(BIN_DIR)
700 @echo " [STRIP] $@"
701 @command -v $(OBJCOPY) >/dev/null 2>&1 || { echo "error: '$(OBJCOPY)' not found (set OBJCOPY=... or install binutils)"; exit 1; }
702ifeq ($(filter $(TARGET),ps4 ps5),)
703 @$(OBJCOPY) -O binary $< $@
704else
705 @$(STRIP) --strip-unneeded -R .comment -R .GCC.command.line $< -o $@
706endif
707 
708deploy: $(OUTPUT_BIN)
709 @if [ "$(TARGET)" = "ps4" ]; then \
710 command -v socat >/dev/null 2>&1 || { echo "error: 'socat' non trovato (brew install socat)"; exit 1; }; \
711 $(PS4_DEPLOY) -h $(PS4_HOST) -p $(PS4_PORT) $(OUTPUT_BIN); \
712 elif [ "$(TARGET)" = "ps5" ]; then \
713 $(PS5_DEPLOY) -h $(PS5_HOST) -p $(PS5_PORT) $(OUTPUT_BIN); \
714 else \
715 echo "error: deploy supportato solo con TARGET=ps4 o TARGET=ps5"; exit 1; \
716 fi
717 
718deploy-i: $(OUTPUT_BIN)
719 @if [ "$(TARGET)" = "ps4" ]; then \
720 command -v socat >/dev/null 2>&1 || { echo "error: 'socat' non trovato (brew install socat)"; exit 1; }; \
721 $(PS4_DEPLOY) -h $(PS4_HOST) -p $(PS4_PORT) -i $(OUTPUT_BIN); \
722 elif [ "$(TARGET)" = "ps5" ]; then \
723 $(PS5_DEPLOY) -h $(PS5_HOST) -p $(PS5_PORT) -i $(OUTPUT_BIN); \
724 else \
725 echo "error: deploy-i supportato solo con TARGET=ps4 o TARGET=ps5"; exit 1; \
726 fi
727 
728deploy-nc: $(OUTPUT_BIN)
729 @if [ "$(TARGET)" = "ps4" ]; then \
730 command -v nc >/dev/null 2>&1 || { echo "error: 'nc' (netcat) non trovato"; exit 1; }; \
731 echo "Sending $(OUTPUT_BIN) to $(PS4_HOST):$(PS4_PORT) via nc..."; \
732 nc -w 10 $(PS4_HOST) $(PS4_PORT) < $(OUTPUT_BIN); \
733 elif [ "$(TARGET)" = "ps5" ]; then \
734 command -v nc >/dev/null 2>&1 || { echo "error: 'nc' (netcat) non trovato"; exit 1; }; \
735 echo "Sending $(OUTPUT_BIN) to $(PS5_HOST):$(PS5_PORT) via nc..."; \
736 nc -w 10 $(PS5_HOST) $(PS5_PORT) < $(OUTPUT_BIN); \
737 else \
738 echo "error: deploy-nc supportato solo con TARGET=ps4 o TARGET=ps5"; exit 1; \
739 fi
740 
741doctor-ps4:
742 @echo "TARGET=ps4 prerequisiti (macOS/Homebrew)"
743 @echo ""
744 @echo "llvm-config:"
745 @{ command -v llvm-config >/dev/null 2>&1 && echo " OK: $$(command -v llvm-config)" || true; }
746 @{ [ -x "/opt/homebrew/opt/llvm/bin/llvm-config" ] && echo " OK: /opt/homebrew/opt/llvm/bin/llvm-config" || true; }
747 @echo ""
748 @echo "ld.lld:"
749 @{ command -v ld.lld >/dev/null 2>&1 && echo " OK: $$(command -v ld.lld)" || true; }
750 @{ command -v brew >/dev/null 2>&1 && p=$$(brew --prefix lld 2>/dev/null || true) && [ -n "$$p" ] && [ -x "$$p/bin/ld.lld" ] && echo " OK: $$p/bin/ld.lld" || true; }
751 @echo ""
752 @echo "Se manca qualcosa:"
753 @echo " brew install llvm lld"
754 @echo " export PATH=\"/opt/homebrew/opt/llvm/bin:/opt/homebrew/opt/lld/bin:$$PATH\""
755 
756#============================================================================
757# HELP
758#============================================================================
759 
760help:
761 @echo "Multi-Platform FTP Server Build System"
762 @echo "========================================"
763 @echo ""
764 @echo "Targets:"
765 @echo " all - Build the FTP server (default)"
766 @echo " clean - Remove build artifacts"
767 @echo " distclean - Deep clean (remove all generated files)"
768 @echo " install - Install to system (requires root)"
769 @echo " analyze - Run static analysis (requires clang)"
770 @echo " test - Run test suite"
771 @echo " help - Display this help message"
772 @echo " web-deploy - Copy web UI to console filesystem"
773 @echo ""
774 @echo "Variables:"
775 @echo " TARGET - Target platform (linux, macos, ps3, ps4, ps5)"
776 @echo " BUILD_TYPE - Build configuration (debug, release)"
777 @echo " ENABLE_LIBARCHIVE - Enable archive extraction (0/1, requires libarchive)"
778 @echo " ENABLE_LIBCURL - Enable URL downloads (0/1, requires libcurl)"
779 @echo " WEB_DEPLOY_DIR - Web UI deploy path (default: /data/zftpd/web)"
780 @echo ""
781 @echo "Examples:"
782 @echo " make # Build for Linux (release)"
783 @echo " make TARGET=macos # Build for macOS"
784 @echo " make TARGET=ps5 # Build for PS5"
785 @echo " make BUILD_TYPE=debug # Build debug version"
786 @echo " make TARGET=ps4 BUILD_TYPE=debug # PS4 debug build"
787 @echo " make ENABLE_LIBARCHIVE=1 ENABLE_LIBCURL=1 # With extract + download"
788 @echo " make web-deploy # Deploy web UI to /data/zftpd/web/"
789 @echo ""
790 @echo "Current configuration:"
791 @echo " Target: $(TARGET)"
792 @echo " Build type: $(BUILD_TYPE)"
793 @echo " Compiler: $(CC)"
794 @echo " libarchive: $(ENABLE_LIBARCHIVE)"
795 @echo " libcurl: $(ENABLE_LIBCURL)"
796 @echo ""
797 
798#============================================================================
799# COMPILATION DATABASE (for IDE/LSP support)
800#============================================================================
801 
802compile_commands.json:
803 @echo "Generating compilation database..."
804 @bear -- make clean all
805 @echo "Compilation database generated."
806 
807.PHONY: compile_commands.json
808 
809#============================================================================
810# WEB DEPLOY — copy modular web UI to console filesystem
811#
812# make web-deploy (uses default WEB_DEPLOY_DIR)
813# make web-deploy WEB_DEPLOY_DIR=/mnt/usb/zftpd/web
814#
815# On PS5: files go to /data/zftpd/web/ (matching HTTP_WEB_ROOT)
816#============================================================================
817 
818WEB_DEPLOY_DIR ?= /data/zftpd/web
819 
820web-deploy:
821 @echo " [WEB] Deploying web UI to $(WEB_DEPLOY_DIR)/"
822 @mkdir -p $(WEB_DEPLOY_DIR)/css
823 @mkdir -p $(WEB_DEPLOY_DIR)/js/views
824 @mkdir -p $(WEB_DEPLOY_DIR)/assets
825 @cp web/index.html $(WEB_DEPLOY_DIR)/
826 @cp web/css/*.css $(WEB_DEPLOY_DIR)/css/
827 @cp web/js/*.js $(WEB_DEPLOY_DIR)/js/
828 @cp web/js/views/*.js $(WEB_DEPLOY_DIR)/js/views/
829 @cp web/assets/* $(WEB_DEPLOY_DIR)/assets/
830 @echo " [WEB] Done — $(shell find web/css web/js -name '*.css' -o -name '*.js' | wc -l | tr -d ' ') files deployed"
831