joeylib2/make/dvx.mk

216 lines
10 KiB
Makefile

# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
# See LICENSE.md for license information.
# SPDX-License-Identifier: MIT
#
# DVX build rules: every example as a DVX program (.app).
#
# A JoeyLib program on DVX runs in a window of the DVX desktop, or on the
# whole screen (src/dvx). The library, libjoey.a, is built here with
# JoeyLib's own DJGPP (DVX is built with the same gcc 12.2.0) against the SDK
# the installer unpacked from a DVX tree's dist/sdk.zip into
# toolchains/opt/dvx/sdk (DVX_SDK); the stage and the graphics are the DOS
# port's (src/x86, the chunky generics). Each example is then a DVX SDK
# project in build/dvx/apps/<name>/ -- symlinks to its sources and to the
# port's entry (src/dvx/dvxApp.c), a generated one-topic help file and
# resource list -- built and packaged by the SDK's own sdk.mk (KIND = app,
# LIBS = libjoey.a, DATA = its data files) and copied into build/dvx/bin/
# laid out as it goes into DVX: apps/<vendor>/<name>/<name>.app, <name>.hlp
# and DATA/. The gates and joey-run copy that tree into a DVX installation.
#
# This file holds the library and programRules, the DVX rules for one
# program, which make/programs.mk instantiates for every program (a project
# reaches both through make/project.mk). JoeyLib's own examples are at the
# end.
PLATFORM := dvx
include $(dir $(lastword $(MAKEFILE_LIST)))/common.mk
$(call requireTool,$(DOS_CC),toolchain:dos)
$(call requireTool,$(DVX_SDK)/sdk.mk,toolchain:dvx)
APPSDIR := $(PROGRAM_BUILD)/apps
PORT_DIR := $(SRC_DIR)/dvx
# sdk.mk's code generation, so the library matches the programs it links
# into; -fomit-frame-pointer as on DOS.
DVX_ARCH := -march=i486 -mtune=i586 -fomit-frame-pointer
# The SDK's header folders (sdk.mk's list: all but the BASIC and socket ones),
# for the port's own files only -- the core never sees a DVX header.
DVX_SDK_INCS := $(addprefix -I,$(filter-out %/basic %/net,$(patsubst %/,%,$(wildcard $(DVX_SDK)/include/*/))))
# A program's exit() and atexit() are the port's (src/dvx/program.c): the
# handlers run, and JoeyLib shuts down, when the program ends rather than
# when DVX does. Applied to the library and the programs, never to the port.
PROGRAM_RENAMES := -Dexit=jlDvxPortExit -Datexit=jlDvxPortAtExit
CFLAGS := $(COMMON_CFLAGS) $(DVX_ARCH) -DJOEYLIB_PLATFORM_DVX -I$(SRC_X86) -I$(SRC_CG) -MMD -MP
LIB_CFLAGS := $(CFLAGS) $(PROGRAM_RENAMES)
PORT_CFLAGS := $(CFLAGS) -I$(PORT_DIR) $(DVX_SDK_INCS)
# The flags every program adds to sdk.mk's (which bring the SDK headers and
# -Werror), with the include folders every port gives its programs: the
# program's main becomes jlGameMain, which the port calls.
APP_CFLAGS := -DJOEYLIB_PLATFORM_DVX -Dmain=jlGameMain $(PROGRAM_RENAMES) -fomit-frame-pointer -I$(INCLUDE_DIR) -I$(SRC_CORE) -I$(PORT_DIR)
# dvxApp.c is compiled into each program (it names it), not the library.
PORT_ENTRY := $(PORT_DIR)/dvxApp.c
PORT_C_SRCS := $(filter-out $(PORT_ENTRY),$(wildcard $(PORT_DIR)/*.c))
X86_C_SRCS := $(wildcard $(SRC_X86)/*.c)
X86_S_SRCS := $(wildcard $(SRC_X86)/*.s)
LIB_OBJS := \
$(patsubst $(SRC_CORE)/%.c,$(BUILD)/obj/core/%.o,$(CORE_C_SRCS_NO_MIX)) \
$(patsubst $(SRC_DIR)/generic/%.c,$(BUILD)/obj/generic/%.o,$(GENERIC_C_SRCS)) \
$(patsubst $(PORT_DIR)/%.c,$(BUILD)/obj/port/%.o,$(PORT_C_SRCS)) \
$(patsubst $(SRC_X86)/%.c,$(BUILD)/obj/x86/%.o,$(X86_C_SRCS)) \
$(patsubst $(SRC_X86)/%.s,$(BUILD)/obj/x86/%.o,$(X86_S_SRCS)) \
$(BUILD)/obj/codegen/spriteCompile.o \
$(BUILD)/obj/codegen/spriteStage.o
LIB := $(LIBDIR)/libjoey.a
$(BUILD)/obj/core/%.o: $(SRC_CORE)/%.c
@mkdir -p $(dir $@)
$(DOS_CC) $(LIB_CFLAGS) -c $< -o $@
$(BUILD)/obj/generic/%.o: $(SRC_DIR)/generic/%.c
@mkdir -p $(dir $@)
$(DOS_CC) $(LIB_CFLAGS) -c $< -o $@
$(BUILD)/obj/x86/%.o: $(SRC_X86)/%.c
@mkdir -p $(dir $@)
$(DOS_CC) $(LIB_CFLAGS) -c $< -o $@
$(BUILD)/obj/x86/%.o: $(SRC_X86)/%.s
@mkdir -p $(dir $@)
$(DOS_CC) -c $< -o $@
$(BUILD)/obj/codegen/%.o: $(SRC_CG)/%.c
@mkdir -p $(dir $@)
$(DOS_CC) $(LIB_CFLAGS) -c $< -o $@
$(BUILD)/obj/port/%.o: $(PORT_DIR)/%.c
@mkdir -p $(dir $@)
$(DOS_CC) $(PORT_CFLAGS) -c $< -o $@
$(LIB): $(LIB_OBJS)
@mkdir -p $(dir $@)
rm -f $@
$(DOS_AR) rcs $@ $^
# Pull in per-object header-dependency files generated by gcc -MMD/-MP.
-include $(LIB_OBJS:.o=.d)
# ---- programs -----------------------------------------------------------------
# A program is a folder laid out as it installs in DVX, apps/<vendor>/<name>/
# under bin/: <name>.app, <name>.hlp and its DATA/. The name is the id in lower
# case (DVX runs on DOS: an 8.3 name). DATA is staged apart, in
# build/dvx/data/<name>/DATA, for the SDK to package.
# DVX_VENDOR the vendor folder: whoever makes the program, 1 to 8
# letters, digits or _ (DVX names its package vendor.name)
# DVX_AUTHOR the resource list's author
# DVX_KIND what the help and resource texts call a program: "<title> is a
# <kind> program" (default JoeyLib)
# The vendor and the author are the project's own: it must set them.
DVX_KIND := JoeyLib
PROGRAM_EXT := .app
PROGRAM_DATA_APART := yes
# What every program's help says after "<title> is a <kind> program.".
DVX_HELP_WINDOW := It runs in its own window at a virtual 70 frames a second; while the window is active every key goes to it except Alt+Tab (switch programs), Ctrl+Esc (Task Manager) and Alt+F4 (close it). Game > Exit also closes it.
DVX_HELP_SCREEN := Game > Full Screen, or Alt+Enter, puts it on the whole screen at 320x200 in 256 colours, and back in its window. It starts the way it was left. Alt+Tab or Ctrl+Esc brings the desktop back while it runs on; coming back to it gives it the whole screen again.
programNameRule = $(call lc,$(1))
programDir = $(or $(call programVar,$(1),BINDIR),$(BINDIR))/apps/$(DVX_VENDOR)/$(call programName,$(1))
programTarget = app-$(call programName,$(1))
programTitle = $(or $(call programVar,$(1),TITLE),$(call capFirst,$(call lc,$(1))))
# One program, $(1) its id, as the phony app-<name>: (re)write its SDK project
# in build/dvx/apps/<name>/ -- the Makefile, symlinks to its sources and the
# port's entry, the help source, the resource list, and its icon -- then let
# sdk.mk build and package it, and copy the package into bin/ (or
# ID_BINDIR). sdk.mk rebuilds only what changed; the project files are
# rewritten only when their contents change, so an unchanged program is not
# relinked. Its CFLAGS are added to the program's flags. Its sources are
# linked by absolute path, so a project may name them relative to its own
# folder.
programRules = $(call programDvxRules,$(1),$(call programName,$(1)))
# $(1) the id, $(2) the name. Everything that may hold a comma (the title,
# the help line) is looked up here rather than passed in, and every line
# written out is shellQuote'd, so a title or help line may hold quotes.
define programDvxRules
$(call requireSet,DVX_VENDOR,the project's own vendor folder$(comma) which its programs install in: 1 to 8 letters$(comma) digits or _)
$(call requireSet,DVX_AUTHOR,the author DVX names for the project's programs)
.PHONY: app-$(2)
app-$(2): $(LIB) $(call programVar,$(1),SRCS) $(PORT_ENTRY) $(call programVar,$(1),DATA) $(APPSDIR)/$(2)/icon32.bmp
@mkdir -p $(APPSDIR)/$(2)
@for src in $(abspath $(call programVar,$(1),SRCS)) $(PORT_ENTRY); do ln -sfn $$$$src $(APPSDIR)/$(2)/$$$$(basename $$$$src); done
@printf '%s\n' \
$(call shellQuote,# Generated by make/dvx.mk: the DVX SDK project of the $(DVX_KIND) $(2).) \
'SDK_DIR = $(DVX_SDK)' \
'KIND = app' \
'NAME = $(2)' \
'VENDOR = $(DVX_VENDOR)' \
'LIBS = $(LIB)' \
'DATA = $(if $(call programVar,$(1),DATA),$(call programDataDir,$(1)))' \
$(call shellQuote,CFLAGS = $(APP_CFLAGS) $(call programVar,$(1),CFLAGS) -I$(dir $(abspath $(firstword $(call programVar,$(1),SRCS)))) -DDVX_PORT_APP_NAME=$(call shellQuote,$(call cQuote,$(call programTitle,$(1))))) \
'include $$$$(SDK_DIR)/sdk.mk' | $(REPO_DIR)/make/support/dvx/writeIfChanged.sh $(APPSDIR)/$(2)/Makefile
@printf '%s\n' \
'.topic $(2).overview' \
$(call shellQuote,.title $(call programTitle,$(1))) \
$(call shellQuote,.toc 0 $(call programTitle,$(1))) \
'.default' \
$(call shellQuote,.index $(call programTitle,$(1))) \
'' \
$(call shellQuote,.h1 $(call programTitle,$(1))) \
'' \
$(call shellQuote,$(call programVar,$(1),HELP)) \
'' \
$(call shellQuote,$(call programTitle,$(1)) is a $(DVX_KIND) program. $(DVX_HELP_WINDOW)) \
'' \
$(call shellQuote,$(DVX_HELP_SCREEN)) \
| $(REPO_DIR)/make/support/dvx/writeIfChanged.sh $(APPSDIR)/$(2)/$(2).dhs
@printf '%s\n' \
'icon32 icon icon32.bmp' \
$(call shellQuote,name text "$(call programTitle,$(1))") \
$(call shellQuote,author text "$(DVX_AUTHOR)") \
$(call shellQuote,description text "$(DVX_KIND): $(call programTitle,$(1))") \
| $(REPO_DIR)/make/support/dvx/writeIfChanged.sh $(APPSDIR)/$(2)/$(2).res
$$(MAKE) -C $(APPSDIR)/$(2) DJGPP_PREFIX=$(DOS_PREFIX) package
@mkdir -p $(or $(call programVar,$(1),BINDIR),$(BINDIR))
rm -rf $(call programDir,$(1))
cp -R $(APPSDIR)/$(2)/pkg/apps $(or $(call programVar,$(1),BINDIR),$(BINDIR))/
# Its icon, the one Program Manager shows: icon32 among its resources, a
# 32x32 BMP on DVX's grey, which sdk.mk takes from the project's folder.
$(APPSDIR)/$(2)/icon32.bmp: $(call programIcon,$(1)) $(ICON_BAKE)
@mkdir -p $$(dir $$@)
$(ICON_BAKE) bmp $$< $$@
endef
# ---- JoeyLib's own build: its examples ------------------------------------------
ifndef JOEY_PROJECT
include $(REPO_DIR)/make/examples.mk
include $(REPO_DIR)/make/programs.mk
# UBER's probe build is part of `all` (scripts/verify-dvx-display.sh).
.DEFAULT_GOAL := all
.PHONY: all dvx dvx-lib dvx-probe clean-dvx clean
all dvx: dvx-lib programs dvx-probe
dvx-lib: $(LIB)
dvx-probe: $(call programTarget,UBERPROB)
clean-dvx:
rm -rf $(BUILD)
# `clean` as an alias for `clean-dvx`. Without it a bare
# `make -f make/dvx.mk clean` FAILS -- and with stderr discarded that
# looks like success while leaving stale objects the next build links.
clean: clean-dvx
endif