Skip to content

Commit 6068577

Browse files
committed
cmake: support building a shared SDL3 DOS executable
1 parent c65c809 commit 6068577

9 files changed

Lines changed: 232 additions & 15 deletions

File tree

.github/actions/setup-djgpp-toolchain/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,5 @@ runs:
6363
id: final
6464
shell: pwsh
6565
run: |
66+
echo "${{ runner.temp }}/djgpp/i586-pc-msdosdjgpp/bin" >> $env:GITHUB_PATH
6667
echo "${{ runner.temp }}/djgpp/bin" >> $env:GITHUB_PATH

.github/workflows/create-test-plan.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ class SharedLibType(Enum):
164164
SO_0 = "libSDL3.so.0"
165165
SO = "libSDL3.so"
166166
DYLIB = "libSDL3.0.dylib"
167+
DXE = "SDL3.dxe"
167168
FRAMEWORK = "SDL3.framework/Versions/A/SDL3"
168169

169170

@@ -829,11 +830,10 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
829830
job.apt_packages = ["ccache", "libfl-dev"] # djgpp needs libfl.so.2
830831
job.cmake_build_type = "Release"
831832
job.setup_ninja = True
833+
job.shared_lib = SharedLibType.DXE
832834
job.static_lib = StaticLibType.A
833-
job.shared_lib = None
834835
job.clang_tidy = False
835836
job.werror = False # FIXME: enable SDL_WERROR
836-
job.shared = False
837837
job.run_tests = False
838838
job.test_pkg_config = False
839839
job.cmake_toolchain_file = "$GITHUB_WORKSPACE/build-scripts/i586-pc-msdosdjgpp.cmake"

CMakeLists.txt

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,19 @@ if(EMSCRIPTEN)
208208
set(SDL_SHARED_AVAILABLE OFF)
209209
endif()
210210

211-
if(VITA OR PSP OR PS2 OR N3DS OR RISCOS OR NGAGE OR DOS)
211+
if(DOS)
212+
if(NOT DJGPP)
213+
message(FATAL_ERROR "SDL3 DOS support requires a DJGPP toolchain")
214+
endif()
215+
216+
find_program(DJGPP_DXE3GEN_BIN NAMES dxe3gen)
217+
if(NOT EXISTS "${DJGPP_DXE3GEN_BIN}")
218+
message(STATUS "Disabling shared library support: dxe3gen not found")
219+
set(SDL_SHARED_AVAILABLE OFF)
220+
endif()
221+
endif()
222+
223+
if(VITA OR PSP OR PS2 OR N3DS OR RISCOS OR NGAGE)
212224
set(SDL_SHARED_AVAILABLE OFF)
213225
endif()
214226

@@ -552,7 +564,7 @@ if (LIBC_IS_GLIBC AND CMAKE_SIZEOF_VOID_P EQUAL 4)
552564
endif()
553565

554566
check_linker_supports_version_file(HAVE_WL_VERSION_SCRIPT)
555-
if(HAVE_WL_VERSION_SCRIPT)
567+
if(HAVE_WL_VERSION_SCRIPT AND (NOT DOS))
556568
sdl_shared_link_options("-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/dynapi/SDL_dynapi.sym")
557569
else()
558570
# When building with tcc on Linux+glibc or Android, avoid emitting an error
@@ -696,7 +708,7 @@ if(USE_GCC OR USE_CLANG OR USE_INTELCC OR USE_QCC)
696708
endif()
697709
endif()
698710

699-
if(NOT OPENBSD)
711+
if(NOT (DOS OR OPENBSD))
700712
cmake_push_check_state()
701713
check_linker_flag(C "-Wl,--no-undefined" LINKER_SUPPORTS_WL_NO_UNDEFINED)
702714
#FIXME: originally this if had an additional "AND NOT (USE_CLANG AND WINDOWS)"
@@ -4075,6 +4087,13 @@ if(SDL_SHARED)
40754087
)
40764088
endif()
40774089
endif()
4090+
if(DJGPP)
4091+
set_property(TARGET SDL3-shared SDL_uclibc PROPERTY POSITION_INDEPENDENT_CODE FALSE)
4092+
set_property(TARGET SDL3-shared PROPERTY IMPORT_SUFFIX ".dxe.a")
4093+
set_property(TARGET SDL3-shared PROPERTY SUFFIX ".dxe")
4094+
set_property(TARGET SDL3-shared PROPERTY C_LINKER_LAUNCHER "${CMAKE_CURRENT_SOURCE_DIR}/build-scripts/djgpp-dxe-linker-wrapper.py;--dxe3gen=${DJGPP_DXE3GEN_BIN};--import-library;$<TARGET_IMPORT_FILE:SDL3-shared>;--library-paths;${DJGPP_LIBRARY_PATHS};--")
4095+
set_property(TARGET SDL3-shared APPEND PROPERTY LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/build-scripts/djgpp-dxe-linker-wrapper.py")
4096+
endif()
40784097

40794098
target_link_libraries(SDL3-shared PRIVATE ${SDL_CMAKE_DEPENDS})
40804099
target_include_directories(SDL3-shared
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import os
5+
import sys
6+
import subprocess
7+
8+
def parse_wrapper_args(args):
9+
parser = argparse.ArgumentParser(allow_abbrev=False)
10+
parser.add_argument("--library-paths", nargs="*", help="Additional DJGPP Library paths")
11+
parser.add_argument("--dxe3gen", default="dxe3gen", help="Path of dxe3gen")
12+
parser.add_argument("--import-library", help="Import library name")
13+
args = parser.parse_args(args)
14+
return args
15+
16+
def parse_linker_args(args):
17+
unknown_args = []
18+
output = None
19+
i = 1
20+
while i < len(args):
21+
arg = args[i]
22+
if arg in ("-fPIC", "-shared"):
23+
consumed = 1
24+
elif arg.startswith("-Wl,"):
25+
unknown_args.extend(arg[4:].split(","))
26+
elif arg.startswith("-I"):
27+
if arg == "-I":
28+
consumed = 2
29+
else:
30+
consumed = 1
31+
elif arg == "-o":
32+
output = args[i + 1]
33+
consumed = 2
34+
elif arg.startswith("-D"):
35+
consumed = 1
36+
elif arg.startswith("-O"):
37+
consumed = 1
38+
else:
39+
unknown_args.append(arg)
40+
consumed = 1
41+
i += consumed
42+
if not output:
43+
print("Missing \"-o\" argument", file=sys.stderr)
44+
return output, unknown_args
45+
46+
try:
47+
pos_double_dash = sys.argv.index("--")
48+
wrapper_args = sys.argv[1:pos_double_dash]
49+
original_args = sys.argv[pos_double_dash+1:]
50+
except IndexError:
51+
wrapper_args = []
52+
original_args = sys.argv[1:]
53+
54+
wrapper_args = parse_wrapper_args(wrapper_args)
55+
output_path, original_linker_args = parse_linker_args(original_args)
56+
57+
dxe3gen_command = [
58+
wrapper_args.dxe3gen,
59+
"-o", output_path,
60+
"-U",
61+
]
62+
if wrapper_args.import_library:
63+
dxe3gen_command.extend(["-Y", wrapper_args.import_library])
64+
for libpath in wrapper_args.library_paths:
65+
dxe3gen_command.append("-L" + libpath)
66+
dxe3gen_command.extend(original_linker_args)
67+
if not "-nostlib" in original_linker_args:
68+
dxe3gen_command.append("-lgcc")
69+
70+
os.environ["DXE_LD_LIBRARY_PATH"] = "dontcare"
71+
os.environ["DJDIR"] = "dontcare"
72+
73+
process_result = subprocess.run(dxe3gen_command)
74+
75+
raise SystemExit(process_result.returncode)

build-scripts/djgpp-platform-overrides.cmake

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@
1111

1212
set(CMAKE_STATIC_LIBRARY_PREFIX "lib")
1313
set(CMAKE_STATIC_LIBRARY_SUFFIX ".a")
14+
set(CMAKE_SHARED_LIBRARY_PREFIX "")
15+
set(CMAKE_SHARED_LIBRARY_SUFFIX ".dxe")
16+
set(CMAKE_IMPORT_LIBRARY_PREFIX "lib")
17+
set(CMAKE_IMPORT_LIBRARY_SUFFIX ".dxe.a")
18+
1419
set(CMAKE_LINK_LIBRARY_SUFFIX "")
1520
set(CMAKE_FIND_LIBRARY_PREFIXES "lib" "")
1621
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".lib")
17-
set(CMAKE_EXECUTABLE_SUFFIX ".exe")
22+
set(CMAKE_EXECUTABLE_SUFFIX ".exe")

build-scripts/i586-pc-msdosdjgpp.cmake

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ set(CMAKE_USER_MAKE_RULES_OVERRIDE "${DJGPP_PLATFORM_OVERRIDES}")
1515
set(CMAKE_STATIC_LIBRARY_PREFIX "lib")
1616
set(CMAKE_STATIC_LIBRARY_SUFFIX ".a")
1717
set(CMAKE_SHARED_LIBRARY_PREFIX "")
18-
set(CMAKE_SHARED_LIBRARY_SUFFIX ".dll")
18+
set(CMAKE_SHARED_LIBRARY_SUFFIX ".dxe")
1919
set(CMAKE_IMPORT_LIBRARY_PREFIX "lib")
20-
set(CMAKE_IMPORT_LIBRARY_SUFFIX ".a")
20+
set(CMAKE_IMPORT_LIBRARY_SUFFIX ".dxe.a")
2121
set(CMAKE_EXECUTABLE_SUFFIX ".exe")
2222
set(CMAKE_LINK_LIBRARY_SUFFIX "")
2323
set(CMAKE_DL_LIBS "")
@@ -67,6 +67,11 @@ endforeach()
6767

6868
list(APPEND CMAKE_FIND_ROOT_PATH ${CC_ROOTS})
6969

70+
set(DJGPP_LIBRARY_PATHS )
71+
foreach(__cc_root ${CC_ROOTS})
72+
list(APPEND DJGPP_LIBRARY_PATHS "${__cc_root}" "${__cc_root}/lib")
73+
endforeach()
74+
7075
# search for programs in the host directories
7176
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
7277

@@ -79,4 +84,4 @@ if(NOT DEFINED CACHE{CMAKE_FIND_ROOT_PATH_MODE_INCLUDE})
7984
endif()
8085
if(NOT DEFINED CACHE{CMAKE_FIND_ROOT_PATH_MODE_PACKAGE})
8186
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
82-
endif()
87+
endif()

cmake/test/CMakeLists.txt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,13 @@ if(TEST_SHARED)
6565
)
6666
endif()
6767

68-
add_library(sharedlib-shared SHARED main_lib.c)
69-
target_link_libraries(sharedlib-shared PRIVATE SDL3::SDL3-shared)
70-
generate_export_header(sharedlib-shared EXPORT_MACRO_NAME MYLIBRARY_EXPORT)
71-
target_compile_definitions(sharedlib-shared PRIVATE "EXPORT_HEADER=\"${CMAKE_CURRENT_BINARY_DIR}/sharedlib-shared_export.h\"")
72-
set_target_properties(sharedlib-shared PROPERTIES C_VISIBILITY_PRESET "hidden")
68+
if(NOT DOS)
69+
add_library(sharedlib-shared SHARED main_lib.c)
70+
target_link_libraries(sharedlib-shared PRIVATE SDL3::SDL3-shared)
71+
generate_export_header(sharedlib-shared EXPORT_MACRO_NAME MYLIBRARY_EXPORT)
72+
target_compile_definitions(sharedlib-shared PRIVATE "EXPORT_HEADER=\"${CMAKE_CURRENT_BINARY_DIR}/sharedlib-shared_export.h\"")
73+
set_target_properties(sharedlib-shared PROPERTIES C_VISIBILITY_PRESET "hidden")
74+
endif()
7375

7476
add_executable(cli-shared main_cli.c)
7577
target_link_libraries(cli-shared PRIVATE SDL3::SDL3-shared)
@@ -96,7 +98,7 @@ if(TEST_STATIC)
9698
add_executable(gui-static WIN32 main_gui.c)
9799
target_link_libraries(gui-static PRIVATE SDL3::SDL3-static)
98100

99-
if(TEST_SHARED)
101+
if(TEST_SHARED AND NOT DOS)
100102
# Assume SDL library has been built with `set(CMAKE_POSITION_INDEPENDENT_CODE ON)`
101103
add_library(sharedlib-static SHARED main_lib.c)
102104
target_link_libraries(sharedlib-static PRIVATE SDL3::SDL3-static)

include/SDL3/SDL_main.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@
220220
void reset_IOP() {}
221221

222222
#elif defined(SDL_PLATFORM_DOS)
223+
223224
/*
224225
On DOS, SDL provides a main function that sets up memory
225226
page locking (code, data, stack are locked, future

include/SDL3/SDL_main_impl.h

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,113 @@
148148

149149
#endif /* SDL_MAIN_HANDLED */
150150

151+
#ifdef SDL_PLATFORM_DOS
152+
153+
/* On DOS, the executable must export symbols for the SDL3 module.
154+
*/
155+
#define FOR_EACH_DXE_EXPORT(X) \
156+
X(__Exit) \
157+
X(___dj_huge_val) \
158+
X(___dj_stderr) \
159+
X(___dj_stdin) \
160+
X(___djgpp_base_address) \
161+
X(___djgpp_nearptr_enable) \
162+
X(___dpmi_free_physical_address_mapping) \
163+
X(___dpmi_int) \
164+
X(___dpmi_physical_address_mapping) \
165+
X(___dpmi_simulate_real_mode_procedure_retf) \
166+
X(___fpclassifyd) \
167+
X(___fpclassifyf) \
168+
X(__go32_dpmi_allocate_dos_memory) \
169+
X(__go32_dpmi_chain_protected_mode_interrupt_vector) \
170+
X(__go32_dpmi_free_dos_memory) \
171+
X(__go32_dpmi_get_protected_mode_interrupt_vector) \
172+
X(__go32_dpmi_lock_code) \
173+
X(__go32_dpmi_lock_data) \
174+
X(__go32_dpmi_set_protected_mode_interrupt_vector) \
175+
X(__go32_info_block) \
176+
X(_abort) \
177+
X(_atof) \
178+
X(_atoi) \
179+
X(_calloc) \
180+
X(_clearerr) \
181+
X(_close) \
182+
X(_closedir) \
183+
X(_delay) \
184+
X(_dlregsym) \
185+
X(_dosmemput) \
186+
X(_environ) \
187+
X(_errno) \
188+
X(_fclose) \
189+
X(_ferror) \
190+
X(_fflush) \
191+
X(_fgets) \
192+
X(_fileno) \
193+
X(_fopen) \
194+
X(_fprintf) \
195+
X(_fputs) \
196+
X(_fread) \
197+
X(_free) \
198+
X(_fseeko) \
199+
X(_fstat) \
200+
X(_ftello) \
201+
X(_fwrite) \
202+
X(_getcwd) \
203+
X(_getenv) \
204+
X(_gethostname) \
205+
X(_getpagesize) \
206+
X(_gettimeofday) \
207+
X(_gmtime_r) \
208+
X(_itoa) \
209+
X(_localtime_r) \
210+
X(_longjmp) \
211+
X(_lseek) \
212+
X(_malloc) \
213+
X(_memcmp) \
214+
X(_memcpy) \
215+
X(_memmove) \
216+
X(_mkdir) \
217+
X(_opendir) \
218+
X(_read) \
219+
X(_readdir) \
220+
X(_realloc) \
221+
X(_remove) \
222+
X(_rename) \
223+
X(_searchpath) \
224+
X(_setenv) \
225+
X(_setjmp) \
226+
X(_sigaction) \
227+
X(_stat) \
228+
X(_strchr) \
229+
X(_strcmp) \
230+
X(_strerror) \
231+
X(_strlcat) \
232+
X(_strlcpy) \
233+
X(_strlen) \
234+
X(_strncmp) \
235+
X(_strnlen) \
236+
X(_strpbrk) \
237+
X(_strrchr) \
238+
X(_strstr) \
239+
X(_strtod) \
240+
X(_strtok_r) \
241+
X(_strtol) \
242+
X(_strtoll) \
243+
X(_strtoul) \
244+
X(_strtoull) \
245+
X(_uclock) \
246+
X(_unsetenv) \
247+
X(_vsnprintf) \
248+
X(_vsscanf) \
249+
X(_write)
250+
251+
#define EXTERN_ASM_SEMICOLON(V) extern_asm(V);
252+
253+
#include <sys/dxe.h>
254+
FOR_EACH_DXE_EXPORT(EXTERN_ASM_SEMICOLON)
255+
DXE_EXPORT_TABLE_AUTO(sdl3_export_syms)
256+
FOR_EACH_DXE_EXPORT(DXE_EXPORT_ASM)
257+
DXE_EXPORT_END
258+
#endif
259+
151260
#endif /* SDL_main_impl_h_ */

0 commit comments

Comments
 (0)