mirror of
https://github.com/seregonwar/zftpd.git
synced 2026-10-19 01:59:57 -05:00
zhttp can now uninstall a title natively, reusing the same embedded helper that drives installs. Three changes make it work on 13.60: - install_helper.c escalates to the SYSTEM_PROCESS auth id (0x4800000000000006) by writing cr_sceAuthID at ucred+0x58 via kernel_copyin; the SDKs
1195 lines
46 KiB
Makefile
1195 lines
46 KiB
Makefile
# Multi-Platform FTP Server - Makefile
|
|
# Supports: Linux, macOS, PS3, PS4, PS5
|
|
# Standards: MISRA C:2012, CERT C, ISO C11
|
|
|
|
# Project information
|
|
PROJECT := zftpd
|
|
VERSION := $(shell grep -E 'define[[:space:]]+RELEASE_VERSION' include/ftp/ftp_config.h | head -n 1 | cut -d'"' -f2)
|
|
ifeq ($(strip $(VERSION)),)
|
|
VERSION := 0.0.0
|
|
endif
|
|
ARTIFACT_PREFIX ?= zftpd
|
|
HOST_ARCH := $(shell uname -m)
|
|
ifeq ($(TARGET),macos)
|
|
PLATFORM_TAG := macos-$(HOST_ARCH)
|
|
else ifeq ($(TARGET),linux)
|
|
PLATFORM_TAG := linux-$(HOST_ARCH)
|
|
else
|
|
PLATFORM_TAG := $(TARGET)
|
|
endif
|
|
|
|
# Host OS detection (for toolchain/linker compatibility)
|
|
HOST_OS := $(shell uname -s)
|
|
|
|
# Target platform (default detection)
|
|
ifeq ($(HOST_OS),Darwin)
|
|
TARGET ?= macos
|
|
else
|
|
TARGET ?= linux
|
|
endif
|
|
# Normalize target to lowercase so TARGET=PS5/PS4 still matches rules
|
|
TARGET := $(shell echo $(TARGET) | tr '[:upper:]' '[:lower:]')
|
|
|
|
# Build configuration
|
|
BUILD_TYPE ?= release
|
|
# Valid values: debug, release
|
|
|
|
PS4_PAYLOAD_SDK ?= $(abspath external/ps4-payload-sdk)
|
|
PS4_HOST ?= ps4
|
|
PS4_PORT ?= 9021
|
|
PS5_PAYLOAD_SDK ?= $(abspath external/ps5-payload-sdk)
|
|
PS5_HOST ?= ps5
|
|
PS5_PORT ?= 9021
|
|
|
|
#============================================================================
|
|
# PLATFORM DETECTION AND CONFIGURATION
|
|
#============================================================================
|
|
|
|
# Compiler selection
|
|
ifeq ($(TARGET),ps3)
|
|
CC := ppu-gcc
|
|
PLATFORM_DEFS := -DPLATFORM_PS3 -DPS3
|
|
PLATFORM_LIBS := -lnet
|
|
PLATFORM_LDFLAGS :=
|
|
endif
|
|
|
|
ifeq ($(TARGET),ps4)
|
|
ORBIS_LLVM_CONFIG := $(LLVM_CONFIG)
|
|
ifeq ($(ORBIS_LLVM_CONFIG),)
|
|
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)
|
|
endif
|
|
ifeq ($(ORBIS_LLVM_CONFIG),)
|
|
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)
|
|
endif
|
|
ifeq ($(ORBIS_LLVM_CONFIG),)
|
|
$(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)
|
|
endif
|
|
export LLVM_CONFIG := $(ORBIS_LLVM_CONFIG)
|
|
include $(PS4_PAYLOAD_SDK)/toolchain/orbis.mk
|
|
PLATFORM_DEFS := -DPLATFORM_PS4 -DPS4
|
|
# SceSysmodule is loaded at runtime with dlopen: the payload SDK does not
|
|
# ship a linkable library for it.
|
|
PLATFORM_LIBS := -lkernel -lpthread -lSceSystemService -lSceUserService
|
|
PLATFORM_LDFLAGS :=
|
|
endif
|
|
|
|
ifeq ($(TARGET),ps5)
|
|
PROSPERO_LLVM_CONFIG := $(LLVM_CONFIG)
|
|
ifeq ($(PROSPERO_LLVM_CONFIG),)
|
|
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)
|
|
endif
|
|
ifeq ($(PROSPERO_LLVM_CONFIG),)
|
|
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)
|
|
endif
|
|
ifeq ($(PROSPERO_LLVM_CONFIG),)
|
|
$(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)
|
|
endif
|
|
export LLVM_CONFIG := $(PROSPERO_LLVM_CONFIG)
|
|
include $(PS5_PAYLOAD_SDK)/toolchain/prospero.mk
|
|
PLATFORM_DEFS := -DPLATFORM_PS5 -DPS5 -D__PROSPERO__
|
|
PLATFORM_LIBS := -lkernel -lpthread -lSceNotification
|
|
PLATFORM_LDFLAGS :=
|
|
endif
|
|
|
|
ifeq ($(TARGET),linux)
|
|
CC := gcc
|
|
PLATFORM_DEFS := -DPLATFORM_LINUX -D_GNU_SOURCE
|
|
PLATFORM_LIBS := -lpthread
|
|
ifeq ($(HOST_OS),Linux)
|
|
PLATFORM_LDFLAGS := -Wl,-z,relro -Wl,-z,now
|
|
else
|
|
PLATFORM_LDFLAGS :=
|
|
endif
|
|
endif
|
|
|
|
ifeq ($(TARGET),macos)
|
|
CC := clang
|
|
PLATFORM_DEFS := -DPLATFORM_MACOS -D_DARWIN_C_SOURCE
|
|
PLATFORM_LIBS := -lpthread
|
|
PLATFORM_LDFLAGS :=
|
|
endif
|
|
|
|
# Default to GCC if no target matched
|
|
CC ?= gcc
|
|
CXX ?= c++
|
|
PLATFORM_LIBS ?= -lpthread
|
|
|
|
# Event loop implementation — kqueue for BSD/macOS/PS4/PS5, epoll for Linux
|
|
ifeq ($(TARGET),linux)
|
|
EVENT_LOOP_SRC := src/runtime/event_loop_epoll.c
|
|
else
|
|
EVENT_LOOP_SRC := src/runtime/event_loop_kqueue.c
|
|
endif
|
|
|
|
#============================================================================
|
|
# COMPILER FLAGS (SAFETY-CRITICAL STANDARDS)
|
|
#============================================================================
|
|
|
|
# C Standard
|
|
CFLAGS := -std=c11
|
|
|
|
# Warning flags (comprehensive)
|
|
CFLAGS += -Wall -Wextra -Wpedantic
|
|
CFLAGS += -Wformat=2 -Wformat-security
|
|
CFLAGS += -Wnull-dereference
|
|
CFLAGS += -Wstack-protector
|
|
CFLAGS += -Wstrict-overflow=5
|
|
CFLAGS += -Warray-bounds
|
|
CFLAGS += -Wcast-align
|
|
CFLAGS += -Wcast-qual
|
|
CFLAGS += -Wconversion
|
|
CFLAGS += -Wsign-conversion
|
|
CFLAGS += -Wstrict-prototypes
|
|
CFLAGS += -Wmissing-prototypes
|
|
CFLAGS += -Wredundant-decls
|
|
CFLAGS += -Wshadow
|
|
CFLAGS += -Wundef
|
|
CFLAGS += -Wwrite-strings
|
|
|
|
# Treat warnings as errors in release builds
|
|
ifeq ($(BUILD_TYPE),release)
|
|
CFLAGS += -Werror
|
|
endif
|
|
|
|
# Toolchain compatibility (Clang on macOS uses different warning set)
|
|
ifeq ($(HOST_OS),Darwin)
|
|
CFLAGS += -Wno-unknown-warning-option
|
|
endif
|
|
|
|
# Security hardening flags
|
|
CFLAGS += -fstack-protector-strong
|
|
CFLAGS += -fPIE
|
|
CFLAGS += -fno-strict-aliasing
|
|
|
|
# _FORTIFY_SOURCE is glibc-specific; avoid redef warnings on non-Linux hosts
|
|
ifeq ($(HOST_OS),Linux)
|
|
CFLAGS += -D_FORTIFY_SOURCE=2
|
|
endif
|
|
|
|
# Optimization and debug flags
|
|
ifeq ($(BUILD_TYPE),debug)
|
|
CFLAGS += -O0 -g3 -DDEBUG -DFTP_DEBUG=1
|
|
CFLAGS += -fsanitize=address,undefined
|
|
LDFLAGS += -fsanitize=address,undefined
|
|
else
|
|
CFLAGS += -O2 -g -DNDEBUG -DFTP_DEBUG=0
|
|
endif
|
|
|
|
# Platform-specific flags
|
|
CFLAGS += $(PLATFORM_DEFS)
|
|
|
|
# Avoid symbol interposition when injected into host processes (PS4/PS5 payload).
|
|
ifneq ($(filter $(TARGET),ps4 ps5),)
|
|
CFLAGS += -fvisibility=hidden
|
|
endif
|
|
|
|
# PS5: disable aggressive function inlining. Without this flag the compiler
|
|
# inlines every static api_* handler into http_api_handle, creating a massive
|
|
# stack frame (~60-80 KB) that overflows the PS5 thread stack (SIGSEGV).
|
|
ifeq ($(TARGET),ps5)
|
|
CFLAGS += -fno-inline-functions
|
|
endif
|
|
|
|
# Include directories
|
|
CFLAGS += -I./include
|
|
CFLAGS += -I./include/ftp -I./include/http -I./include/platform
|
|
CFLAGS += -I./include/platform/ps5 -I./include/archive -I./include/runtime
|
|
|
|
#============================================================================
|
|
# LINKER FLAGS
|
|
#============================================================================
|
|
|
|
# Console payloads are loaded by loaders (GoldHEN binloader, elfldr) that
|
|
# expect a position-independent executable, no matter which host built them.
|
|
ifneq ($(filter $(TARGET),ps4 ps5),)
|
|
LDFLAGS += -pie
|
|
else ifeq ($(HOST_OS),Linux)
|
|
LDFLAGS += -pie
|
|
endif
|
|
LDFLAGS += $(PLATFORM_LDFLAGS)
|
|
LIBS := $(PLATFORM_LIBS)
|
|
|
|
#============================================================================
|
|
# SOURCE FILES
|
|
#============================================================================
|
|
|
|
# Source files
|
|
SOURCES := src/platform/pal_network.c
|
|
SOURCES += src/platform/fileio/basic.c
|
|
SOURCES += src/platform/fileio/sendfile.c
|
|
SOURCES += src/platform/fileio/copy.c
|
|
SOURCES += src/platform/fileio/tree.c
|
|
SOURCES += src/platform/fileio/directory.c
|
|
SOURCES += src/platform/pal_alloc.c
|
|
SOURCES += src/platform/pal_scratch.c
|
|
SOURCES += src/platform/pal_notification.c
|
|
SOURCES += src/platform/pal_disc.c
|
|
SOURCES += src/platform/pal_volume.c
|
|
SOURCES += src/platform/pal_dns_filter.c
|
|
SOURCES += src/platform/pal_filesystem.c
|
|
SOURCES += src/platform/pal_filesystem_psx.c
|
|
SOURCES += src/ftp/ftp_path.c
|
|
SOURCES += src/ftp/ftp_server.c
|
|
SOURCES += src/ftp/ftp_session.c
|
|
SOURCES += src/ftp/ftp_protocol.c
|
|
SOURCES += src/ftp/commands/control.c
|
|
SOURCES += src/ftp/commands/listing.c
|
|
SOURCES += src/ftp/commands/transfer.c
|
|
SOURCES += src/ftp/commands/filesystem.c
|
|
SOURCES += src/ftp/commands/data_connection.c
|
|
SOURCES += src/ftp/commands/extensions.c
|
|
SOURCES += src/ftp/commands/information.c
|
|
SOURCES += src/ftp/commands/transfer_params.c
|
|
SOURCES += src/ftp/ftp_buffer_pool.c
|
|
SOURCES += src/ftp/ftp_log.c
|
|
SOURCES += src/ftp/ftp_crypto.c
|
|
SOURCES += src/app/main.c
|
|
SOURCES += src/app/instance_control.c
|
|
SOURCES += src/platform/pal_resilient_server.c
|
|
SOURCES += src/ftp/ftp_instance.c
|
|
|
|
# PS5 kernel syscall hook is opt-in until its firmware-specific install path
|
|
# has been validated on real hardware. The normal payload never loads it.
|
|
ENABLE_PS5_NET_FILTER ?= 0
|
|
ifeq ($(TARGET),ps5)
|
|
CFLAGS += -DENABLE_PS5_NET_FILTER=$(ENABLE_PS5_NET_FILTER)
|
|
endif
|
|
ifeq ($(TARGET),ps5)
|
|
SOURCES += src/platform/ps5/ps5_fw_offsets.c
|
|
ifeq ($(ENABLE_PS5_NET_FILTER),1)
|
|
SOURCES += src/platform/ps5/ps5_net_filter.c
|
|
endif
|
|
endif
|
|
|
|
#============================================================================
|
|
# ZHTTPD (Web File Explorer) — compile-time toggle
|
|
# Enabled by default on consoles (PS4/PS5), disabled on PC
|
|
#============================================================================
|
|
|
|
ifneq ($(filter $(TARGET),ps4 ps5),)
|
|
ENABLE_ZHTTPD ?= 1
|
|
else
|
|
ENABLE_ZHTTPD ?= 1
|
|
endif
|
|
|
|
# Artifact/build variants (e.g., zhttp)
|
|
ifeq ($(ENABLE_ZHTTPD),1)
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_query
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_confinement
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_notify
|
|
VARIANT_TAG := zhttp
|
|
endif
|
|
|
|
# Build output directories (variant-aware)
|
|
BUILD_DIR := build/$(TARGET)/$(BUILD_TYPE)$(if $(VARIANT_TAG),-$(VARIANT_TAG),)
|
|
OBJ_DIR := $(BUILD_DIR)/obj
|
|
DEP_DIR := $(BUILD_DIR)/dep
|
|
BIN_DIR := $(BUILD_DIR)
|
|
|
|
ARTIFACT_BASE := $(ARTIFACT_PREFIX)-$(PLATFORM_TAG)$(if $(VARIANT_TAG),-$(VARIANT_TAG),)-v$(VERSION)
|
|
|
|
ifeq ($(TARGET),macos)
|
|
OUTPUT_ELF := $(BIN_DIR)/$(ARTIFACT_BASE)
|
|
else
|
|
OUTPUT_ELF := $(BIN_DIR)/$(ARTIFACT_BASE).elf
|
|
endif
|
|
OUTPUT_BIN := $(BIN_DIR)/$(ARTIFACT_BASE).bin
|
|
|
|
OBJCOPY ?= objcopy
|
|
STRIP ?= strip
|
|
|
|
ifeq ($(ENABLE_ZHTTPD),1)
|
|
CFLAGS += -DENABLE_ZHTTPD=1
|
|
CFLAGS += -DENABLE_WEB_UPLOAD=1
|
|
# PKG installation rides along with the web interface; override with =0.
|
|
ENABLE_PKG_INSTALL ?= 1
|
|
CFLAGS += -DENABLE_PKG_INSTALL=$(ENABLE_PKG_INSTALL)
|
|
ifeq ($(TARGET),ps5)
|
|
ifeq ($(ENABLE_PKG_INSTALL),1)
|
|
# The system installer cannot be driven from a payload process, so a
|
|
# small helper program is embedded here and delivered at runtime. With
|
|
# the installer disabled the helper is neither built nor embedded.
|
|
INSTALL_HELPER_ELF := $(BUILD_DIR)/install_helper.elf
|
|
INSTALL_HELPER_BLOB := $(BUILD_DIR)/generated/install_helper_blob.c
|
|
SOURCES += $(INSTALL_HELPER_BLOB)
|
|
CFLAGS += -DZFTPD_INSTALL_HELPER=1
|
|
endif
|
|
SOURCES += src/http/games/ps5_install_helper.c
|
|
endif
|
|
ENABLE_LIBCURL ?= 1
|
|
SOURCES += $(EVENT_LOOP_SRC)
|
|
SOURCES += src/http/http_server.c
|
|
SOURCES += src/http/http_listener.c
|
|
SOURCES += src/http/http_upload.c
|
|
SOURCES += src/http/http_parser.c
|
|
SOURCES += src/http/http_response.c
|
|
SOURCES += src/http/http_response_stream.c
|
|
SOURCES += src/http/http_json.c
|
|
SOURCES += src/http/http_api.c
|
|
SOURCES += src/http/http_api_common.c
|
|
SOURCES += src/http/http_api_files.c
|
|
SOURCES += src/http/http_api_process.c
|
|
SOURCES += src/http/http_api_system.c src/http/games/common.c src/http/games/psx_install.c src/http/games/psx_appdb.c src/http/games/psx_launch.c src/http/games/admin_api.c src/http/games/metadata_api.c src/http/games/catalog.c
|
|
SOURCES += src/http/http_api_transfer.c
|
|
SOURCES += src/http/http_static.c
|
|
SOURCES += src/http/http_api_archive.c
|
|
SOURCES += src/http/http_api_dump.c
|
|
SOURCES += src/http/http_csrf.c
|
|
WEB_RESOURCE_FILES := $(shell find web -path web/legacy -prune -o -type f -print | sort)
|
|
HTTP_RESOURCES_C := $(BUILD_DIR)/generated/http/http_resources.c
|
|
SOURCES += $(HTTP_RESOURCES_C)
|
|
SOURCES += src/archive/exfat_unpacker.c
|
|
SOURCES += src/archive/pkg_unpacker.c
|
|
SOURCES += src/archive/builtin_unzip.c
|
|
SOURCES += src/archive/zip_writer.c
|
|
SOURCES += src/archive/archive_path.c
|
|
SOURCES += src/transfer/transfer_manager.c
|
|
SOURCES += src/transfer/transfer_state.c
|
|
SOURCES += src/transfer/dump_job.c
|
|
else
|
|
# Keep the switch visible in every translation unit either way.
|
|
CFLAGS += -DENABLE_ZHTTPD=0
|
|
endif
|
|
|
|
# NFS URL transfers are enabled by default for PS5 zhttp builds. Other targets
|
|
# can opt in explicitly with ENABLE_LIBNFS=1 when libnfs is installed.
|
|
ifeq ($(ENABLE_ZHTTPD),1)
|
|
ifeq ($(TARGET),ps5)
|
|
ENABLE_LIBNFS ?= 1
|
|
else
|
|
ENABLE_LIBNFS ?= 0
|
|
endif
|
|
else
|
|
ENABLE_LIBNFS := 0
|
|
endif
|
|
|
|
#============================================================================
|
|
# Enable with ENABLE_MCP=1
|
|
#============================================================================
|
|
|
|
ifeq ($(ENABLE_MCP),)
|
|
ENABLE_MCP ?= 0
|
|
endif
|
|
|
|
ifeq ($(ENABLE_MCP),1)
|
|
CFLAGS += -DENABLE_MCP=1
|
|
CFLAGS += -I./mcp/include
|
|
CFLAGS += -I./external/sJson-main/src
|
|
# JSON configuration for sJson
|
|
CFLAGS += -DJSON_MAX_DEPTH=32
|
|
CFLAGS += -DJSON_MAX_STRING_LEN=65536
|
|
CFLAGS += -DJSON_MAX_NODES=16384
|
|
SOURCES += mcp/src/mcp_protocol.c
|
|
SOURCES += mcp/src/mcp_server.c
|
|
SOURCES += mcp/src/mcp_handlers.c
|
|
SOURCES += external/sJson-main/src/sJson.c
|
|
SOURCES += $(EVENT_LOOP_SRC)
|
|
# Execution modules
|
|
SOURCES += mcp/src/mcp_execution/payload.c
|
|
SOURCES += mcp/src/mcp_execution/syscall_race.c
|
|
# Hunter modules (Zero-Day detection)
|
|
SOURCES += mcp/src/mcp_hunter/process_monitor.c
|
|
SOURCES += mcp/src/mcp_hunter/vuln_fuzzer.c
|
|
SOURCES += mcp/src/mcp_hunter/exploit_chain.c
|
|
SOURCES += mcp/src/mcp_hunter/jit_compiler.c
|
|
# Additional include path for hunter headers
|
|
CFLAGS += -I./mcp/src/mcp_hunter
|
|
$(info [INFO] MCP kernel analysis module enabled (with Zero-Day Hunter))
|
|
endif
|
|
|
|
#============================================================================
|
|
# OPTIONAL LIBRARIES — enable with ENABLE_LIBARCHIVE=1 / ENABLE_LIBCURL=1
|
|
#
|
|
# When enabled, the Makefile verifies that the required header is actually
|
|
# available. For desktop (gcc/clang) it uses a compiler probe. For
|
|
# PS4/PS5 cross-compilers the probe fails (missing sysroot headers),
|
|
# so we fall back to a simple file-existence check on bundled headers.
|
|
#============================================================================
|
|
|
|
# ── libarchive: zhttp only; auto-detect on desktop ─────────────────────
|
|
override ENABLE_LIBCURL ?= 0
|
|
ifeq ($(ENABLE_ZHTTPD),1)
|
|
ifeq ($(TARGET),ps5)
|
|
# Optional PacBrew library for non-ZIP formats; ZIP keeps the builtin reader.
|
|
override ENABLE_LIBARCHIVE ?= 0
|
|
ifeq ($(ENABLE_LIBARCHIVE),1)
|
|
PS5_PKG_CONFIG ?= $(PS5_PAYLOAD_SDK)/bin/prospero-pkg-config
|
|
_HAS_PS5_ARCHIVE := $(shell test -x $(PS5_PKG_CONFIG) && $(PS5_PKG_CONFIG) --exists libarchive && echo 1 || echo 0)
|
|
ifneq ($(_HAS_PS5_ARCHIVE),1)
|
|
$(error PS5 archive extraction requires PacBrew ps5-payload-libarchive)
|
|
endif
|
|
CFLAGS += -DENABLE_LIBARCHIVE=1 $(shell $(PS5_PKG_CONFIG) --cflags libarchive)
|
|
LIBS += $(shell $(PS5_PKG_CONFIG) --static --libs libarchive)
|
|
$(info [INFO] PS5 archive extraction: PacBrew libarchive)
|
|
endif
|
|
else ifeq ($(TARGET),ps4)
|
|
# PS4 builds use the builtin ZIP backend.
|
|
override ENABLE_LIBARCHIVE := 0
|
|
else
|
|
# Desktop (macOS/Linux): auto-detect unless user explicitly set it to 0
|
|
ifneq ($(ENABLE_LIBARCHIVE),0)
|
|
_HAS_ARCHIVE := 0
|
|
# 1) System installation
|
|
_SYS_ARCHIVE := $(shell echo '\#include <archive.h>' | $(CC) -xc -fsyntax-only - 2>/dev/null && echo 1 || echo 0)
|
|
ifeq ($(_SYS_ARCHIVE),1)
|
|
_HAS_ARCHIVE := 1
|
|
LIBS += -larchive
|
|
$(info [INFO] libarchive: using system installation)
|
|
endif
|
|
# 2) Homebrew keg (libarchive is keg-only on macOS)
|
|
ifeq ($(_HAS_ARCHIVE),0)
|
|
ifeq ($(HOST_OS),Darwin)
|
|
_BREW_ARCHIVE_PREFIX := $(shell brew --prefix libarchive 2>/dev/null)
|
|
ifneq ($(_BREW_ARCHIVE_PREFIX),)
|
|
_HB_ARCHIVE := $(shell echo '\#include <archive.h>' | $(CC) -xc -fsyntax-only -I$(_BREW_ARCHIVE_PREFIX)/include - 2>/dev/null && echo 1 || echo 0)
|
|
ifeq ($(_HB_ARCHIVE),1)
|
|
_HAS_ARCHIVE := 1
|
|
CFLAGS += -I$(_BREW_ARCHIVE_PREFIX)/include
|
|
LDFLAGS += -L$(_BREW_ARCHIVE_PREFIX)/lib
|
|
LIBS += -larchive
|
|
$(info [INFO] libarchive: using Homebrew keg $(_BREW_ARCHIVE_PREFIX))
|
|
endif
|
|
endif
|
|
endif
|
|
endif
|
|
# 3) Bundled headers fallback (external/libarchive-3.8.6)
|
|
ifeq ($(_HAS_ARCHIVE),0)
|
|
_BUNDLED_ARCHIVE_H := $(wildcard external/libarchive-3.8.6/libarchive/archive.h)
|
|
ifneq ($(_BUNDLED_ARCHIVE_H),)
|
|
_HAS_ARCHIVE := 1
|
|
CFLAGS += -I./external/libarchive-3.8.6/libarchive
|
|
ifeq ($(wildcard external/libarchive-3.8.6-compiled/.libs/libarchive.a),)
|
|
LIBS += -larchive
|
|
else
|
|
LIBS += external/libarchive-3.8.6-compiled/.libs/libarchive.a
|
|
endif
|
|
$(info [INFO] libarchive: using bundled headers (external/libarchive-3.8.6))
|
|
endif
|
|
endif
|
|
# 4) Not found — disable
|
|
ifeq ($(_HAS_ARCHIVE),0)
|
|
$(info [INFO] libarchive not found — extraction disabled (install: brew install libarchive))
|
|
override ENABLE_LIBARCHIVE := 0
|
|
else
|
|
override ENABLE_LIBARCHIVE := 1
|
|
CFLAGS += -DENABLE_LIBARCHIVE=1
|
|
endif
|
|
endif
|
|
endif
|
|
else
|
|
override ENABLE_LIBARCHIVE := 0
|
|
endif
|
|
|
|
# ── libcurl detection ─────────────────────────────────────────────────────
|
|
# zftpd deliberately uses external libcurl instead of maintaining an HTTP/TLS
|
|
# reimplementation. PS5 consumes the official PacBrew ps5-payload-curl port.
|
|
ifeq ($(ENABLE_LIBCURL),1)
|
|
ifeq ($(TARGET),ps5)
|
|
PS5_PKG_CONFIG ?= $(PS5_PAYLOAD_SDK)/bin/prospero-pkg-config
|
|
_HAS_PS5_CURL := $(shell test -x $(PS5_PKG_CONFIG) && $(PS5_PKG_CONFIG) --exists libcurl && echo 1 || echo 0)
|
|
ifneq ($(_HAS_PS5_CURL),1)
|
|
$(error PS5 zhttp requires PacBrew ps5-payload-curl)
|
|
endif
|
|
CFLAGS += -DENABLE_LIBCURL=1 $(shell $(PS5_PKG_CONFIG) --cflags libcurl)
|
|
LIBS += $(shell $(PS5_PKG_CONFIG) --libs libcurl)
|
|
PS5_CA_BUNDLE ?= $(PS5_PAYLOAD_SDK)/target/user/homebrew/etc/ca-bundle.crt
|
|
ifeq ($(wildcard $(PS5_CA_BUNDLE)),)
|
|
$(error PS5 libcurl CA bundle not found: $(PS5_CA_BUNDLE))
|
|
endif
|
|
GENERATED_CA_HEADER := $(BUILD_DIR)/generated/zftpd_ca_bundle.h
|
|
CFLAGS += -DZFTPD_EMBEDDED_CA_BUNDLE=1 -I$(BUILD_DIR)/generated
|
|
CURL_CA_BUNDLE_SRC := $(PS5_CA_BUNDLE)
|
|
$(info [INFO] URL downloads: external PacBrew libcurl + embedded CA bundle)
|
|
else ifeq ($(TARGET),ps4)
|
|
# PacBrew ps4-openorbis portlibs (libcurl + mbedTLS + zlib) as unpacked by
|
|
# tools/fetch_pacbrew_ps4.sh. Only the headers and the static libraries
|
|
# are used: the curl wrapper the package ships emits the OpenOrbis
|
|
# toolchain's own link flags, which do not belong in a payload-SDK link.
|
|
PS4_PACBREW_ROOT ?= $(shell for d in $(abspath external/pacbrew/ps4/openorbis) /opt/pacbrew/ps4/openorbis; do [ -f "$$d/usr/lib/libcurl.a" ] && { printf '%s' "$$d"; break; }; done)
|
|
|
|
ifneq ($(strip $(PS4_PACBREW_ROOT)),)
|
|
# The OpenOrbis build reaches errno through musl's __errno_location()
|
|
# and uses fnmatch()/execl(); the payload libc has none of them.
|
|
SOURCES += src/platform/ps4/ps4_pacbrew_compat.c
|
|
CFLAGS += -DENABLE_LIBCURL=1 -DCURL_STATICLIB -I$(PS4_PACBREW_ROOT)/usr/include
|
|
LIBS += -L$(PS4_PACBREW_ROOT)/usr/lib -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lz -lSceNet
|
|
# Same trust-store contract as PS5: HTTPS verification must not degrade
|
|
# silently, so the Mozilla bundle is embedded at build time.
|
|
PS4_CA_BUNDLE ?= $(PS4_PACBREW_ROOT)/usr/etc/ca-bundle.crt
|
|
ifeq ($(wildcard $(PS4_CA_BUNDLE)),)
|
|
$(error PS4 libcurl CA bundle not found: $(PS4_CA_BUNDLE) — re-run tools/fetch_pacbrew_ps4.sh or set PS4_CA_BUNDLE=<path>)
|
|
endif
|
|
GENERATED_CA_HEADER := $(BUILD_DIR)/generated/zftpd_ca_bundle.h
|
|
CFLAGS += -DZFTPD_EMBEDDED_CA_BUNDLE=1 -I$(BUILD_DIR)/generated
|
|
CURL_CA_BUNDLE_SRC := $(PS4_CA_BUNDLE)
|
|
$(info [INFO] URL downloads: PacBrew PS4 portlibs ($(PS4_PACBREW_ROOT)) + embedded CA bundle)
|
|
else
|
|
# A toolchain-provided pkg-config entry (or curl-config) still works.
|
|
# Probe the target environment only — a host libcurl would link code built
|
|
# for the wrong OS.
|
|
PS4_PKG_CONFIG ?= $(shell for c in $(PS4_PAYLOAD_SDK)/bin/orbis-pkg-config $(PS4_PAYLOAD_SDK)/bin/openorbis-pkg-config $(OPENORBIS)/bin/openorbis-pkg-config; do [ -x "$$c" ] && { printf '%s' "$$c"; break; }; done)
|
|
PS4_CURL_CONFIG ?= $(shell for c in $(PS4_PAYLOAD_SDK)/bin/orbis-curl-config $(PS4_PAYLOAD_SDK)/bin/curl-config $(PS4_PAYLOAD_SDK)/bin/openorbis-curl-config; do [ -x "$$c" ] && { printf '%s' "$$c"; break; }; done)
|
|
|
|
_HAS_PS4_CURL := $(shell if [ -n "$(strip $(PS4_PKG_CONFIG))" ] && $(PS4_PKG_CONFIG) --exists libcurl >/dev/null 2>&1; then echo 1; elif [ -n "$(strip $(PS4_CURL_CONFIG))" ]; then echo 1; fi)
|
|
|
|
ifneq ($(_HAS_PS4_CURL),1)
|
|
$(info [INFO] PS4 external libcurl not found — URL downloader disabled)
|
|
$(info [INFO] run tools/fetch_pacbrew_ps4.sh to install PacBrew ps4-openorbis-portlibs)
|
|
$(info [INFO] or point PS4_PKG_CONFIG / PS4_CURL_CONFIG at a target config tool)
|
|
override ENABLE_LIBCURL := 0
|
|
else
|
|
ifneq ($(strip $(PS4_PKG_CONFIG)),)
|
|
PS4_CURL_CFLAGS := $(shell $(PS4_PKG_CONFIG) --cflags libcurl)
|
|
PS4_CURL_LIBS := $(shell $(PS4_PKG_CONFIG) --static --libs libcurl)
|
|
else
|
|
PS4_CURL_CFLAGS := $(shell $(PS4_CURL_CONFIG) --cflags)
|
|
PS4_CURL_LIBS := $(shell $(PS4_CURL_CONFIG) --static-libs)
|
|
endif
|
|
CFLAGS += -DENABLE_LIBCURL=1 $(PS4_CURL_CFLAGS)
|
|
LIBS += $(PS4_CURL_LIBS)
|
|
|
|
# Same trust-store contract as PS5: the PacBrew curl package ships the
|
|
# Mozilla bundle, and HTTPS verification must not degrade silently.
|
|
PS4_CA_BUNDLE ?= $(PS4_PAYLOAD_SDK)/target/user/homebrew/etc/ca-bundle.crt
|
|
ifeq ($(wildcard $(PS4_CA_BUNDLE)),)
|
|
$(error PS4 libcurl CA bundle not found: $(PS4_CA_BUNDLE) — override with PS4_CA_BUNDLE=<path>)
|
|
endif
|
|
GENERATED_CA_HEADER := $(BUILD_DIR)/generated/zftpd_ca_bundle.h
|
|
CFLAGS += -DZFTPD_EMBEDDED_CA_BUNDLE=1 -I$(BUILD_DIR)/generated
|
|
CURL_CA_BUNDLE_SRC := $(PS4_CA_BUNDLE)
|
|
$(info [INFO] URL downloads: PacBrew PS4 libcurl + embedded CA bundle)
|
|
endif
|
|
endif
|
|
else
|
|
_HAS_CURL := $(shell echo '\#include <curl/curl.h>' | $(CC) -xc -fsyntax-only - 2>/dev/null && echo 1 || echo 0)
|
|
ifneq ($(_HAS_CURL),1)
|
|
$(info [INFO] libcurl headers not found — disabling ENABLE_LIBCURL)
|
|
override ENABLE_LIBCURL := 0
|
|
else
|
|
CFLAGS += -DENABLE_LIBCURL=1
|
|
LIBS += -lcurl
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
ifeq ($(ENABLE_LIBCURL),1)
|
|
SOURCES += src/transfer/backend_curl.c
|
|
endif
|
|
|
|
# Native BitTorrent engine for magnet links. PS5 needs a libtorrent-rasterbar
|
|
# port installed into its payload SDK; host libraries must never be linked
|
|
# into a console payload.
|
|
ENABLE_LIBTORRENT ?= 0
|
|
ifeq ($(ENABLE_LIBTORRENT),1)
|
|
ifeq ($(TARGET),ps5)
|
|
PS5_PKG_CONFIG ?= $(PS5_PAYLOAD_SDK)/bin/prospero-pkg-config
|
|
ifneq ($(shell test -x $(PS5_PKG_CONFIG) && $(PS5_PKG_CONFIG) --exists libtorrent-rasterbar && echo 1),1)
|
|
$(error PS5 magnet downloads require a PS5 libtorrent-rasterbar port in the payload SDK)
|
|
endif
|
|
TORRENT_CFLAGS := $(shell $(PS5_PKG_CONFIG) --cflags libtorrent-rasterbar)
|
|
TORRENT_LIBS := $(shell $(PS5_PKG_CONFIG) --static --libs libtorrent-rasterbar)
|
|
else
|
|
ifneq ($(shell pkg-config --exists libtorrent-rasterbar 2>/dev/null && echo 1),1)
|
|
$(error ENABLE_LIBTORRENT=1 requires libtorrent-rasterbar development files)
|
|
endif
|
|
TORRENT_CFLAGS := $(shell pkg-config --cflags libtorrent-rasterbar)
|
|
TORRENT_LIBS := $(shell pkg-config --libs libtorrent-rasterbar)
|
|
ifeq ($(HOST_OS),Darwin)
|
|
TORRENT_BOOST_PREFIX := $(shell brew --prefix boost 2>/dev/null)
|
|
TORRENT_CFLAGS += -I$(TORRENT_BOOST_PREFIX)/include
|
|
TORRENT_OPENSSL_PREFIX := $(shell brew --prefix openssl@3 2>/dev/null)
|
|
TORRENT_CFLAGS += -I$(TORRENT_OPENSSL_PREFIX)/include
|
|
endif
|
|
endif
|
|
CFLAGS += -DENABLE_LIBTORRENT=1
|
|
TORRENT_CXXFLAGS := -std=c++17 -Wall -Wextra -Wpedantic -Werror -O2 -fPIE \
|
|
$(PLATFORM_DEFS) -I./include $(TORRENT_CFLAGS)
|
|
ifeq ($(TARGET),ps5)
|
|
TORRENT_CXXFLAGS += -fvisibility=hidden -fno-inline-functions
|
|
endif
|
|
LIBS += $(TORRENT_LIBS)
|
|
TORRENT_OBJECT := $(OBJ_DIR)/transfer/backend_torrent.o
|
|
CXX_LINK := $(CXX)
|
|
else
|
|
CXX_LINK := $(CC)
|
|
endif
|
|
|
|
# ── libnfs detection ──────────────────────────────────────────────────────
|
|
ifeq ($(ENABLE_LIBNFS),1)
|
|
ifeq ($(TARGET),ps5)
|
|
PS5_PKG_CONFIG ?= $(PS5_PAYLOAD_SDK)/bin/prospero-pkg-config
|
|
_HAS_LIBNFS := $(shell test -x $(PS5_PKG_CONFIG) && $(PS5_PKG_CONFIG) --exists libnfs && echo 1 || echo 0)
|
|
ifneq ($(_HAS_LIBNFS),1)
|
|
$(error PS5 NFS transfers require PacBrew ps5-payload-libnfs)
|
|
endif
|
|
CFLAGS += -DENABLE_LIBNFS=1 $(shell $(PS5_PKG_CONFIG) --cflags libnfs)
|
|
LIBS += $(shell $(PS5_PKG_CONFIG) --libs libnfs)
|
|
else
|
|
_HAS_LIBNFS := $(shell pkg-config --exists libnfs 2>/dev/null && echo 1 || echo 0)
|
|
ifneq ($(_HAS_LIBNFS),1)
|
|
$(error ENABLE_LIBNFS=1 requires libnfs development files)
|
|
endif
|
|
CFLAGS += -DENABLE_LIBNFS=1 $(shell pkg-config --cflags libnfs)
|
|
LIBS += $(shell pkg-config --static --libs libnfs)
|
|
endif
|
|
SOURCES += src/transfer/backend_nfs.c
|
|
endif
|
|
|
|
# Object files (handle both src/ and mcp/src/ paths)
|
|
OBJECTS := $(patsubst src/%.c,$(OBJ_DIR)/%.o,$(filter src/%.c,$(SOURCES)))
|
|
OBJECTS += $(patsubst mcp/src/%.c,$(OBJ_DIR)/mcp/%.o,$(filter mcp/src/%.c,$(SOURCES)))
|
|
OBJECTS += $(patsubst $(BUILD_DIR)/generated/%.c,$(OBJ_DIR)/generated/%.o,$(filter $(BUILD_DIR)/generated/%.c,$(SOURCES)))
|
|
OBJECTS += $(TORRENT_OBJECT)
|
|
|
|
# FFI Object files
|
|
FFI_SOURCES := ffi/c_core/pal_ffi.c
|
|
FFI_OBJECTS := $(patsubst ffi/%.c,$(OBJ_DIR)/ffi/%.o,$(FFI_SOURCES))
|
|
|
|
# Object files without main (for unit tests and ffi library)
|
|
LIB_OBJECTS := $(filter-out $(OBJ_DIR)/app/main.o,$(OBJECTS))
|
|
|
|
# Dependency files
|
|
DEPENDS := $(patsubst $(OBJ_DIR)/%.o,$(DEP_DIR)/%.d,$(filter-out $(OBJ_DIR)/mcp/%.o,$(OBJECTS)))
|
|
DEPENDS += $(patsubst $(OBJ_DIR)/mcp/%.o,$(DEP_DIR)/mcp/%.d,$(filter $(OBJ_DIR)/mcp/%.o,$(OBJECTS)))
|
|
|
|
#============================================================================
|
|
# BUILD TARGETS
|
|
#============================================================================
|
|
|
|
.PHONY: all clean distclean install test help bin deploy deploy-i deploy-nc doctor-ps4
|
|
.PHONY: all-platforms release-all debug-all ffi ffi-java ffi-rust ffi-python resources
|
|
.PHONY: ps5-hook-blob
|
|
|
|
# ============================================================================
|
|
# PS5 NET FILTER HOOK — Kernel-safe compilation pipeline
|
|
#
|
|
# The hook functions (src/platform/ps5/ps5_net_filter_hook.c) run in ring-0 (kernel mode)
|
|
# and require special compiler flags that differ from the normal build.
|
|
#
|
|
# Pipeline:
|
|
# 1. Compile hook with kernel-safe flags → ps5_net_filter_hook.o
|
|
# 2. Extract .text.hook_connect and .text.hook_sendto sections → .bin
|
|
# 3. Generate C byte-array header → ps5_net_filter_hook_blob.h
|
|
#
|
|
# The blob header is included by ps5_net_filter.c to replace the placeholder
|
|
# byte arrays (g_hook_connect_code[], g_hook_sendto_code[]).
|
|
#
|
|
# Run manually before the PS5 build:
|
|
# make ps5-hook-blob
|
|
# ============================================================================
|
|
|
|
ifeq ($(TARGET),ps5)
|
|
|
|
HOOK_OBJ := $(OBJ_DIR)/ps5_net_filter_hook.o
|
|
HOOK_BIN := $(OBJ_DIR)/ps5_net_filter_hook.bin
|
|
HOOK_BLOB_H := $(BUILD_DIR)/generated/ps5/ps5_net_filter_hook_blob.h
|
|
|
|
# Kernel-safe compiler flags (MUST differ from normal CFLAGS)
|
|
HOOK_CFLAGS := \
|
|
-DPS5_HOOK_BUILD \
|
|
-DPLATFORM_PS5 \
|
|
-std=c11 \
|
|
-O2 \
|
|
-fno-stack-protector \
|
|
-mno-red-zone \
|
|
-fPIC \
|
|
-mcmodel=large \
|
|
-fno-plt \
|
|
-fno-common \
|
|
-fno-builtin \
|
|
-fno-exceptions \
|
|
-fomit-frame-pointer \
|
|
-I include/platform/ps5/
|
|
|
|
ps5-hook-blob: $(HOOK_BLOB_H)
|
|
@echo " [BLOB] $< generated ($(shell wc -c < $(HOOK_BIN) 2>/dev/null || echo '?') bytes)"
|
|
|
|
$(HOOK_BLOB_H): $(HOOK_BIN)
|
|
@echo " [XXD] $@"
|
|
@mkdir -p $(dir $@)
|
|
@xxd -i $< > $@
|
|
|
|
$(HOOK_BIN): $(HOOK_OBJ)
|
|
@echo " [OBJCOPY] $@"
|
|
@$(OBJCOPY) -O binary \
|
|
--only-section=.text.hook_connect \
|
|
--only-section=.text.hook_sendto \
|
|
$< $@
|
|
|
|
$(HOOK_OBJ): src/platform/ps5/ps5_net_filter_hook.c | $(OBJ_DIR)
|
|
@echo " [HOOK-CC] $<"
|
|
@$(CC) $(HOOK_CFLAGS) -c $< -o $@
|
|
|
|
endif # TARGET=ps5
|
|
|
|
resources: $(HTTP_RESOURCES_C)
|
|
|
|
$(HTTP_RESOURCES_C): tools/generate_resources.py $(WEB_RESOURCE_FILES)
|
|
@echo " [GEN] $@"
|
|
@mkdir -p $(dir $@)
|
|
@python3 tools/generate_resources.py > $@
|
|
|
|
.DEFAULT_GOAL := all
|
|
|
|
# FFI Shared Library output
|
|
ifeq ($(TARGET),macos)
|
|
FFI_OUTPUT := $(BIN_DIR)/libzftpd_ffi.dylib
|
|
FFI_LDFLAGS := -dynamiclib
|
|
else
|
|
FFI_OUTPUT := $(BIN_DIR)/libzftpd_ffi.so
|
|
FFI_LDFLAGS := -shared
|
|
endif
|
|
|
|
$(BIN_DIR) $(OBJ_DIR) $(DEP_DIR) $(BUILD_DIR)/tests $(OBJ_DIR)/ffi/c_core $(OBJ_DIR)/mcp:
|
|
@mkdir -p $@
|
|
|
|
ifeq ($(filter $(TARGET),ps4 ps5),)
|
|
all: $(OUTPUT_ELF) $(if $(ffi_langs),ffi)
|
|
else
|
|
all: $(OUTPUT_BIN) $(if $(ffi_langs),ffi)
|
|
endif
|
|
|
|
$(PROJECT): all
|
|
@true
|
|
|
|
# Link executable
|
|
$(OUTPUT_ELF): $(OBJECTS) | $(BIN_DIR)
|
|
@echo " [LD] $@"
|
|
@mkdir -p $(BIN_DIR)
|
|
@$(CXX_LINK) $(LDFLAGS) -o $@ $^ $(LIBS)
|
|
@echo "Build complete: $(PROJECT) ($(TARGET), $(BUILD_TYPE))"
|
|
|
|
# FFI Build Targets
|
|
.PHONY: ffi
|
|
ffi: $(FFI_OUTPUT)
|
|
ifneq ($(findstring java,$(ffi_langs)),)
|
|
@echo " [FFI] Building Java bindings..."
|
|
@$(MAKE) ffi-java
|
|
endif
|
|
ifneq ($(findstring rust,$(ffi_langs)),)
|
|
@echo " [FFI] Building Rust bindings..."
|
|
@$(MAKE) ffi-rust
|
|
endif
|
|
ifneq ($(findstring python,$(ffi_langs)),)
|
|
@echo " [FFI] Building Python bindings..."
|
|
@$(MAKE) ffi-python
|
|
endif
|
|
ifneq ($(findstring go,$(ffi_langs)),)
|
|
@echo " [FFI] Building Go bindings..."
|
|
@$(MAKE) ffi-go
|
|
endif
|
|
|
|
$(FFI_OUTPUT): $(LIB_OBJECTS) $(FFI_OBJECTS) | $(BIN_DIR)
|
|
@echo " [LD] $@ (Shared Library)"
|
|
@mkdir -p $(BIN_DIR)
|
|
@$(CXX_LINK) $(LDFLAGS) $(FFI_LDFLAGS) -fPIC -o $@ $(LIB_OBJECTS) $(FFI_OBJECTS) $(LIBS)
|
|
@echo "FFI C-Core built: $@"
|
|
|
|
# Build all supported platforms (best-effort: includes only toolchains found on the host).
|
|
TARGETS_ALL ?= $(shell \
|
|
echo macos; \
|
|
command -v gcc >/dev/null 2>&1 && echo linux || true; \
|
|
command -v ppu-gcc >/dev/null 2>&1 && echo ps3 || true; \
|
|
[ -d external/ps4-payload-sdk ] && echo ps4 || true; \
|
|
[ -d external/ps5-payload-sdk ] && echo ps5 || true)
|
|
|
|
# Build matrix toggles for ZHTTPD variant
|
|
ZHTTP_VARIANTS ?= 0 1
|
|
|
|
JAVA_HOME_PATH ?= $(shell /usr/libexec/java_home)
|
|
|
|
# Java FFI Target
|
|
.PHONY: ffi-java
|
|
ffi-java: $(FFI_OUTPUT)
|
|
@echo " [JAVAC] Compiling Java FFI bindings..."
|
|
@mkdir -p $(BIN_DIR)/ffi/java
|
|
@javac -J-Xint -d $(BIN_DIR)/ffi/java ffi/java/src/main/java/org/zftpd/ffi/*.java
|
|
@echo " [JAVAC] Compiling Java FFI tests..."
|
|
@javac -J-Xint -cp $(BIN_DIR)/ffi/java -d $(BIN_DIR)/ffi/java ffi/java/src/test/java/org/zftpd/ffi/*.java
|
|
@echo " [CC] Compiling JNI C wrapper..."
|
|
@$(CC) $(CFLAGS) $(FFI_LDFLAGS) -fPIC \
|
|
-I"$(JAVA_HOME_PATH)/include" \
|
|
-I"$(JAVA_HOME_PATH)/include/darwin" \
|
|
-I"./include" -I"./ffi/c_core" \
|
|
-o $(BIN_DIR)/libzftpd_ffi_java$(suffix $(FFI_OUTPUT)) \
|
|
ffi/java/src/main/c/pal_ffi_jni.c \
|
|
-L$(BIN_DIR) -lzftpd_ffi $(LIBS)
|
|
@echo " [JAVA] Running FFI tests..."
|
|
@java -Xint -Djava.library.path=$(BIN_DIR) -cp $(BIN_DIR)/ffi/java org.zftpd.ffi.FfiTests
|
|
@echo "Java FFI bindings built successfully."
|
|
|
|
# Rust FFI Target
|
|
.PHONY: ffi-rust
|
|
ffi-rust: $(FFI_OUTPUT)
|
|
@echo " [CARGO] Building Rust FFI bindings..."
|
|
@cd ffi/rust && cargo build --release
|
|
@echo " [CARGO] Running Rust FFI tests..."
|
|
ifeq ($(TARGET),macos)
|
|
@cd ffi/rust && DYLD_LIBRARY_PATH=../../build/macos/release cargo test
|
|
else
|
|
@cd ffi/rust && LD_LIBRARY_PATH=../../build/linux/release cargo test
|
|
endif
|
|
@echo "Rust FFI bindings built successfully."
|
|
|
|
# Python FFI Target
|
|
.PHONY: ffi-python
|
|
ffi-python: $(FFI_OUTPUT)
|
|
@echo " [PYTHON] Installing dependencies..."
|
|
@python3 -m pip install -q cffi pytest
|
|
@echo " [PYTHON] Running Python FFI tests..."
|
|
@cd ffi/python && PYTHONPATH=. pytest tests/
|
|
@echo "Python FFI bindings tested successfully."
|
|
|
|
# Go FFI Target
|
|
.PHONY: ffi-go
|
|
ffi-go: $(FFI_OUTPUT)
|
|
@echo " [GO] Compiling and Testing Go FFI bindings..."
|
|
ifeq ($(TARGET),macos)
|
|
@cd ffi/go/zftpd && DYLD_LIBRARY_PATH=../../../build/macos/release go test -v
|
|
else
|
|
@cd ffi/go/zftpd && LD_LIBRARY_PATH=../../../build/linux/release go test -v
|
|
endif
|
|
@echo "Go FFI bindings tested successfully."
|
|
|
|
all-platforms: release-all
|
|
|
|
release-all:
|
|
@set -e; \
|
|
for t in $(TARGETS_ALL); do \
|
|
echo "==> Building $$t (release)"; \
|
|
$(MAKE) TARGET=$$t BUILD_TYPE=release clean all; \
|
|
done
|
|
|
|
# Build all platforms in release with/without ZHTTPD (produces ELF and BIN where applicable)
|
|
release-matrix:
|
|
@set -e; \
|
|
for t in $(TARGETS_ALL); do \
|
|
for z in $(ZHTTP_VARIANTS); do \
|
|
echo "==> Building $$t (release, ENABLE_ZHTTPD=$$z)"; \
|
|
$(MAKE) TARGET=$$t BUILD_TYPE=release ENABLE_ZHTTPD=$$z \
|
|
ENABLE_LIBARCHIVE=$(ENABLE_LIBARCHIVE) \
|
|
ENABLE_LIBCURL=$(ENABLE_LIBCURL) \
|
|
clean all; \
|
|
done; \
|
|
done
|
|
|
|
debug-all:
|
|
@set -e; \
|
|
for t in $(TARGETS_ALL); do \
|
|
echo "==> Building $$t (debug)"; \
|
|
$(MAKE) TARGET=$$t BUILD_TYPE=debug clean all; \
|
|
done
|
|
# Generate the embedded trust store from PacBrew's Mozilla CA bundle (PS4/PS5).
|
|
ifneq ($(strip $(CURL_CA_BUNDLE_SRC)),)
|
|
$(OBJ_DIR)/transfer/backend_curl.o: $(GENERATED_CA_HEADER)
|
|
|
|
$(GENERATED_CA_HEADER): $(CURL_CA_BUNDLE_SRC) tools/embed_binary.py
|
|
@echo " [GEN] $@"
|
|
@python3 tools/embed_binary.py $< $@ zftpd_ca_bundle
|
|
endif
|
|
|
|
# Compile C source files
|
|
$(OBJ_DIR)/transfer/backend_torrent.o: src/transfer/backend_torrent.cpp | $(OBJ_DIR) $(DEP_DIR)
|
|
@echo " [CXX] $<"
|
|
@mkdir -p $(dir $@) $(dir $(DEP_DIR)/transfer/backend_torrent.d)
|
|
@$(CXX) $(TORRENT_CXXFLAGS) -MMD -MP -MF $(DEP_DIR)/transfer/backend_torrent.d -MT $@ -c $< -o $@
|
|
|
|
$(OBJ_DIR)/%.o: src/%.c | $(OBJ_DIR) $(DEP_DIR)
|
|
@echo " [CC] $<"
|
|
@mkdir -p $(dir $@) $(dir $(DEP_DIR)/$*.d)
|
|
@$(CC) $(CFLAGS) -MMD -MP -MF $(DEP_DIR)/$*.d -MT $@ -c $< -o $@
|
|
|
|
# Compile generated C sources without writing into the source tree.
|
|
$(OBJ_DIR)/generated/%.o: $(BUILD_DIR)/generated/%.c | $(OBJ_DIR) $(DEP_DIR)
|
|
@echo " [CC] $<"
|
|
@mkdir -p $(dir $@) $(dir $(DEP_DIR)/generated/$*.d)
|
|
@$(CC) $(CFLAGS) -MMD -MP -MF $(DEP_DIR)/generated/$*.d -MT $@ -c $< -o $@
|
|
|
|
# Compile FFI C source files (with -fPIC for shared library)
|
|
$(OBJ_DIR)/ffi/%.o: ffi/%.c | $(OBJ_DIR)/ffi/c_core
|
|
@echo " [CC] $< (FFI)"
|
|
@mkdir -p $(dir $@) $(dir $(DEP_DIR)/ffi/$*.d)
|
|
@$(CC) $(CFLAGS) -fPIC -MMD -MP -MF $(DEP_DIR)/ffi/$*.d -MT $@ -c $< -o $@
|
|
|
|
# Compile MCP C source files
|
|
$(OBJ_DIR)/mcp/%.o: mcp/src/%.c | $(OBJ_DIR)/mcp
|
|
@echo " [CC] $< (MCP)"
|
|
@mkdir -p $(dir $@) $(dir $(DEP_DIR)/mcp/$*.d)
|
|
@$(CC) $(CFLAGS) -MMD -MP -MF $(DEP_DIR)/mcp/$*.d -MT $@ -c $< -o $@
|
|
|
|
# Include dependency files
|
|
-include $(DEPENDS)
|
|
|
|
#============================================================================
|
|
# UTILITY TARGETS
|
|
#============================================================================
|
|
|
|
# Clean build artifacts
|
|
clean:
|
|
@echo "Cleaning build artifacts..."
|
|
@rm -rf $(BUILD_DIR)
|
|
@rm -f src/*.o src/*.d
|
|
@rm -f tests/*.o tests/*.d
|
|
@rm -f $(PROJECT) $(PROJECT).elf $(PROJECT).bin
|
|
@rm -f tests/test_size
|
|
@echo "Clean complete."
|
|
|
|
# Deep clean (including configuration)
|
|
distclean: clean
|
|
@echo "Removing all generated files..."
|
|
@rm -f *~ core
|
|
@rm -rf build
|
|
@echo "Distclean complete."
|
|
|
|
# Install (simple copy for now)
|
|
install: $(OUTPUT_ELF)
|
|
@echo "Installing $(PROJECT)..."
|
|
@install -D -m 0755 $(OUTPUT_ELF) $(DESTDIR)/usr/local/bin/$(PROJECT)
|
|
@echo "Install complete."
|
|
|
|
# Run static analysis
|
|
analyze:
|
|
@echo "Running static analysis..."
|
|
@clang --analyze $(CFLAGS) $(SOURCES)
|
|
@echo "Analysis complete."
|
|
|
|
# Run tests
|
|
TEST_BINS := $(BUILD_DIR)/tests/test_size
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_security
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_path_security
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_buffer_pool
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_scratch
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_alloc
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_mlst_ascii
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_list_flag
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_instance
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_instance_control
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_chmod
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_copy_atomic
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_ftp_commands
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_fileio_tree
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_fileio_basic
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_sendfile_console
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_ps5_fw_offsets
|
|
ifeq ($(ENABLE_ZHTTPD),1)
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_transfer
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_transfer_parallel
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_transfer_state
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_transfer_queue
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_transfer_orphans
|
|
ifeq ($(ENABLE_LIBTORRENT),1)
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_transfer_torrent
|
|
endif
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_parser
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_upload
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_server
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_response_stream
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_api_common
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_files
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_process
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_system
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_games
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_archive_path
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_builtin_unzip
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_zip_writer
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_pal_volume
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_http_archive
|
|
TEST_BINS += $(BUILD_DIR)/tests/test_dump_job
|
|
endif
|
|
|
|
ifeq ($(filter $(TARGET),linux macos),)
|
|
test: $(OUTPUT_BIN)
|
|
@echo "Tests skipped for TARGET=$(TARGET)"
|
|
else
|
|
test: $(OUTPUT_ELF) $(TEST_BINS)
|
|
@echo "Running tests..."
|
|
@set -e; for t in $(TEST_BINS); do ./$$t; done
|
|
endif
|
|
|
|
$(BUILD_DIR)/tests/test_instance_control: tests/test_instance_control.c src/app/instance_control.c tests/instance_control_stubs/sys/user.h tests/instance_control_stubs/sys/sysctl.h $(LIB_OBJECTS) | $(BUILD_DIR)/tests
|
|
@echo " [CC] $<"
|
|
@$(CXX_LINK) $(CFLAGS) -Itests/instance_control_stubs -o $@ -x c $< -x none $(LIB_OBJECTS) $(LDFLAGS) $(LIBS)
|
|
|
|
$(BUILD_DIR)/tests/test_sendfile_console: tests/test_sendfile_console.c src/platform/fileio/sendfile.c tests/sendfile_stubs/sys/mount.h $(LIB_OBJECTS) | $(BUILD_DIR)/tests
|
|
@echo " [CC] $<"
|
|
@$(CXX_LINK) $(CFLAGS) -Itests/sendfile_stubs -o $@ -x c $< -x none $(LIB_OBJECTS) $(LDFLAGS) $(LIBS)
|
|
|
|
$(BUILD_DIR)/tests/test_http_query: tests/test_http_query.c $(LIB_OBJECTS) | $(BUILD_DIR)/tests
|
|
@echo " [CC] $<"
|
|
@$(CXX_LINK) $(CFLAGS) -DFTP_AUTH_DELAY=0 -DFTP_PORT_ALLOW_FOREIGN_IP=1 -o $@ -x c $< -x none $(LIB_OBJECTS) $(LDFLAGS) $(LIBS)
|
|
|
|
$(BUILD_DIR)/tests/test_transfer_torrent: tests/test_transfer_torrent.cpp $(TORRENT_OBJECT) | $(BUILD_DIR)/tests
|
|
@echo " [CXX] $<"
|
|
@$(CXX) $(TORRENT_CXXFLAGS) -o $@ $< $(TORRENT_OBJECT) $(LDFLAGS) $(TORRENT_LIBS) -lpthread
|
|
|
|
$(BUILD_DIR)/tests/%: tests/%.c $(LIB_OBJECTS) | $(BUILD_DIR)/tests
|
|
@echo " [CC] $<"
|
|
@$(CXX_LINK) $(CFLAGS) -DFTP_AUTH_DELAY=0 -DFTP_PORT_ALLOW_FOREIGN_IP=1 -o $@ -x c $< -x none $(LIB_OBJECTS) $(LDFLAGS) $(LIBS)
|
|
|
|
bin: $(OUTPUT_BIN)
|
|
|
|
$(OUTPUT_BIN): $(OUTPUT_ELF) | $(BIN_DIR)
|
|
@echo " [STRIP] $@"
|
|
@command -v $(OBJCOPY) >/dev/null 2>&1 || { echo "error: '$(OBJCOPY)' not found (set OBJCOPY=... or install binutils)"; exit 1; }
|
|
ifeq ($(filter $(TARGET),ps4 ps5),)
|
|
@$(OBJCOPY) -O binary $< $@
|
|
else
|
|
@$(STRIP) --strip-unneeded -R .comment -R .GCC.command.line $< -o $@
|
|
endif
|
|
|
|
deploy: $(OUTPUT_BIN)
|
|
@if [ "$(TARGET)" = "ps4" ]; then \
|
|
command -v socat >/dev/null 2>&1 || { echo "error: 'socat' non trovato (brew install socat)"; exit 1; }; \
|
|
$(PS4_DEPLOY) -h $(PS4_HOST) -p $(PS4_PORT) $(OUTPUT_BIN); \
|
|
elif [ "$(TARGET)" = "ps5" ]; then \
|
|
$(PS5_DEPLOY) -h $(PS5_HOST) -p $(PS5_PORT) $(OUTPUT_BIN); \
|
|
else \
|
|
echo "error: deploy supportato solo con TARGET=ps4 o TARGET=ps5"; exit 1; \
|
|
fi
|
|
|
|
deploy-i: $(OUTPUT_BIN)
|
|
@if [ "$(TARGET)" = "ps4" ]; then \
|
|
command -v socat >/dev/null 2>&1 || { echo "error: 'socat' non trovato (brew install socat)"; exit 1; }; \
|
|
$(PS4_DEPLOY) -h $(PS4_HOST) -p $(PS4_PORT) -i $(OUTPUT_BIN); \
|
|
elif [ "$(TARGET)" = "ps5" ]; then \
|
|
$(PS5_DEPLOY) -h $(PS5_HOST) -p $(PS5_PORT) -i $(OUTPUT_BIN); \
|
|
else \
|
|
echo "error: deploy-i supportato solo con TARGET=ps4 o TARGET=ps5"; exit 1; \
|
|
fi
|
|
|
|
deploy-nc: $(OUTPUT_BIN)
|
|
@if [ "$(TARGET)" = "ps4" ]; then \
|
|
command -v nc >/dev/null 2>&1 || { echo "error: 'nc' (netcat) non trovato"; exit 1; }; \
|
|
echo "Sending $(OUTPUT_BIN) to $(PS4_HOST):$(PS4_PORT) via nc..."; \
|
|
nc -w 10 $(PS4_HOST) $(PS4_PORT) < $(OUTPUT_BIN); \
|
|
elif [ "$(TARGET)" = "ps5" ]; then \
|
|
command -v nc >/dev/null 2>&1 || { echo "error: 'nc' (netcat) non trovato"; exit 1; }; \
|
|
echo "Sending $(OUTPUT_BIN) to $(PS5_HOST):$(PS5_PORT) via nc..."; \
|
|
nc -w 10 $(PS5_HOST) $(PS5_PORT) < $(OUTPUT_BIN); \
|
|
else \
|
|
echo "error: deploy-nc supportato solo con TARGET=ps4 o TARGET=ps5"; exit 1; \
|
|
fi
|
|
|
|
doctor-ps4:
|
|
@echo "TARGET=ps4 prerequisiti (macOS/Homebrew)"
|
|
@echo ""
|
|
@echo "llvm-config:"
|
|
@{ command -v llvm-config >/dev/null 2>&1 && echo " OK: $$(command -v llvm-config)" || true; }
|
|
@{ [ -x "/opt/homebrew/opt/llvm/bin/llvm-config" ] && echo " OK: /opt/homebrew/opt/llvm/bin/llvm-config" || true; }
|
|
@echo ""
|
|
@echo "ld.lld:"
|
|
@{ command -v ld.lld >/dev/null 2>&1 && echo " OK: $$(command -v ld.lld)" || true; }
|
|
@{ 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; }
|
|
@echo ""
|
|
@echo "Se manca qualcosa:"
|
|
@echo " brew install llvm lld"
|
|
@echo " export PATH=\"/opt/homebrew/opt/llvm/bin:/opt/homebrew/opt/lld/bin:$$PATH\""
|
|
|
|
#============================================================================
|
|
# HELP
|
|
#============================================================================
|
|
|
|
help:
|
|
@echo "Multi-Platform FTP Server Build System"
|
|
@echo "========================================"
|
|
@echo ""
|
|
@echo "Targets:"
|
|
@echo " all - Build the FTP server (default)"
|
|
@echo " clean - Remove build artifacts"
|
|
@echo " distclean - Deep clean (remove all generated files)"
|
|
@echo " install - Install to system (requires root)"
|
|
@echo " analyze - Run static analysis (requires clang)"
|
|
@echo " test - Run test suite"
|
|
@echo " help - Display this help message"
|
|
@echo " resources - Generate embedded web resources under build/.../generated"
|
|
@echo ""
|
|
@echo "Variables:"
|
|
@echo " TARGET - Target platform (linux, macos, ps3, ps4, ps5)"
|
|
@echo " BUILD_TYPE - Build configuration (debug, release)"
|
|
@echo " ENABLE_LIBARCHIVE - Enable archive extraction (0/1, requires libarchive)"
|
|
@echo " ENABLE_LIBCURL - Enable URL downloads (0/1, requires libcurl)"
|
|
@echo " WEB_DEPLOY_DIR - Deprecated (web UI is now embedded in binary)"
|
|
@echo ""
|
|
@echo "Examples:"
|
|
@echo " make # Build for Linux (release)"
|
|
@echo " make TARGET=macos # Build for macOS"
|
|
@echo " make TARGET=ps5 # Build for PS5"
|
|
@echo " make BUILD_TYPE=debug # Build debug version"
|
|
@echo " make TARGET=ps4 BUILD_TYPE=debug # PS4 debug build"
|
|
@echo " make ENABLE_LIBARCHIVE=1 ENABLE_LIBCURL=1 # With extract + download"
|
|
@echo " make resources # Regenerate embedded web resources"
|
|
@echo ""
|
|
@echo "Current configuration:"
|
|
@echo " Target: $(TARGET)"
|
|
@echo " Build type: $(BUILD_TYPE)"
|
|
@echo " Compiler: $(CC)"
|
|
@echo " libarchive: $(ENABLE_LIBARCHIVE)"
|
|
@echo " libcurl: $(ENABLE_LIBCURL)"
|
|
@echo ""
|
|
|
|
#============================================================================
|
|
# COMPILATION DATABASE (for IDE/LSP support)
|
|
#============================================================================
|
|
|
|
compile_commands.json:
|
|
@echo "Generating compilation database..."
|
|
@bear -- make clean all
|
|
@echo "Compilation database generated."
|
|
|
|
.PHONY: compile_commands.json
|
|
|
|
#============================================================================
|
|
#============================================================================
|
|
# WEB DEPLOY — DEPRECATED
|
|
#
|
|
# All web assets are now embedded directly in the binary via
|
|
# http_resources.c (run make resources to regenerate).
|
|
# The web-deploy target is kept as a no-op for backward compatibility.
|
|
#============================================================================
|
|
|
|
web-deploy:
|
|
@echo " [WEB] web-deploy is deprecated — web UI is now embedded in the binary"
|
|
@echo " [WEB] Resources are generated automatically under build/.../generated"
|
|
@echo " [WEB] Done — $(shell find web -path web/legacy -prune -o \( -name '*.css' -o -name '*.js' \) -print | wc -l | tr -d ' ') files deployed"
|
|
|
|
#============================================================================
|
|
# Flag changes must invalidate object files.
|
|
#
|
|
# Objects do not record the flags they were built with, so toggling a feature
|
|
# (ENABLE_LIBTORRENT, ENABLE_PKG_INSTALL, ...) used to relink stale objects and
|
|
# fail with undefined symbols. The stamp is rewritten only when the flags
|
|
# actually change, and every object depends on it, so a real change rebuilds.
|
|
#============================================================================
|
|
|
|
FLAG_STAMP := $(OBJ_DIR)/.build-flags
|
|
|
|
.PHONY: FORCE
|
|
FORCE:
|
|
|
|
$(FLAG_STAMP): FORCE
|
|
@mkdir -p $(OBJ_DIR)
|
|
@printf '%s\n' '$(CFLAGS)' '$(LDFLAGS)' '$(LIBS)' > $@.tmp
|
|
@if ! cmp -s $@.tmp $@; then \
|
|
echo " [FLAGS] build flags changed — objects will be rebuilt"; \
|
|
mv -f $@.tmp $@; \
|
|
fi
|
|
@rm -f $@.tmp
|
|
|
|
$(OBJECTS) $(LIB_OBJECTS): $(FLAG_STAMP)
|
|
|
|
#============================================================================
|
|
# PS5 installer helper
|
|
#============================================================================
|
|
|
|
ifneq ($(INSTALL_HELPER_ELF),)
|
|
$(INSTALL_HELPER_ELF): src/platform/ps5/install_helper/install_helper.c | $(BIN_DIR)
|
|
@echo " [CC] $<"
|
|
@$(CXX_LINK) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) -lSceIpmi -lSceAppInstUtil
|
|
|
|
$(INSTALL_HELPER_BLOB): $(INSTALL_HELPER_ELF) tools/gen_blob.py
|
|
@mkdir -p $(dir $@)
|
|
@python3 tools/gen_blob.py $< install_helper_elf $@
|
|
|
|
$(OBJ_DIR)/generated/install_helper_blob.o: $(INSTALL_HELPER_BLOB)
|
|
endif
|