From be171cfe66719aef3159006ab616b7fe88fb8acb Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 3 Jun 2023 14:39:21 +0300 Subject: [PATCH] Add a library target, complete with headers that strip implementation details out. --- .github/actions/install_deps.sh | 15 +++++++- .github/workflows/sanity.yml | 2 +- Core/debugger.c | 6 ++-- Core/defs.h | 8 ++--- Core/gb.c | 4 +-- Core/save_state.c | 6 ++-- Core/save_state.h | 2 +- Core/timing.h | 2 +- Makefile | 61 +++++++++++++++++++++++++++++---- README.md | 8 +++-- Windows/stdio.h | 8 +++++ 11 files changed, 96 insertions(+), 26 deletions(-) diff --git a/.github/actions/install_deps.sh b/.github/actions/install_deps.sh index 52c66d9..991f2e1 100755 --- a/.github/actions/install_deps.sh +++ b/.github/actions/install_deps.sh @@ -12,9 +12,22 @@ case `echo $1 | cut -d '-' -f 1` in cd .. rm -rf * ) + + ( + cd `mktemp -d` + curl -L https://github.com/BR903/cppp/archive/refs/heads/master.zip > cppp.zip + unzip cppp.zip + cd cppp-* + make -sj + sudo make install + cd .. + rm -rf * + ) + + ;; macos) - brew install rgbds sdl2 + brew install rgbds sdl2 cppp ;; *) echo "Unsupported OS" diff --git a/.github/workflows/sanity.yml b/.github/workflows/sanity.yml index a594aeb..9bd487e 100644 --- a/.github/workflows/sanity.yml +++ b/.github/workflows/sanity.yml @@ -29,7 +29,7 @@ jobs: ./.github/actions/install_deps.sh ${{ matrix.os }} - name: Build run: | - ${{ matrix.cc }} -v; (make -j sdl tester libretro ${{ matrix.extra_targets }} CONF=release CC=${{ matrix.cc }} || (echo "==== Build Failed ==="; make sdl tester libretro ${{ matrix.extra_targets }} CONF=release CC=${{ matrix.cc }})) + ${{ matrix.cc }} -v; (make -j all CONF=release CC=${{ matrix.cc }} || (echo "==== Build Failed ==="; make all CONF=release CC=${{ matrix.cc }})) - name: Sanity tests shell: bash run: | diff --git a/Core/debugger.c b/Core/debugger.c index b7829e1..ea6834c 100644 --- a/Core/debugger.c +++ b/Core/debugger.c @@ -2723,17 +2723,17 @@ static bool is_in_trivial_memory(uint16_t addr) typedef uint16_t opcode_address_getter_t(GB_gameboy_t *gb, uint8_t opcode); -uint16_t trivial_1(GB_gameboy_t *gb, uint8_t opcode) +static uint16_t trivial_1(GB_gameboy_t *gb, uint8_t opcode) { return gb->pc + 1; } -uint16_t trivial_2(GB_gameboy_t *gb, uint8_t opcode) +static uint16_t trivial_2(GB_gameboy_t *gb, uint8_t opcode) { return gb->pc + 2; } -uint16_t trivial_3(GB_gameboy_t *gb, uint8_t opcode) +static uint16_t trivial_3(GB_gameboy_t *gb, uint8_t opcode) { return gb->pc + 3; } diff --git a/Core/defs.h b/Core/defs.h index 94ce819..07a07d9 100644 --- a/Core/defs.h +++ b/Core/defs.h @@ -10,6 +10,10 @@ #define likely(x) GB_likely(x) #define unlikely(x) GB_unlikely(x) +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) +#define __builtin_bswap16(x) ({ typeof(x) _x = (x); _x >> 8 | _x << 8; }) +#endif + #define internal __attribute__((visibility("internal"))) #define noinline __attribute__((noinline)) @@ -44,10 +48,6 @@ #endif #endif -#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) -#define __builtin_bswap16(x) ({ typeof(x) _x = (x); _x >> 8 | _x << 8; }) -#endif - struct GB_gameboy_s; typedef struct GB_gameboy_s GB_gameboy_t; #endif diff --git a/Core/gb.c b/Core/gb.c index b532d98..75de6f8 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1402,7 +1402,7 @@ GB_accessory_t GB_get_built_in_accessory(GB_gameboy_t *gb) bool GB_is_inited(GB_gameboy_t *gb) { - return gb->magic == state_magic(); + return gb->magic == GB_state_magic(); } bool GB_is_cgb(const GB_gameboy_t *gb) @@ -1784,7 +1784,7 @@ static void GB_reset_internal(GB_gameboy_t *gb, bool quick) gb->io_registers[GB_IO_OBP1] = preserved_state->obp1; } - gb->magic = state_magic(); + gb->magic = GB_state_magic(); request_boot_rom(gb); } diff --git a/Core/save_state.c b/Core/save_state.c index 2cb3538..aefc72e 100644 --- a/Core/save_state.c +++ b/Core/save_state.c @@ -1452,7 +1452,7 @@ static int get_state_model_internal(virtual_file_t *file, GB_model_t *model) if (file->read(file, GB_GET_SECTION(&save, header), GB_SECTION_SIZE(header)) != GB_SECTION_SIZE(header)) return errno; fix_broken_windows_saves = true; } - if (save.magic != state_magic()) { + if (save.magic != GB_state_magic()) { return get_state_model_bess(file, model); } if (!READ_SECTION(&save, file, core_state)) return errno ?: EIO; @@ -1499,7 +1499,7 @@ bool GB_is_save_state(const char *path) if (!f) return false; uint32_t magic = 0; fread(&magic, sizeof(magic), 1, f); - if (magic == state_magic()) { + if (magic == GB_state_magic()) { ret = true; goto exit; } @@ -1507,7 +1507,7 @@ bool GB_is_save_state(const char *path) // Legacy corrupted Windows save state if (magic == 0) { fread(&magic, sizeof(magic), 1, f); - if (magic == state_magic()) { + if (magic == GB_state_magic()) { ret = true; goto exit; } diff --git a/Core/save_state.h b/Core/save_state.h index 655122e..f9cf4d6 100644 --- a/Core/save_state.h +++ b/Core/save_state.h @@ -44,7 +44,7 @@ int GB_get_state_model(const char *path, GB_model_t *model); int GB_get_state_model_from_buffer(const uint8_t *buffer, size_t length, GB_model_t *model); #ifdef GB_INTERNAL -static inline uint32_t state_magic(void) +static inline uint32_t GB_state_magic(void) { if (sizeof(bool) == 1) return 'SAME'; return 'S4ME'; diff --git a/Core/timing.h b/Core/timing.h index 9e44cb7..d793bd5 100644 --- a/Core/timing.h +++ b/Core/timing.h @@ -48,13 +48,13 @@ if ((gb)->unit##_cycles <= 0) {\ return;\ }\ switch ((gb)->unit##_state) -#endif #define GB_BATCHABLE_STATE_MACHINE(gb, unit, cycles, divisor, allow_batching) \ const bool __state_machine_allow_batching = (allow_batching); \ GB_STATE_MACHINE(gb, unit, cycles, divisor) #define GB_STATE(gb, unit, state) case state: goto unit##state +#endif #define GB_UNIT(unit) int32_t unit##_cycles, unit##_state diff --git a/Makefile b/Makefile index 48ed4cb..dd871d8 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,12 @@ else DEFAULT := sdl endif -ifneq ($(shell which xdg-open)$(FREEDESKTOP),) +NULL := /dev/null +ifeq ($(PLATFORM),windows32) +NULL := NUL +endif + +ifneq ($(shell which xdg-open 2> $(NULL))$(FREEDESKTOP),) # Running on an FreeDesktop environment, configure for (optional) installation DESTDIR ?= PREFIX ?= /usr/local @@ -54,6 +59,9 @@ CONF ?= debug BIN := build/bin OBJ := build/obj +INC := build/include/sameboy +LIB := build/lib + BOOTROMS_DIR ?= $(BIN)/BootROMs ifdef DATA_DIR @@ -64,13 +72,13 @@ endif # Use clang if it's available. ifeq ($(origin CC),default) -ifneq (, $(shell which clang)) +ifneq (, $(shell which clang 2> $(NULL))) CC := clang endif endif # Find libraries with pkg-config if available. -ifneq (, $(shell which pkg-config)) +ifneq (, $(shell which pkg-config 2> $(NULL))) # But not on macOS, it's annoying ifneq ($(PLATFORM),Darwin) PKG_CONFIG := pkg-config @@ -100,11 +108,9 @@ endif # Set compilation and linkage flags based on target, platform and configuration OPEN_DIALOG = OpenDialog/gtk.c -NULL := /dev/null ifeq ($(PLATFORM),windows32) OPEN_DIALOG = OpenDialog/windows.c -NULL := NUL endif ifeq ($(PLATFORM),Darwin) @@ -212,6 +218,11 @@ LDFLAGS += -Wl,/NODEFAULTLIB:libcmt.lib endif endif +LIBFLAGS := -nostdlib -Wl,-r +ifneq ($(PLATFORM),Darwin) +LIBFLAGS += -no-pie +endif + ifeq ($(CONF),debug) CFLAGS += -g else ifeq ($(CONF), release) @@ -241,6 +252,8 @@ else $(error Invalid value for CONF: $(CONF). Use "debug", "release" or "native_release") endif + + # Define our targets ifeq ($(PLATFORM),windows32) @@ -259,11 +272,20 @@ tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin $ _ios: $(BIN)/SameBoy-iOS.app $(OBJ)/reregister ios-ipa: $(BIN)/SameBoy-iOS.ipa ios-deb: $(BIN)/SameBoy-iOS.deb -all: cocoa sdl tester libretro +ifeq ($(PLATFORM),windows32) +lib: lib-unsupported +else +lib: $(LIB)/libsameboy.o $(LIB)/libsameboy.a +endif +all: sdl tester libretro lib +ifeq ($(PLATFORM),Darwin) +all: cocoa ios-ipa ios-deb +endif # Get a list of our source files and their respective object file targets CORE_SOURCES := $(shell ls Core/*.c) +CORE_HEADERS := $(shell ls Core/*.h) SDL_SOURCES := $(shell ls SDL/*.c) $(OPEN_DIALOG) $(patsubst %,SDL/audio/%.c,$(SDL_AUDIO_DRIVERS)) TESTER_SOURCES := $(shell ls Tester/*.c) IOS_SOURCES := $(filter-out iOS/reregister.m, $(shell ls iOS/*.m)) $(shell ls AppleCommon/*.m) @@ -275,12 +297,15 @@ CORE_SOURCES += $(shell ls Windows/*.c) endif CORE_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(CORE_SOURCES)) +PUBLIC_HEADERS := $(patsubst Core/%,$(INC)/%,$(CORE_HEADERS)) COCOA_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(COCOA_SOURCES)) IOS_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(IOS_SOURCES)) QUICKLOOK_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(QUICKLOOK_SOURCES)) SDL_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(SDL_SOURCES)) TESTER_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(TESTER_SOURCES)) +lib: $(PUBLIC_HEADERS) + # Automatic dependency generation ifneq ($(filter-out ios ios-ipa ios-dev clean bootroms libretro %.bin, $(MAKECMDGOALS)),) @@ -631,9 +656,31 @@ $(OBJ)/control.tar.gz: iOS/deb-postinst iOS/deb-control $(OBJ)/debian-binary: -@$(MKDIR) -p $(dir $@) echo 2.0 > $@ + +$(LIB)/libsameboy.o: $(CORE_OBJECTS) + -@$(MKDIR) -p $(dir $@) + @# This is a somewhat simple hack to force Clang and GCC to build a native object file out of one or many LTO objects + echo "static const char __attribute__((used)) x=0;"| $(CC) $(filter-out -flto,$(CFLAGS)) -c -x c - -o $(OBJ)/lto_hack.o + @# And this is a somewhat complicated hack to invoke the correct LTO-enabled LD command in a mostly cross-platform nature + $(CC) $(FAT_FLAGS) $(CFLAGS) $(LIBFLAGS) $^ $(OBJ)/lto_hack.o -o $@ + -@rm $(OBJ)/lto_hack.o + +$(LIB)/libsameboy.a: $(LIB)/libsameboy.o + -@$(MKDIR) -p $(dir $@) + -@rm -f $@ + ar -crs $@ $^ + +$(INC)/%.h: Core/%.h + -@$(MKDIR) -p $(dir $@) + -@# CPPP doesn't like multibyte characters, so we replace the single quote character before processing so it doesn't complain + sed "s/'/@SINGLE_QUOTE@/g" $^ | cppp -UGB_INTERNAL | sed "s/@SINGLE_QUOTE@/'/g" > $@ + +lib-unsupported: + @echo Due to limitations of lld-link, compiling SameBoy as a library on Windows is not supported. + @false # Clean clean: rm -rf build -.PHONY: libretro tester cocoa ios _ios +.PHONY: libretro tester cocoa ios _ios ios-ipa ios-deb liblib-unsupported diff --git a/README.md b/README.md index 26f6e69..c94812e 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,10 @@ SameBoy is an open-source project licensed under the MIT license, and you're wel SameBoy requires the following tools and libraries to build: * clang (Recommended; required for macOS) or GCC * make - * macOS Cocoa port: macOS SDK and Xcode (For command line tools and ibtool) - * SDL port: libsdl2 + * macOS Cocoa frontend: macOS SDK and Xcode (For command line tools and ibtool) + * SDL frontend: libsdl2 * [rgbds](https://github.com/gbdev/rgbds/releases/), for boot ROM compilation + * [cppp](https://github.com/BR903/cppp), for cleaning up headers when compiling SameBoy as a library On Windows, SameBoy also requires: * Visual Studio (For headers, etc.) @@ -52,7 +53,8 @@ On Windows, SameBoy also requires: To compile, simply run `make`. The targets are: * `cocoa` (Default for macOS) * `sdl` (Default for everything else) - * `ios` (Plain iOS .app bundle), `ios-ipa` (iOS IPA archive for sideloading), `ios-deb` (iOS deb package for jailbroken devices) + * `lib` (Creates libsameboy.o and libsameboy.a for statically linking SameBoy, as well as a headers directory with corresponding headers; currently not supported on Windows due to linker limitations) + * `ios` (Plain iOS .app bundle), `ios-ipa` (iOS IPA archive for side-loading), `ios-deb` (iOS deb package for jailbroken devices) * `libretro` * `bootroms` * `tester` diff --git a/Windows/stdio.h b/Windows/stdio.h index 4a9279e..aeb7ec8 100755 --- a/Windows/stdio.h +++ b/Windows/stdio.h @@ -1,5 +1,13 @@ #pragma once + +#ifdef noinline +#undef noinline #include_next +#define noinline __attribute__((noinline)) +#else +#include_next +#endif + #include #include