Zero-copy FTP/HTTP Daemon compatible with all POSIX systems
| 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 |
| 6 | PROJECT := zftpd |
| 7 | VERSION := $(shell grep -E 'define[[:space:]]+RELEASE_VERSION' include/ftp_config.h | head -n 1 | cut -d'"' -f2) |
| 8 | ifeq ($(strip $(VERSION)),) |
| 9 | VERSION := 0.0.0 |
| 10 | endif |
| 11 | ARTIFACT_PREFIX ?= zftpd |
| 12 | HOST_ARCH := $(shell uname -m) |
| 13 | ifeq ($(TARGET),macos) |
| 14 | PLATFORM_TAG := macos-$(HOST_ARCH) |
| 15 | else ifeq ($(TARGET),linux) |
| 16 | PLATFORM_TAG := linux-$(HOST_ARCH) |
| 17 | else |
| 18 | PLATFORM_TAG := $(TARGET) |
| 19 | endif |
| 20 | |
| 21 | # Host OS detection (for toolchain/linker compatibility) |
| 22 | HOST_OS := $(shell uname -s) |
| 23 | |
| 24 | # Target platform (default detection) |
| 25 | ifeq ($(HOST_OS),Darwin) |
| 26 | TARGET ?= macos |
| 27 | else |
| 28 | TARGET ?= linux |
| 29 | endif |
| 30 | # Normalize target to lowercase so TARGET=PS5/PS4 still matches rules |
| 31 | TARGET := $(shell echo $(TARGET) | tr '[:upper:]' '[:lower:]') |
| 32 | |
| 33 | # Build configuration |
| 34 | BUILD_TYPE ?= release |
| 35 | # Valid values: debug, release |
| 36 | |
| 37 | PS4_PAYLOAD_SDK ?= $(abspath external/ps4-payload-sdk) |
| 38 | PS4_HOST ?= ps4 |
| 39 | PS4_PORT ?= 9021 |
| 40 | PS5_PAYLOAD_SDK ?= $(abspath external/ps5-payload-sdk) |
| 41 | PS5_HOST ?= ps5 |
| 42 | PS5_PORT ?= 9021 |
| 43 | |
| 44 | #============================================================================ |
| 45 | # PLATFORM DETECTION AND CONFIGURATION |
| 46 | #============================================================================ |
| 47 | |
| 48 | # Compiler selection |
| 49 | ifeq ($(TARGET),ps3) |
| 50 | CC := ppu-gcc |
| 51 | PLATFORM_DEFS := -DPLATFORM_PS3 -DPS3 |
| 52 | PLATFORM_LIBS := -lnet |
| 53 | PLATFORM_LDFLAGS := |
| 54 | endif |
| 55 | |
| 56 | ifeq ($(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 := |
| 72 | endif |
| 73 | |
| 74 | ifeq ($(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 := |
| 90 | endif |
| 91 | |
| 92 | ifeq ($(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 |
| 101 | endif |
| 102 | |
| 103 | ifeq ($(TARGET),macos) |
| 104 | CC := clang |
| 105 | PLATFORM_DEFS := -DPLATFORM_MACOS -D_DARWIN_C_SOURCE |
| 106 | PLATFORM_LIBS := -lpthread |
| 107 | PLATFORM_LDFLAGS := |
| 108 | endif |
| 109 | |
| 110 | # Default to GCC if no target matched |
| 111 | CC ?= gcc |
| 112 | PLATFORM_LIBS ?= -lpthread |
| 113 | |
| 114 | #============================================================================ |
| 115 | # COMPILER FLAGS (SAFETY-CRITICAL STANDARDS) |
| 116 | #============================================================================ |
| 117 | |
| 118 | # C Standard |
| 119 | CFLAGS := -std=c11 |
| 120 | |
| 121 | # Warning flags (comprehensive) |
| 122 | CFLAGS += -Wall -Wextra -Wpedantic |
| 123 | CFLAGS += -Wformat=2 -Wformat-security |
| 124 | CFLAGS += -Wnull-dereference |
| 125 | CFLAGS += -Wstack-protector |
| 126 | CFLAGS += -Wstrict-overflow=5 |
| 127 | CFLAGS += -Warray-bounds |
| 128 | CFLAGS += -Wcast-align |
| 129 | CFLAGS += -Wcast-qual |
| 130 | CFLAGS += -Wconversion |
| 131 | CFLAGS += -Wsign-conversion |
| 132 | CFLAGS += -Wstrict-prototypes |
| 133 | CFLAGS += -Wmissing-prototypes |
| 134 | CFLAGS += -Wredundant-decls |
| 135 | CFLAGS += -Wshadow |
| 136 | CFLAGS += -Wundef |
| 137 | CFLAGS += -Wwrite-strings |
| 138 | |
| 139 | # Treat warnings as errors in release builds |
| 140 | ifeq ($(BUILD_TYPE),release) |
| 141 | CFLAGS += -Werror |
| 142 | endif |
| 143 | |
| 144 | # Toolchain compatibility (Clang on macOS uses different warning set) |
| 145 | ifeq ($(HOST_OS),Darwin) |
| 146 | CFLAGS += -Wno-unknown-warning-option |
| 147 | endif |
| 148 | |
| 149 | # Security hardening flags |
| 150 | CFLAGS += -fstack-protector-strong |
| 151 | CFLAGS += -fPIE |
| 152 | CFLAGS += -fno-strict-aliasing |
| 153 | |
| 154 | # _FORTIFY_SOURCE is glibc-specific; avoid redef warnings on non-Linux hosts |
| 155 | ifeq ($(HOST_OS),Linux) |
| 156 | CFLAGS += -D_FORTIFY_SOURCE=2 |
| 157 | endif |
| 158 | |
| 159 | # Optimization and debug flags |
| 160 | ifeq ($(BUILD_TYPE),debug) |
| 161 | CFLAGS += -O0 -g3 -DDEBUG -DFTP_DEBUG=1 |
| 162 | CFLAGS += -fsanitize=address,undefined |
| 163 | LDFLAGS += -fsanitize=address,undefined |
| 164 | else |
| 165 | CFLAGS += -O2 -g -DNDEBUG -DFTP_DEBUG=0 |
| 166 | endif |
| 167 | |
| 168 | # Platform-specific flags |
| 169 | CFLAGS += $(PLATFORM_DEFS) |
| 170 | |
| 171 | # Avoid symbol interposition when injected into host processes (PS4/PS5 payload). |
| 172 | ifneq ($(filter $(TARGET),ps4 ps5),) |
| 173 | CFLAGS += -fvisibility=hidden |
| 174 | endif |
| 175 | |
| 176 | # Include directories |
| 177 | CFLAGS += -I./include |
| 178 | |
| 179 | #============================================================================ |
| 180 | # LINKER FLAGS |
| 181 | #============================================================================ |
| 182 | |
| 183 | ifeq ($(HOST_OS),Linux) |
| 184 | LDFLAGS += -pie |
| 185 | endif |
| 186 | LDFLAGS += $(PLATFORM_LDFLAGS) |
| 187 | LIBS := $(PLATFORM_LIBS) |
| 188 | |
| 189 | #============================================================================ |
| 190 | # SOURCE FILES |
| 191 | #============================================================================ |
| 192 | |
| 193 | # Source files |
| 194 | SOURCES := src/pal_network.c |
| 195 | SOURCES += src/pal_fileio.c |
| 196 | SOURCES += src/pal_alloc.c |
| 197 | SOURCES += src/pal_scratch.c |
| 198 | SOURCES += src/pal_notification.c |
| 199 | SOURCES += src/pal_filesystem.c |
| 200 | SOURCES += src/pal_filesystem_psx.c |
| 201 | SOURCES += src/ftp_path.c |
| 202 | SOURCES += src/ftp_server.c |
| 203 | SOURCES += src/ftp_session.c |
| 204 | SOURCES += src/ftp_protocol.c |
| 205 | SOURCES += src/ftp_commands.c |
| 206 | SOURCES += src/ftp_buffer_pool.c |
| 207 | SOURCES += src/ftp_log.c |
| 208 | SOURCES += src/ftp_crypto.c |
| 209 | SOURCES += src/main.c |
| 210 | |
| 211 | # PS5-specific modules |
| 212 | ifeq ($(TARGET),ps5) |
| 213 | SOURCES += src/ps5_net_filter.c |
| 214 | endif |
| 215 | |
| 216 | #============================================================================ |
| 217 | # ZHTTPD (Web File Explorer) — compile-time toggle |
| 218 | # Enabled by default on consoles (PS4/PS5), disabled on PC |
| 219 | #============================================================================ |
| 220 | |
| 221 | ifneq ($(filter $(TARGET),ps4 ps5),) |
| 222 | ENABLE_ZHTTPD ?= 0 |
| 223 | else |
| 224 | ENABLE_ZHTTPD ?= 1 |
| 225 | endif |
| 226 | |
| 227 | # Artifact/build variants (e.g., zhttp) |
| 228 | ifeq ($(ENABLE_ZHTTPD),1) |
| 229 | VARIANT_TAG := zhttp |
| 230 | endif |
| 231 | |
| 232 | # Build output directories (variant-aware) |
| 233 | BUILD_DIR := build/$(TARGET)/$(BUILD_TYPE)$(if $(VARIANT_TAG),-$(VARIANT_TAG),) |
| 234 | OBJ_DIR := $(BUILD_DIR)/obj |
| 235 | DEP_DIR := $(BUILD_DIR)/dep |
| 236 | BIN_DIR := $(BUILD_DIR) |
| 237 | |
| 238 | ARTIFACT_BASE := $(ARTIFACT_PREFIX)-$(PLATFORM_TAG)$(if $(VARIANT_TAG),-$(VARIANT_TAG),)-v$(VERSION) |
| 239 | |
| 240 | ifeq ($(TARGET),macos) |
| 241 | OUTPUT_ELF := $(BIN_DIR)/$(ARTIFACT_BASE) |
| 242 | else |
| 243 | OUTPUT_ELF := $(BIN_DIR)/$(ARTIFACT_BASE).elf |
| 244 | endif |
| 245 | OUTPUT_BIN := $(BIN_DIR)/$(ARTIFACT_BASE).bin |
| 246 | |
| 247 | OBJCOPY ?= objcopy |
| 248 | STRIP ?= strip |
| 249 | |
| 250 | ifeq ($(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 |
| 262 | endif |
| 263 | |
| 264 | #============================================================================ |
| 265 | # Enable with ENABLE_MCP=1 |
| 266 | #============================================================================ |
| 267 | |
| 268 | ifeq ($(ENABLE_MCP),) |
| 269 | ifneq ($(filter $(TARGET),ps4 ps5),) |
| 270 | ENABLE_MCP ?= 1 |
| 271 | else |
| 272 | ENABLE_MCP ?= 0 |
| 273 | endif |
| 274 | endif |
| 275 | |
| 276 | ifeq ($(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)) |
| 300 | endif |
| 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 | |
| 311 | override ENABLE_LIBARCHIVE ?= 0 |
| 312 | override ENABLE_LIBCURL ?= 0 |
| 313 | |
| 314 | # ── libarchive detection ────────────────────────────────────────────────── |
| 315 | ifeq ($(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 |
| 340 | endif |
| 341 | |
| 342 | ifeq ($(ENABLE_LIBARCHIVE),1) |
| 343 | CFLAGS += -DENABLE_LIBARCHIVE=1 |
| 344 | endif |
| 345 | |
| 346 | # ── libcurl detection ───────────────────────────────────────────────────── |
| 347 | ifeq ($(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 |
| 366 | endif |
| 367 | |
| 368 | ifeq ($(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 |
| 375 | endif |
| 376 | |
| 377 | # Object files (handle both src/ and mcp/src/ paths) |
| 378 | OBJECTS := $(patsubst src/%.c,$(OBJ_DIR)/%.o,$(filter src/%.c,$(SOURCES))) |
| 379 | OBJECTS += $(patsubst mcp/src/%.c,$(OBJ_DIR)/mcp/%.o,$(filter mcp/src/%.c,$(SOURCES))) |
| 380 | |
| 381 | # FFI Object files |
| 382 | FFI_SOURCES := ffi/c_core/pal_ffi.c |
| 383 | FFI_OBJECTS := $(patsubst ffi/%.c,$(OBJ_DIR)/ffi/%.o,$(FFI_SOURCES)) |
| 384 | |
| 385 | # Object files without main (for unit tests and ffi library) |
| 386 | LIB_OBJECTS := $(filter-out $(OBJ_DIR)/main.o,$(OBJECTS)) |
| 387 | |
| 388 | # Dependency files |
| 389 | DEPENDS := $(patsubst $(OBJ_DIR)/%.o,$(DEP_DIR)/%.d,$(filter-out $(OBJ_DIR)/mcp/%.o,$(OBJECTS))) |
| 390 | DEPENDS += $(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 | |
| 418 | ifeq ($(TARGET),ps5) |
| 419 | |
| 420 | HOOK_OBJ := $(OBJ_DIR)/ps5_net_filter_hook.o |
| 421 | HOOK_BIN := $(OBJ_DIR)/ps5_net_filter_hook.bin |
| 422 | HOOK_BLOB_H := src/ps5_net_filter_hook_blob.h |
| 423 | |
| 424 | # Kernel-safe compiler flags (MUST differ from normal CFLAGS) |
| 425 | HOOK_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 | |
| 441 | ps5-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 | |
| 459 | endif # TARGET=ps5 |
| 460 | |
| 461 | resources: |
| 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 |
| 468 | ifeq ($(TARGET),macos) |
| 469 | FFI_OUTPUT := $(BIN_DIR)/libzftpd_ffi.dylib |
| 470 | FFI_LDFLAGS := -dynamiclib |
| 471 | else |
| 472 | FFI_OUTPUT := $(BIN_DIR)/libzftpd_ffi.so |
| 473 | FFI_LDFLAGS := -shared |
| 474 | endif |
| 475 | |
| 476 | $(BIN_DIR) $(OBJ_DIR) $(DEP_DIR) $(BUILD_DIR)/tests $(OBJ_DIR)/ffi/c_core $(OBJ_DIR)/mcp: |
| 477 | @mkdir -p $@ |
| 478 | |
| 479 | ifeq ($(filter $(TARGET),ps4 ps5),) |
| 480 | all: $(OUTPUT_ELF) $(if $(ffi_langs),ffi) |
| 481 | else |
| 482 | all: $(OUTPUT_BIN) $(if $(ffi_langs),ffi) |
| 483 | endif |
| 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 |
| 497 | ffi: $(FFI_OUTPUT) |
| 498 | ifneq ($(findstring java,$(ffi_langs)),) |
| 499 | @echo " [FFI] Building Java bindings..." |
| 500 | @$(MAKE) ffi-java |
| 501 | endif |
| 502 | ifneq ($(findstring rust,$(ffi_langs)),) |
| 503 | @echo " [FFI] Building Rust bindings..." |
| 504 | @$(MAKE) ffi-rust |
| 505 | endif |
| 506 | ifneq ($(findstring python,$(ffi_langs)),) |
| 507 | @echo " [FFI] Building Python bindings..." |
| 508 | @$(MAKE) ffi-python |
| 509 | endif |
| 510 | ifneq ($(findstring go,$(ffi_langs)),) |
| 511 | @echo " [FFI] Building Go bindings..." |
| 512 | @$(MAKE) ffi-go |
| 513 | endif |
| 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). |
| 522 | TARGETS_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 |
| 530 | ZHTTP_VARIANTS ?= 0 1 |
| 531 | |
| 532 | JAVA_HOME_PATH ?= $(shell /usr/libexec/java_home) |
| 533 | |
| 534 | # Java FFI Target |
| 535 | .PHONY: ffi-java |
| 536 | ffi-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 |
| 556 | ffi-rust: $(FFI_OUTPUT) |
| 557 | @echo " [CARGO] Building Rust FFI bindings..." |
| 558 | @cd ffi/rust && cargo build --release |
| 559 | @echo " [CARGO] Running Rust FFI tests..." |
| 560 | ifeq ($(TARGET),macos) |
| 561 | @cd ffi/rust && DYLD_LIBRARY_PATH=../../build/macos/release cargo test |
| 562 | else |
| 563 | @cd ffi/rust && LD_LIBRARY_PATH=../../build/linux/release cargo test |
| 564 | endif |
| 565 | @echo "Rust FFI bindings built successfully." |
| 566 | |
| 567 | # Python FFI Target |
| 568 | .PHONY: ffi-python |
| 569 | ffi-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 |
| 578 | ffi-go: $(FFI_OUTPUT) |
| 579 | @echo " [GO] Compiling and Testing Go FFI bindings..." |
| 580 | ifeq ($(TARGET),macos) |
| 581 | @cd ffi/go/zftpd && DYLD_LIBRARY_PATH=../../../build/macos/release go test -v |
| 582 | else |
| 583 | @cd ffi/go/zftpd && LD_LIBRARY_PATH=../../../build/linux/release go test -v |
| 584 | endif |
| 585 | @echo "Go FFI bindings tested successfully." |
| 586 | |
| 587 | all-platforms: release-all |
| 588 | |
| 589 | release-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) |
| 597 | release-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 | |
| 609 | debug-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 |
| 641 | clean: |
| 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) |
| 651 | distclean: 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) |
| 658 | install: $(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 |
| 664 | analyze: |
| 665 | @echo "Running static analysis..." |
| 666 | @clang --analyze $(CFLAGS) $(SOURCES) |
| 667 | @echo "Analysis complete." |
| 668 | |
| 669 | # Run tests |
| 670 | TEST_BINS := $(BUILD_DIR)/tests/test_size |
| 671 | TEST_BINS += $(BUILD_DIR)/tests/test_security |
| 672 | TEST_BINS += $(BUILD_DIR)/tests/test_path_security |
| 673 | TEST_BINS += $(BUILD_DIR)/tests/test_buffer_pool |
| 674 | TEST_BINS += $(BUILD_DIR)/tests/test_scratch |
| 675 | TEST_BINS += $(BUILD_DIR)/tests/test_alloc |
| 676 | TEST_BINS += $(BUILD_DIR)/tests/test_mlst_ascii |
| 677 | TEST_BINS += $(BUILD_DIR)/tests/test_http_query |
| 678 | TEST_BINS += $(BUILD_DIR)/tests/test_http_confinement |
| 679 | |
| 680 | ifeq ($(filter $(TARGET),linux macos),) |
| 681 | test: $(OUTPUT_BIN) |
| 682 | @echo "Tests skipped for TARGET=$(TARGET)" |
| 683 | else |
| 684 | test: $(OUTPUT_ELF) $(OUTPUT_BIN) $(TEST_BINS) |
| 685 | @echo "Running tests..." |
| 686 | @for t in $(TEST_BINS); do ./$$t; done |
| 687 | endif |
| 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 | |
| 697 | bin: $(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; } |
| 702 | ifeq ($(filter $(TARGET),ps4 ps5),) |
| 703 | @$(OBJCOPY) -O binary $< $@ |
| 704 | else |
| 705 | @$(STRIP) --strip-unneeded -R .comment -R .GCC.command.line $< -o $@ |
| 706 | endif |
| 707 | |
| 708 | deploy: $(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 | |
| 718 | deploy-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 | |
| 728 | deploy-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 | |
| 741 | doctor-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 | |
| 760 | help: |
| 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 | |
| 802 | compile_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 | |
| 818 | WEB_DEPLOY_DIR ?= /data/zftpd/web |
| 819 | |
| 820 | web-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 |