# The MIT License (MIT)
#
# Copyright (C) 2026 Scott Duensing
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

# DVX GUI  -- Top-level Makefile
#
# Builds the full DVX stack: core library, task switcher,
# bootstrap loader, text help library, widgets, shell, and apps.
#
# Source tree mirrors bin/ layout:
#   src/libs/kpunch/<libname>/   -> bin/libs/kpunch/<libname>/<libname>.lib
#   src/widgets/kpunch/<widget>/ -> bin/widgets/kpunch/<widget>/<widget>.wgt
#   src/apps/kpunch/<app>/       -> bin/apps/kpunch/<app>/<app>.app
#   src/loader/                  -> bin/DVX.EXE
#   src/tools/                   -> bin/host/<tool>

# Libraries are discovered, never listed: every src/libs/kpunch/<name>/
# with a Makefile is one.  Run-time load order comes from each library's
# .dep file; the build only needs the core libraries first (and the shell
# before the task manager, which links against it).
LIBS      = $(sort $(notdir $(patsubst %/Makefile,%,$(wildcard src/libs/kpunch/*/Makefile))))
CORE_LIBS = libdvx libtasks

.PHONY: all clean $(LIBS) loader widgets apps savers tools installer dist deploy-helpsrc compile-help deploy-sdk test test-quick test-asan test-host test-modules test-samples coverage test-qa test-fuzz bench bench-compare

# Every build ends by making the release media in dist/ (a second or
# two), so dist/ always holds the latest DVX.
all: $(LIBS) loader tools widgets apps savers installer deploy-helpsrc compile-help deploy-sdk
	src/installer/mkDist.sh $(or $(FLOPPY),1.44M)

$(LIBS):
	$(MAKE) -C src/libs/kpunch/$@

$(filter-out $(CORE_LIBS),$(LIBS)): $(CORE_LIBS)

taskmgr: dvxshell

loader: libdvx libtasks
	$(MAKE) -C src/loader

# Every .wgt recipe runs bin/host/dvxres, so widgets need the tools.
widgets: libdvx libtasks texthelp listhelp richtext tools
	$(MAKE) -C src/widgets/kpunch

tools:
	$(MAKE) -C src/tools

# SETUP.EXE (bin/setup.exe, a static build of the core it needs, with
# fonts mkfont makes) and the package tool bin/host/dvxpkg.
installer: tools
	$(MAKE) -C src/installer

# Release media in dist/: the CD image and floppy images (FLOPPY=360K,
# 720K, 1.2M, 1.44M (default), 2.88M or a number of bytes per disk).
# See src/installer/mkDist.sh.  `all` makes them; `make dist` is the
# same build under the name people look for.
dist: all

apps: libdvx libtasks dvxshell tools
	$(MAKE) -C src/apps/kpunch

# The screen savers (bin/shared/savers): BASIC ones need bascomp, which
# the apps build makes.
savers: apps
	$(MAKE) -C src/savers/kpunch

# Copies help sources into the module directories under bin/, so every
# module that owns one must be built first.
deploy-helpsrc: $(LIBS) widgets apps tools
	$(MAKE) -C src/tools deploy-helpsrc

# Host-side tests.  Every suite is native gcc; the GUI suites build
# obj/host/libdvx.a, the widget .so files and libdvxtest.a themselves
# (sanitizers on by default, SAN=1).
#   make test        -- everything: DOS module symbol check, BASIC
#                       harnesses + fuzz, sample goldens, proxy build, all
#                       host suites, tools goldens, lint gate
#   make test-quick  -- same minus the fuzzers, static analyzer and tools
#   make test-asan   -- BASIC harnesses rebuilt under ASan/UBSan plus the
#                       host suites (which are already sanitized)
DVXBASIC_DIR = src/apps/kpunch/dvxbasic

# HOST_SUITES: the one list of host suites (shared with runCoverage.sh).
include src/test/suites.mk

test: apps
	$(MAKE) test-modules
	$(MAKE) -C $(DVXBASIC_DIR) tests
	$(MAKE) -C src/tools/proxy
	$(MAKE) test-samples
	$(MAKE) test-host
	src/test/tools/runTools.sh
	$(MAKE) -C src/test/lint

test-quick: apps
	$(MAKE) test-modules
	$(MAKE) -C $(DVXBASIC_DIR) tests FUZZ_N=0
	$(MAKE) test-samples
	$(MAKE) test-host
	$(MAKE) -C src/test/lint style ascii

test-asan: apps
	$(MAKE) test-modules
	$(MAKE) -C $(DVXBASIC_DIR) tests TESTSAN=1
	$(MAKE) test-host

# Runs every suite even after a failure, then fails naming the failed ones.
test-host:
	@failed=""; for d in $(HOST_SUITES); do $(MAKE) -C src/test/$$d || failed="$$failed $$d"; done; \
	if [ -n "$$failed" ]; then echo "test-host: FAILED suites:$$failed"; exit 1; fi

test-samples:
	src/test/samples/runSamples.sh

# Every unresolved symbol of every built DOS module (bin/ .lib .wgt .drv
# .app .dxe, plus the BASIC stub that compiled programs are made from) must
# be exported by dvx.exe or by a module loaded before it, or dlopen() fails
# on DOS (src/test/shell/checkDxeExports.sh, src/test/tools/checkModules.py).
# Builds every module first so the check sees the current binaries.
BASIC_STUB = obj/dvxbasic/basstub.app

test-modules: $(LIBS) loader widgets apps savers
	src/test/shell/checkDxeExports.sh . $(BASIC_STUB)

# BASIC compiler / VM benchmarks (src/test/basic/basBench.c, programs in
# src/test/basic/bench): results in obj/test/bench/results.txt;
# bench-compare shows the change from BENCH_OLD (default the Phase 0
# baseline, src/test/basic/bench/baseline.txt) to BENCH_NEW (default the
# last make bench).  BENCH_ARGS passes basBench options (-runs N, -profile,
# -filter NAME).
bench:
	$(MAKE) -C src/test/basic bench

bench-compare:
	$(MAKE) -C src/test/basic bench-compare $(if $(BENCH_OLD),BENCH_OLD=$(abspath $(BENCH_OLD))) $(if $(BENCH_NEW),BENCH_NEW=$(abspath $(BENCH_NEW)))

# The real DOS build in an emulator (src/test/dos/runDos.sh; not part of
# make test).  EMULATOR=86box (default) or dosbox; SCRIPTS= a subset.
test-dos: all
	src/test/dos/runDos.sh $(or $(EMULATOR),86box) --no-build $(SCRIPTS)

# Line coverage of the host suites: rebuilds everything with gcc
# --coverage (no sanitizers) under obj/hostcov, runs the BASIC
# harnesses, sample goldens and every host suite, then writes the
# report to obj/coverage/html and prints a per-directory summary.
coverage: tools
	$(MAKE) -C src/test/coverage

# Report-only quality tools beyond make test (claudeDocs/qaTools.md):
# test-qa runs the clang host build, the lizard complexity report,
# MemorySanitizer and valgrind (src/test/lint qa; clang-tidy is in the
# lint gate, so in make test); test-fuzz runs
# every libFuzzer harness for FUZZ_SECONDS (default 60) each
# (src/test/fuzz run).  Neither is part of make test.
test-qa:
	$(MAKE) -C src/test/lint qa

test-fuzz:
	$(MAKE) -C src/test/fuzz run

HLPC = bin/host/dvxhlpc

HELP_FILES = src/tools/helpFiles.sh

# The system help: the user guide (userHelp.dhs, kept beside Program
# Manager; Program Manager's own progman.dhs is its separate progman.hlp),
# then the library reference found by $(HELP_FILES), then the widget
# reference.  Libraries, apps and widgets are discovered, so adding one
# needs no edit here.
USER_GUIDE_DHS = src/apps/kpunch/progman/userHelp.dhs

BASIC_DHS = src/apps/kpunch/dvxbasic/ideguide.dhs src/apps/kpunch/dvxbasic/langref.dhs \
	src/apps/kpunch/dvxbasic/ctrlover.dhs src/apps/kpunch/dvxbasic/form.dhs \
	src/apps/kpunch/dvxbasic/userctl.dhs

# Needs bin/host/dvxhlpc, and every module built and its help deployed
# (library and app help are classified by the modules found in bin/).
# Each app's own help (<app>.dhs beside its source) becomes <app>.hlp
# next to <app>.app.
compile-help: deploy-helpsrc
	@mkdir -p docs
	$(HLPC) -o bin/apps/kpunch/dvxhelp/dvxhelp.hlp \
		--html docs/dvx_help_viewer.html \
		src/apps/kpunch/dvxhelp/help.dhs
	libHelp=$$($(HELP_FILES) libs) && \
	$(HLPC) -o bin/apps/kpunch/progman/dvxhelp.hlp \
		--html docs/dvx_system_reference.html \
		-i src/libs/kpunch/libdvx \
		$(USER_GUIDE_DHS) \
		$$libHelp \
		src/widgets/kpunch/wgtsys.dhs \
		$$(find src/widgets/kpunch -name "*.dhs" ! -path "*/wgtsys.dhs" | sort)
	$(HLPC) -o bin/apps/kpunch/dvxbasic/dvxbasic.hlp \
		--html docs/dvx_basic_reference.html \
		$(BASIC_DHS) \
		$$(find src/widgets/kpunch -name "*.bhs" | sort)
	$(HELP_FILES) compile $(HLPC)

SDKDIR = bin/sdk

# Public header lists (SDK_*_H), shared with the ABI size test.
SDK_ROOT =
include src/sdkHeaders.mk

# The SDK's own files (README, sdk.mk, newProject.sh, project templates)
# live in src/sdk; its bin/ holds the host tools a project build runs.
SDK_SRC   = src/sdk
SDK_TOOLS = bin/host/dvxres bin/host/dvxhlpc bin/host/bascomp bin/host/mkfont bin/host/dvxpkg src/tools/dxeGen.sh

# The host tools come from tools (dvxres, dvxhlpc, mkfont), apps (bascomp),
# and installer (dvxpkg).
deploy-sdk: tools apps installer
	@echo "Building SDK..."
	@# Start from an empty tree so a header dropped from the lists goes away.
	rm -rf $(SDKDIR)
	@mkdir -p $(SDKDIR)/bin $(SDKDIR)/include/core $(SDKDIR)/include/shell $(SDKDIR)/include/tasks $(SDKDIR)/include/sql $(SDKDIR)/include/texthelp $(SDKDIR)/include/listhelp $(SDKDIR)/include/richtext $(SDKDIR)/include/fileops $(SDKDIR)/include/hlpfile $(SDKDIR)/include/xfer $(SDKDIR)/include/conn $(SDKDIR)/include/print $(SDKDIR)/include/game $(SDKDIR)/include/joystick $(SDKDIR)/include/audio $(SDKDIR)/include/music $(SDKDIR)/include/pkg $(SDKDIR)/include/json $(SDKDIR)/include/http $(SDKDIR)/include/pkgnet $(SDKDIR)/include/net $(SDKDIR)/include/basic $(SDKDIR)/samples/basic/basdemo $(SDKDIR)/samples/basic/widshow
	cp $(SDK_CORE_H) $(SDKDIR)/include/core/
	cp $(SDK_SHELL_H) $(SDKDIR)/include/shell/
	cp $(SDK_TASKS_H) $(SDKDIR)/include/tasks/
	cp $(SDK_SQL_H) $(SDKDIR)/include/sql/
	cp $(SDK_TEXTHELP_H) $(SDKDIR)/include/texthelp/
	cp $(SDK_LISTHELP_H) $(SDKDIR)/include/listhelp/
	cp $(SDK_RICHTEXT_H) $(SDKDIR)/include/richtext/
	cp $(SDK_FILEOPS_H) $(SDKDIR)/include/fileops/
	cp $(SDK_HLPFILE_H) $(SDKDIR)/include/hlpfile/
	cp $(SDK_XFER_H) $(SDKDIR)/include/xfer/
	cp $(SDK_CONN_H) $(SDKDIR)/include/conn/
	cp $(SDK_PRINT_H) $(SDKDIR)/include/print/
	cp $(SDK_GAME_H) $(SDKDIR)/include/game/
	cp $(SDK_JOYSTICK_H) $(SDKDIR)/include/joystick/
	cp $(SDK_AUDIO_H) $(SDKDIR)/include/audio/
	cp $(SDK_MUSIC_H) $(SDKDIR)/include/music/
	cp $(SDK_PKG_H) $(SDKDIR)/include/pkg/
	cp $(SDK_JSON_H) $(SDKDIR)/include/json/
	cp $(SDK_HTTP_H) $(SDKDIR)/include/http/
	cp $(SDK_PKGNET_H) $(SDKDIR)/include/pkgnet/
	@# Networking: dvxNet.h and the standard socket headers, which keep their
	@# subdirectories (sys/, netinet/, arpa/); sdk.mk adds include/net only
	@# for programs that set NET = 1.
	cp $(SDK_NET_H) $(SDKDIR)/include/net/
	@for h in $(SDK_NET_STD_H); do \
		mkdir -p $(SDKDIR)/include/net/$$(dirname $$h) && cp $(SDK_NET_DIR)/$$h $(SDKDIR)/include/net/$$h || exit 1; \
	done
	@for h in $(SDK_SERIAL_H); do \
		mkdir -p $(SDKDIR)/include/serial/$$(dirname $$h) && cp $(SDK_SERIAL_DIR)/$$h $(SDKDIR)/include/serial/$$h || exit 1; \
	done
	@# Widget headers -- one subdir per widget
	@for h in $(SDK_WIDGET_H); do \
		wgt=$$(basename $$(dirname $$h)); \
		mkdir -p $(SDKDIR)/include/widget/$$wgt && cp $$h $(SDKDIR)/include/widget/$$wgt/ || exit 1; \
	done
	@# BASIC include files
	@cp src/include/basic/*.bas $(SDKDIR)/include/basic/
	@# BASIC sample: basdemo (project file + form + class module + icon + help source)
	@cp src/apps/kpunch/basdemo/basdemo.dbp  $(SDKDIR)/samples/basic/basdemo/
	@cp src/apps/kpunch/basdemo/basdemo.frm  $(SDKDIR)/samples/basic/basdemo/
	@cp src/apps/kpunch/basdemo/shape.cls    $(SDKDIR)/samples/basic/basdemo/
	@cp src/apps/kpunch/basdemo/basdemo.dhs  $(SDKDIR)/samples/basic/basdemo/
	@cp src/apps/kpunch/basdemo/ICON32.BMP   $(SDKDIR)/samples/basic/basdemo/
	@# BASIC sample: widshow (widget showcase)
	@cp src/apps/kpunch/widshow/widshow.dbp  $(SDKDIR)/samples/basic/widshow/
	@cp src/apps/kpunch/widshow/widshow.frm  $(SDKDIR)/samples/basic/widshow/
	@cp src/apps/kpunch/widshow/widshow.dhs  $(SDKDIR)/samples/basic/widshow/
	@cp src/apps/kpunch/widshow/ICON32.BMP   $(SDKDIR)/samples/basic/widshow/
	@# SDK files, project templates and the host tools
	cp $(SDK_SRC)/README.TXT $(SDK_SRC)/sdk.mk $(SDK_SRC)/newProject.sh $(SDKDIR)/
	cp -R $(SDK_SRC)/templates $(SDKDIR)/
	cp $(SDK_TOOLS) $(SDKDIR)/bin/

# Every build output: the DOS targets through each directory's clean,
# then the whole object tree (host libraries, test binaries, coverage)
# and the host-only bin/ directories.
clean:
	for d in $(LIBS); do $(MAKE) -C src/libs/kpunch/$$d clean || exit 1; done
	$(MAKE) -C src/loader clean
	$(MAKE) -C src/widgets/kpunch clean
	$(MAKE) -C src/apps/kpunch clean
	$(MAKE) -C src/savers/kpunch clean
	$(MAKE) -C src/tools clean
	$(MAKE) -C src/tools/proxy clean
	$(MAKE) -C src/installer clean
	rm -rf obj bin/host bin/test bin/system
	rm -rf bin/shared bin/widgets bin/libs bin/sdk
	rm -f docs/*.html
