Files
FBNeo/makefile.headless
T
Leonardo Kasperavičius 621233c80c Add an optional headless embedding layer (C API) for native frontends (#2841)
* Add headless frontend for embedding FBNeo

A new "headless" port that builds libfbneo as a shared library (fbneo_headless.dylib) exposing a small C API, for embedding in native host frontends.

Built originally to support OpenEmu-Silicon.

* Add input mapping and timing/aspect/reset to the C API

Rounds out the headless frontend with the functions a host wrapper needs to
actually run and control a game, instead of only loading one.

Input mapping (headless_input.cpp): FBHeadlessSetInput(player, button, value)
turns a simple arcade control (Up/Down/Left/Right, buttons 1-6, Start, Coin,
Service) into the right input for whatever driver is loaded. It does this by
reading each input's canonical name from the driver ("p1 up", "p1 fire 1",
"p1 coin", "p1 start", and the "coin N"/"start N" variants) and rebinding that
input to a private switch code the headless input plugin reads back. Inputs
that don't match a control are parked on a dead code so they always read as
released.

Timing and geometry, needed for correct video/audio sync and aspect ratio:
- FBHeadlessGetFrameRate()  - the game's refresh rate (fps x 100)
- FBHeadlessGetAspectX/Y()  - the game's pixel aspect ratio
- FBHeadlessReset()         - reset the running game

The FBHeadlessButton enum lists the supported controls in a fixed order so the
host can pass them straight through.

* Add save/load functions

* Add support for cheats and RetroAchievements

* Address comments from adversarial review

* Honor BDF_16BIT_ONLY (Cave, CPS3, Megadrive)

These drivers only blit RGB565. Rendering them at 32bpp produced garbled graphics. Render at 16bpp and convert to 32bpp on present (combined with the existing rotation pass so vertical games get both), fixing Cave, CPS3 and Megadrive-arcade video while keeping the 32bpp zero-copy fast path.

* Map the Service and Configure keys to the test and service switches

Most drivers name their cabinet switches "diag" (test switch, opens the
settings menu) and "service" with no player prefix, so the headless mapper
never bound them and the frontend's Service key did nothing.

Service now drives the test switch, or "service" when a driver has no test
switch. A new FBHeadlessButtonConfigure drives "service" in drivers that
have both (it usually adds a service credit).

* Map twin-stick games

Drivers name the second stick inconsistently: "pN <dir> 2", or another player's plain d-pad (Robotron's "Fire Up" is "p2 up", Smash TV's fire stick is p3/p4, Angel Kids' left stick is p3). The headless mapper dropped the first form and sent the second to the wrong player, so Robotron's fire stick only worked from a second controller.

When the display name names a stick (Left Stick, Right Stick, Fire, Shoot, Aim...), it now decides the stick and the player; otherwise "pN <dir> 2" is player N's right stick. Like the MAME core, the left stick goes on the d-pad and the right stick on buttons 1-4 (left, up, down, right), since OpenEmu's arcade controller has one stick.

* Report little-endian cheat memory for TMS34010 and V60 boards

GetMainMemoryBigEndian returned 1 for CPUs with MB_CHEAT_ENDI_SWAP, but those (TMS34010, V60) are little-endian; the flag only exists because FBNeo's own cheat engine stores values big-endian. The host then wrote multi-byte cheats in the wrong order: in Mortal Kombat "0000A260:00A1" stored bytes 00 A1, so P1 health read as 0xA100 (bar overfilled, dies in one hit).

Every board now reports little-endian, which is what the 68K boards already did by the RetroAchievements/libretro convention.

Headless check (mk, round 1, cheat 0000A260:00A1 at frame 3100):
before: A260=00 A261=A1 after the cheat (was A1 00)
after: A260=A1 A261=00, unchanged from what the game wrote

* Save battery-backed RAM in the headless burner

Drivers keep battery-backed RAM (ACB_NVRAM: Mortal Kombat's settings,
Robotron's high scores...) only as a scan area; the frontend has to save
it. The Windows burner does that with StatedAuto in DrvInit/DrvExit, but
the headless burner never did, so those games lost their settings every
session (unlike EEPROM games, which the device saves itself).

Like the Windows burner, DrvInit(bRestore) now loads
<szAppEEPROMPath><set>.fs and DrvExit saves it. Loading a game and Reset
both restore it. Drivers without NVRAM write no file.

Headless check (mk, three runs sharing the support folder, 600 frames):
before: only mk.ini saved
after:  mk.fs saved; run 1 (blank NVRAM) sits on one screen, runs 2 and
        3 boot normally and are identical. sfa still saves sfa.nv only.
2026-10-28 20:21:11 -04:00

640 lines
15 KiB
Plaintext

# Makefile for FBNeo, headless core (derived from makefile.sdl2)
#
# The first pass makes sure all intermediary targets are present. The second pass updates
# any targets, if necessary. (Intermediary) targets which have their own unique rules
# are generated as required.
unexport
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
DARWIN=1
endif
ifdef DARWIN
PLATFLAGS += -arch arm64 -mmacosx-version-min=11.0 -Wno-deprecated-declarations
endif
ifeq ($(OS),Windows_NT)
WINDOWS=1
endif
#
# Flags. Uncomment any of these declarations to enable their function.
#
# Check for changes in header files
ifndef SKIPDEPEND
DEPEND = 1
endif
# SANITIZE = 1
#
# Declare variables
#
# Specify the name of the executable file, without ".exe"
NAME = fbneo
PERL = 1
LSB_FIRST = 1
INCLUDE_7Z_SUPPORT = 1
BUILD_X86_ASM=
INCLUDE_AVI_RECORDING=
BUILD_A68K=
UNICODE=
#
# Specify paths/files
#
objdir = obj/
srcdir = src/
include makefile.burn_rules
# Platform stuff
alldir += burner burner/headless burner/sdl dep/libs/libspng dep/libs/lib7z dep/libs/zlib intf intf/video \
intf/video/scalers intf/video/headless intf/audio intf/audio/headless intf/input intf/input/headless intf/cd \
dep/generated
depobj += cdlist.o \
\
conc.o cong.o dat.o gamc.o gami.o image.o ioapi.o misc.o sshot.o state.o statec.o unzip.o zipfn.o \
\
adler32.o compress.o crc32.o deflate.o gzclose.o gzlib.o gzread.o gzwrite.o infback.o inffast.o inflate.o inftrees.o \
trees.o uncompr.o zutil.o \
\
spng.o \
\
aud_dsp.o aud_interface.o cd_interface.o cd_chd.o inp_interface.o interface.o lowpass2.o vid_interface.o \
vid_support.o \
\
inp_headless.o aud_headless.o vid_headless.o support_paths.o ips_manager.o scrn.o localise_gamelist.o romdata.o \
cd_img.o stringset.o bzip.o media.o \
inpdipsw.o inputbuf.o replay.o stated.o headless_main.o headless_drv.o headless_misc.o headless_input.o headless_memory.o
ifdef FORCE_PULSE_AUDIO
alldir += intf/audio/linux
depobj += aud_pulse_simple.o
endif
ifdef INCLUDE_7Z_SUPPORT
depobj += un7z.o \
\
7zArcIn.o 7zBuf.o 7zBuf2.o 7zCrc.o 7zCrcOpt.o 7zDec.o 7zFile.o 7zStream.o Alloc.o Bcj2.o Bra.o Bra86.o BraIA64.o CpuArch.o \
Delta.o LzFindOpt.o LzmaDec.o Lzma2Dec.o MtDec.o Ppmd7.o Ppmd7Dec.o Ppmd7aDec.o Sha256.o Sha1Opt.o Sha256Opt.o Threads.o Xxh64.o Xz.o XzCrc64.o XzCrc64Opt.o XzDec.o ZstdDec.o
endif
autobj += $(depobj)
ifdef BUILD_X86_ASM
autobj += eagle_fm.o 2xsaimmx.o hq2x32.o hq3x32.o hq4x32.o superscale.o
endif
# End, platform stuff
incdir = $(foreach dir,$(alldir),-I$(srcdir)$(dir)) -I$(objdir)dep/generated
lib = -lstdc++ -lm -lpthread
ifdef FORCE_PULSE_AUDIO
lib += -lpulse-simple
endif
ifdef INCLUDE_SWITCHRES
incdir += `pkg-config --cflags switchres`
lib += `pkg-config --libs switchres`
endif
autdep = $(depobj:.o=.d)
drvdep = $(drvsrc:.o=.d)
ifdef BUILD_A68K
a68k.o = $(objdir)cpu/a68k/a68k.o
endif
driverlist.h = $(srcdir)dep/generated/driverlist.h
ctv.h = $(srcdir)dep/generated/ctv.h
toa_gp9001_func.h = $(srcdir)dep/generated/toa_gp9001_func.h
neo_sprite_func.h = $(srcdir)dep/generated/neo_sprite_func.h
cave_tile_func.h = $(srcdir)dep/generated/cave_tile_func.h
cave_sprite_func.h = $(srcdir)dep/generated/cave_sprite_func.h
psikyo_tile_func.h = $(srcdir)dep/generated/psikyo_tile_func.h
pgm_sprite.h = $(srcdir)dep/generated/pgm_sprite.h
build_details.h = $(srcdir)dep/generated/build_details.h
allobj = $(objdir)cpu/m68k/m68kcpu.o $(objdir)cpu/m68k/m68kops.o \
$(foreach file,$(autobj:.o=.c), \
$(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \
$(firstword $(subst .c,.o,$(wildcard $(srcdir)$(dir)/$(file))))))) \
$(foreach file,$(autobj:.o=.cpp), \
$(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \
$(firstword $(subst .cpp,.o,$(wildcard $(srcdir)$(dir)/$(file))))))) \
$(foreach file,$(autobj:.o=.asm), \
$(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \
$(firstword $(subst .asm,.o,$(wildcard $(srcdir)$(dir)/$(file))))))) \
$(foreach file,$(autobj:.o=.rc), \
$(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \
$(firstword $(subst .rc,.o,$(wildcard $(srcdir)$(dir)/$(file)))))))
ifdef BUILD_A68K
allobj += $(a68k.o)
endif
alldep = $(foreach file,$(autobj:.o=.c), \
$(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \
$(firstword $(subst .c,.d,$(wildcard $(srcdir)$(dir)/$(file))))))) \
$(foreach file,$(autobj:.o=.cpp), \
$(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \
$(firstword $(subst .cpp,.d,$(wildcard $(srcdir)$(dir)/$(file))))))) \
$(foreach file,$(autobj:.o=.rc), \
$(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \
$(firstword $(subst .rc,.d,$(wildcard $(srcdir)$(dir)/$(file)))))))
autdrv := $(drvsrc:.cpp=.o)
#
#
# Specify compiler/linker/assembler
#
#
ifdef DARWIN
CC = gcc
else
CC = gcc
endif
CXX = $(CC)
LD = $(CC)
AS = nasm
# While cross-compiling, tools need to be compiled for host so that they can be executed by host.
# if HOST_CC is not set, then we aren't cross-compiling, so we'll use target compiler as host compiler
ifeq ($(HOST_CC),)
HOST_CC := $(CC)
HOST_CXX := $(CXX)
HOST_CFLAGS := $(CFLAGS)
HOST_CXXFLAGS := $(CXXFLAGS)
HOST_LDFLAGS := $(LDFLAGS)
endif
#LDFLAGS = -static
CFLAGS = -O2 -fomit-frame-pointer -Wno-write-strings \
-Wall -Wno-long-long -Wno-sign-compare -Wno-uninitialized -Wno-unused \
-Wno-conversion -Wno-attributes \
-Wno-unused-parameter -Wno-unused-value -std=c99 \
$(PLATFLAGS) $(DEF) $(incdir)
CXXFLAGS = -O2 -fomit-frame-pointer -Wno-write-strings \
-Wall -W -Wno-long-long -Wno-sign-compare \
-Wunknown-pragmas -Wundef -Wconversion -Wno-missing-braces \
-Wuninitialized -Wpointer-arith -Winline -Wno-multichar \
-Wno-conversion -Wno-attributes \
-Wno-unused-parameter -Wno-unused-value -Wno-narrowing \
$(PLATFLAGS) $(DEF) $(incdir)
ASFLAGS = -O1 -f coff -w-orphan-labels
# D3DUtils & D3DMath need these
# DEF = -Dsinf=\(float\)sin -Dcosf=\(float\)cos -Dasinf=\(float\)asin -Dacosf=\(float\)acos -Dsqrtf=\(float\)sqrt
# FIXME
DEF := -DBUILD_HEADLESS -DINCLUDE_FLACMP3_SUPPORT -DUSE_SPEEDHACKS -DFILENAME=$(NAME) -DUSE_FILE32API -DHAVE_UNISTD_H
ifdef FORCE_PULSE_AUDIO
DEF := $(DEF) -DFORCE_PULSE_AUDIO
endif
ifdef WINDOWS
DEF := $(DEF) -DSDL_WINDOWS
endif
ifdef UNICODE
DEF := $(DEF) -D_UNICODE
endif
ifdef SPECIALBUILD
DEF := $(DEF) -DSPECIALBUILD=$(SPECIALBUILD)
endif
ifdef FASTCALL
DEF := $(DEF) -DFASTCALL
endif
ifdef DEBUG
DEF := $(DEF) -DFBNEO_DEBUG
endif
ifdef ROM_VERIFY
DEF := $(DEF) -DROM_VERIFY
endif
ifdef INCLUDE_7Z_SUPPORT
DEF := $(DEF) -DINCLUDE_7Z_SUPPORT
endif
ifdef INCLUDE_AVI_RECORDING
DEF := $(DEF) -DINCLUDE_AVI_RECORDING
endif
ifdef LSB_FIRST
DEF := $(DEF) -DLSB_FIRST
endif
ifdef INCLUDE_LIB_PNGH
DEF := $(DEF) -DINCLUDE_LIB_PNGH
endif
ifdef BUILD_A68K
DEF := $(DEF) -DBUILD_A68K
endif
ifdef BUILD_X86_ASM
DEF := $(DEF) -DBUILD_X86_ASM
endif
ifdef BUILD_X64_EXE
DEF := $(DEF) -DBUILD_X64_EXE
endif
ifdef INCLUDE_SWITCHRES
DEF := $(DEF) -DINCLUDE_SWITCHRES
endif
ifdef SANITIZE
ifndef SYMBOL
SYMBOL =1
endif
CFLAGS += -fsanitize=address -fsanitize=undefined -fsanitize=bounds-strict
CXXFLAGS += -fsanitize=address -fsanitize=undefined -fsanitize=bounds-strict
endif
ifdef SYMBOL
CFLAGS += -ggdb3 -fno-omit-frame-pointer
CXXFLAGS += -ggdb3 -fno-omit-frame-pointer
ASFLAGS += -g
DEF := $(DEF) -D_DEBUG
else
LDFLAGS += -s
endif
ifdef PROFILE
CFLAGS += -pg
CXXFLAGS += -pg
endif
ifdef BUILD_NATIVE
CFLAGS += -march=native -mtune=native
CXXFLAGS += -march=native -mtune=native
endif
# For zlib
DEF := $(DEF) -DNO_VIZ -D_LARGEFILE64_SOURCE=0 -D_FILE_OFFSET_BITS=64
# For libchdr
DEF := $(DEF) -DCHDR_SYSTEM_ZLIB
# For lib7z
ifdef INCLUDE_7Z_SUPPORT
DEF := $(DEF) -D_7ZIP_PPMD_SUPPPORT -D_7ZIP_ST
endif
#
#
# Specify paths
#
#
vpath %.asm $(foreach dir,$(alldir),$(srcdir)$(dir)/ )
vpath %.cpp $(foreach dir,$(alldir),$(srcdir)$(dir)/ )
vpath %.c $(foreach dir,$(alldir),$(srcdir)$(dir)/ )
vpath %.h $(foreach dir,$(alldir),$(srcdir)$(dir)/ )
vpath %.rc $(foreach dir,$(alldir),$(srcdir)$(dir)/ )
vpath %.o $(foreach dir,$(alldir),$(objdir)$(dir)/ )
vpath %.d $(foreach dir,$(alldir),$(objdir)$(dir)/ )
#
#
# Rules
#
#
.PHONY: all init cleandep touch clean
ifeq ($(MAKELEVEL),1)
ifdef DEPEND
all: init $(drvdep) $(autdep) $(autobj) $(autdrv)
@$(MAKE) -f makefile.headless -s
else
all: init $(autobj) $(autdrv)
@$(MAKE) -f makefile.headless -s
endif
else
all: fbneo_headless.dylib
endif
#
#
# Rule for linking the executable
#
#
ifeq ($(MAKELEVEL),2)
$(objdir)drivers.o: $(autdrv)
@echo Linking drivers...
@$(LD) -r -nostdlib -o $@ $^
ifdef WINDOWS
$(NAME): $(allobj) $(objdir)drivers.o
@echo
@echo Linking executable... $(NAME)
@$(LD) -Wl,--image-base -Wl,0x10000000 -mconsole $(CFLAGS) $(LDFLAGS) -o $@ $^ $(lib)
else
fbneo_headless.dylib: $(allobj) $(objdir)drivers.o
@echo
@echo Linking dylib... $@
@$(LD) -dynamiclib -install_name @rpath/fbneo_headless.dylib $(CFLAGS) $(LDFLAGS) -o $@ $^ $(lib)
endif
ifdef DEBUG
# Don't compress when making a debug build
else
ifdef COMPRESS
@upx --best $@
endif
endif
endif
ifeq ($(MAKELEVEL),1)
ifdef FORCE_UPDATE
$(build_details.h): FORCE
endif
endif
#
# Generate the gamelist
#
burn.o burn.d: $(driverlist.h)
$(driverlist.h): $(drvsrc) $(srcdir)dep/scripts/gamelist.pl
ifdef PERL
@$(srcdir)dep/scripts/gamelist.pl -o $@ -l gamelist.txt \
$(filter %.cpp,$(foreach file,$(drvsrc:.o=.cpp),$(foreach dir,$(alldir), \
$(firstword $(wildcard $(srcdir)$(dir)/$(file))))))
else
ifeq ($(MAKELEVEL),2)
@echo
@echo Warning: Perl is not available on this system.
@echo $@ cannot be updated or created!
@echo
endif
endif
#
# Verify if driverlist.h needs to be updated
#
#ifeq ($(MAKELEVEL),1)
#ifdef FORCE_UPDATE
#$(driverlist.h): FORCE
#endif
#endif
#
# Compile 68000 cores
#
# A68K
ifdef BUILD_A68K
$(a68k.o): fba_make68k.c
@echo Compiling A68K MC68000 core...
@$(CC) -mconsole $(CFLAGS) $(LDFLAGS) -DWIN32 -Wno-unused -Wno-conversion -Wno-missing-prototypes \
-s $< -o $(subst $(srcdir),$(objdir),$(<D))/$(<F:.c=.exe)
@$(subst $(srcdir),$(objdir),$(<D))/$(<F:.c=.exe) $(@:.o=.asm) \
$(@D)/a68k_tab.asm 00 $(ppro)
@echo Assembling A68K MC68000 core...
@$(AS) $(ASFLAGS) $(@:.o=.asm) -o $@
endif
# Musashi
$(objdir)cpu/m68k/m68kcpu.o: $(srcdir)cpu/m68k/m68kcpu.c $(objdir)dep/generated/m68kops.h $(srcdir)cpu/m68k/m68k.h $(srcdir)cpu/m68k/m68kconf.h
@echo Compiling Musashi MC680x0 core \(m68kcpu.c\)...
@$(CC) $(CFLAGS) -c $(srcdir)cpu/m68k/m68kcpu.c -o $(objdir)cpu/m68k/m68kcpu.o
$(objdir)cpu/m68k/m68kops.o: $(objdir)cpu/m68k/m68kmake $(objdir)dep/generated/m68kops.h $(objdir)dep/generated/m68kops.c $(srcdir)cpu/m68k/m68k.h $(srcdir)cpu/m68k/m68kconf.h
@echo Compiling Musashi MC680x0 core \(m68kops.c\)...
@$(CC) $(CFLAGS) -c $(objdir)dep/generated/m68kops.c -o $(objdir)cpu/m68k/m68kops.o
$(objdir)dep/generated/m68kops.h $(objdir)dep/generated/m68kops.c: $(objdir)cpu/m68k/m68kmake $(srcdir)cpu/m68k/m68k_in.c
$(objdir)cpu/m68k/m68kmake $(objdir)dep/generated/ $(srcdir)cpu/m68k/m68k_in.c
$(objdir)cpu/m68k/m68kmake: $(srcdir)cpu/m68k/m68kmake.c
@echo Compiling Musashi MC680x0 core \(m68kmake.c\)...
@$(HOST_CC) $(HOST_CFLAGS) $(srcdir)cpu/m68k/m68kmake.c -o $(objdir)cpu/m68k/m68kmake -Dmain=main
#
# Extra rules for generated header file ctv.h, needed by ctv.cpp
#
ctv.d ctv.o: $(ctv.h)
$(ctv.h): ctv_make.cpp
@echo Generating $(srcdir)dep/generated/$(@F)...
@$(HOST_CC) $(HOST_CXXFLAGS) $(HOST_LDFLAGS) $< \
-o $(subst $(srcdir),$(objdir),$(<D))/$(<F:.cpp=.exe) -Dmain=main
@$(subst $(srcdir),$(objdir),$(<D))/$(<F:.cpp=.exe) >$@
#
# Extra rules for generated header file toa_gp9001_func.h, needed by toa_gp9001.cpp
#
toa_bcu2.d toa_bcu2.o toa_gp9001.d toa_gp9001.o: $(toa_gp9001_func.h)
$(toa_gp9001_func.h): $(srcdir)dep/scripts/toa_gp9001_func.pl
@$(srcdir)dep/scripts/toa_gp9001_func.pl -o $(toa_gp9001_func.h)
#
# Extra rules for generated header file neo_sprite_func.h, needed by neo_sprite.cpp
#
neo_sprite.d neo_sprite.o: $(neo_sprite_func.h)
$(neo_sprite_func.h): $(srcdir)dep/scripts/neo_sprite_func.pl
@$(srcdir)dep/scripts/neo_sprite_func.pl -o $(neo_sprite_func.h)
#
# Extra rules for generated header file cave_tile_func.h, needed by cave_tile.cpp
#
cave_tile.d cave_tile.o: $(cave_tile_func.h)
$(cave_tile_func.h): $(srcdir)dep/scripts/cave_tile_func.pl
@$(srcdir)dep/scripts/cave_tile_func.pl -o $(cave_tile_func.h)
#
# Extra rules for generated header file cave_sprite_func.h, needed by cave_sprite.cpp
#
cave_sprite.d cave_sprite.o: $(cave_sprite_func.h)
$(cave_sprite_func.h): $(srcdir)dep/scripts/cave_sprite_func.pl
@$(srcdir)dep/scripts/cave_sprite_func.pl -o $(cave_sprite_func.h)
#
# Extra rules for generated header file psikyo_tile_func.h / psikyo_sprite_func.h, needed by psikyo_tile.cpp / psikyo_sprite.cpp
#
psikyo_tile.d psikyo_tile.o psikyosprite.d psikyo_sprite.o: $(psikyo_tile_func.h)
$(psikyo_tile_func.h): $(srcdir)dep/scripts/psikyo_tile_func.pl
$(srcdir)dep/scripts/psikyo_tile_func.pl -o $(psikyo_tile_func.h)
#
# Extra rules for generated header file pgm_sprite.h, needed by pgm_draw.cpp
#
pgm_draw.d pgm_draw.o: $(pgm_sprite.h)
$(pgm_sprite.h): pgm_sprite_create.cpp
@echo Generating $(srcdir)dep/generated/$(@F)...
@$(HOST_CC) $(HOST_CXXFLAGS) $(HOST_LDFLAGS) $< \
-o $(subst $(srcdir),$(objdir),$(<D))/$(<F:.cpp=.exe) -Dmain=main
@$(subst $(srcdir),$(objdir),$(<D))/$(<F:.cpp=.exe) >$@
ifeq ($(MAKELEVEL),2)
ifdef DEPEND
include $(alldep)
endif
endif
#
# Generic rules for C/C++ files
#
# Note: require init to complete before assembling anything (see "| init" below)
# to avoid parallization issues on fresh builds.
#
ifeq ($(MAKELEVEL),1)
%.o: %.cpp
@echo Compiling $<...
@$(CC) $(CXXFLAGS) -c $< -o $(subst $(srcdir),$(objdir),$(<D))/$(@F)
%.o: %.c
@echo Compiling $<...
@$(CC) $(CFLAGS) -c $< -o $(subst $(srcdir),$(objdir),$(<D))/$(@F)
%.o: %.asm | init
@echo Assembling $<...
@$(AS) $(ASFLAGS) $< -o $(subst $(srcdir),$(objdir),$(<D))/$(@F)
else
%.o: %.c
@echo Compiling $<...
@$(CC) $(CFLAGS) -c $< -o $@
%.o: %.asm | init
@echo Assembling $<...
@$(AS) $(ASFLAGS) $< -o $@
%.o:
@echo Compiling $<...
@$(CC) $(CXXFLAGS) -c $< -o $@
endif
#
# Generate dependencies for C/C++ files
#
ifdef DEPEND
%.d: %.c
@echo Generating depend file for $<...
@$(CC) -MM -MT "$(subst $(srcdir),$(objdir),$(<D))/$(*F).o $(subst $(srcdir),$(objdir),$(<D))/$(@F)" -x c++ $(CXXFLAGS) $< >$(subst $(srcdir),$(objdir),$(<D))/$(@F)
%.d: %.cpp
@echo Generating depend file for $<...
@$(CC) -MM -MT "$(subst $(srcdir),$(objdir),$(<D))/$(*F).o $(subst $(srcdir),$(objdir),$(<D))/$(@F)" -x c++ $(CXXFLAGS) $< >$(subst $(srcdir),$(objdir),$(<D))/$(@F)
%.d: %.rc
@echo Generating depend file for $<...
@$(CC) -MM -MT "$(subst $(srcdir),$(objdir),$(<D))/$(*F).o $(subst $(srcdir),$(objdir),$(<D))/$(@F)" -x c++ $(CXXFLAGS) $< >$(subst $(srcdir),$(objdir),$(<D))/$(@F)
endif
#
# Phony targets
#
init:
ifdef DEBUG
@echo Making debug build...
else
@echo Making normal build...
endif
@echo
@mkdir -p $(foreach dir, $(alldir),$(objdir)$(dir))
@mkdir -p $(srcdir)dep/generated
cleandep:
@echo Removing depend files from $(objdir)...
-@for dir in $(alldir); do rm -f $(objdir)$$dir/*.d; done
touch:
@echo Marking all targets for $(NAME) as uptodate...
-@touch $(NAME).exe
-@touch -c -r $(NAME).exe $(srcdir)/dep/generated/*
-@for dir in $(alldir); do touch -c -r $(NAME).exe $(objdir)$$dir/*; done
clean:
@echo Removing build files...
-@rm -fr $(objdir) $(ctv.h) $(dep)generated gamelist.txt $(NAME)
ifdef PERL
@echo Removing all files generated with perl scripts...
-@rm -f -r $(app_gnuc.rc) $(driverlist)
endif
#
# Rule to force recompilation of any target that depends on it
#
FORCE: