Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ jobs:
-DWANT_PYTHON_WRAPPER=on
- name: ⚒️ Build shared
run: cmake --build build_shared --config Debug
- name: 🔍 Check delay-loaded DLLs
run: |
$env:Path += ";$(Split-Path -Path $(& "${env:programfiles(x86)}\microsoft visual studio\installer\vswhere" -latest -find "**\Hostx64\x64\dumpbin.exe" | Select-Object -Last 1) -Parent)"
function Show-Delay-Dlls {param([string]$file) Echo "Delay-loaded DLLs for ${file}:"; & dumpbin /dependents $file | ForEach-Object {switch($_) {" Image has the following delay load dependencies:" {$state=2} "" {$state--} default { if ($state -eq 1) {$_}}}} }
Show-Delay-Dlls build_shared\lib\Debug\allegro_audio-5.2.dll
Show-Delay-Dlls build_shared\lib\Debug\allegro_acodec-5.2.dll
Show-Delay-Dlls build_shared\lib\Debug\allegro_image-5.2.dll
- name: 🐍 Test Python wrapper
run: |
cp build_shared/python/allegro.py build_shared/lib/Debug/
Expand Down Expand Up @@ -86,12 +93,21 @@ jobs:
mingw-w64-x86_64-enet
mingw-w64-x86_64-libwebp
mingw-w64-x86_64-libopenmpt
mingw-w64-x86_64-openal
mingw-w64-x86_64-tools-git
- name: ⚙️ Configure
shell: msys2 {0}
run: cmake -B build -S . -G Ninja -DWANT_PYTHON_WRAPPER=on
- name: ⚒️ Build
shell: msys2 {0}
run: cmake --build build
- name: 🔍 Check delay-loaded DLLs
run: |
$env:Path += ";$(Split-Path -Path $(& "${env:programfiles(x86)}\microsoft visual studio\installer\vswhere" -latest -find "**\Hostx64\x64\dumpbin.exe" | Select-Object -Last 1) -Parent)"
function Show-Delay-Dlls {param([string]$file) Echo "Delay-loaded DLLs for ${file}:"; & dumpbin /dependents $file | ForEach-Object {switch($_) {" Image has the following delay load dependencies:" {$state=2} "" {$state--} default { if ($state -eq 1) {$_}}}} }
Show-Delay-Dlls build\lib\allegro_audio-5.2.dll
Show-Delay-Dlls build\lib\allegro_acodec-5.2.dll
Show-Delay-Dlls build\lib\allegro_image-5.2.dll
- name: 🐍 Test Python wrapper
shell: msys2 {0}
run: |
Expand Down
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ set(FRAMEWORK_INSTALL_PREFIX "/Library/Frameworks" CACHE STRING

option(PREFER_STATIC_DEPS "Whether to prefer static dependencies." off)

option(WANT_DELAYLOAD "Use delay-loaded DLLs on Windows" on)
include(cmake/DelayLoad.cmake)

#
# Platforms and drivers.
#
Expand Down
2 changes: 2 additions & 0 deletions addons/acodec/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -451,12 +451,14 @@ configure_file(
${PROJECT_BINARY_DIR}/include/allegro5/internal/aintern_acodec_cfg.h
)

DelayLoad(ACODEC_LIBRARIES ACODEC_LINK_OPTIONS)
add_our_addon_library(allegro_acodec
AllegroAcodec-${ALLEGRO_SOVERSION}
"${ACODEC_SOURCES};${ACODEC_INCLUDE_FILES}"
"-DALLEGRO_ACODEC_SRC ${FLAC__NO_DLL}"
"${AUDIO_LINK_WITH};${ACODEC_LIBRARIES}"
)
target_link_options(allegro_acodec PRIVATE "${ACODEC_LINK_OPTIONS}")

if(SUPPORT_OPENMPT AND SHARED AND NOT WANT_MONOLITH)
set_target_properties(allegro_acodec PROPERTIES LINKER_LANGUAGE CXX)
Expand Down
1 change: 1 addition & 0 deletions addons/acodec/allegro5/internal/aintern_acodec_cfg.h.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@


/* Define if the library should be loaded dynamically. */
#cmakedefine WANT_DELAYLOAD
#cmakedefine ALLEGRO_CFG_ACODEC_FLAC_DLL "@ALLEGRO_CFG_ACODEC_FLAC_DLL@"
#cmakedefine ALLEGRO_CFG_ACODEC_DUMB_DLL "@ALLEGRO_CFG_ACODEC_DUMB_DLL@"
#cmakedefine ALLEGRO_CFG_ACODEC_VORBISFILE_DLL "@ALLEGRO_CFG_ACODEC_VORBISFILE_DLL@"
Expand Down
4 changes: 2 additions & 2 deletions addons/acodec/flac.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ static void error_callback(const FLAC__StreamDecoder *decoder,
(void)decoder;
(void)client_data;

#ifdef ALLEGRO_CFG_ACODEC_FLAC_DLL
#if defined(ALLEGRO_CFG_ACODEC_FLAC_DLL) || defined(WANT_DELAYLOAD)
(void)status;
ALLEGRO_ERROR("Got FLAC error callback\n"); /* lazy */
#else
Expand Down Expand Up @@ -498,7 +498,7 @@ static FLACFILE *flac_open(ALLEGRO_FILE* f)
seek_callback, tell_callback, length_callback, eof_callback,
write_callback, metadata_callback, error_callback, ff);
if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
#ifdef ALLEGRO_CFG_ACODEC_FLAC_DLL
#if defined(ALLEGRO_CFG_ACODEC_FLAC_DLL) || defined(WANT_DELAYLOAD)
ALLEGRO_ERROR("Error initializing FLAC decoder\n"); /* lazy */
#else
ALLEGRO_ERROR("Error initializing FLAC decoder: %s\n",
Expand Down
2 changes: 2 additions & 0 deletions addons/audio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,14 @@ endif(NOT SUPPORT_AUDIO)

include_directories(SYSTEM ${AUDIO_INCLUDE_DIRECTORIES})
link_directories(${AUDIO_LINK_DIRECTORIES})
DelayLoad(AUDIO_LIBRARIES AUDIO_LINK_OPTIONS)
add_our_addon_library(allegro_audio
AllegroAudio-${ALLEGRO_SOVERSION}
"${AUDIO_SOURCES};${AUDIO_INCLUDE_FILES}"
"-DALLEGRO_KCM_AUDIO_SRC"
"${ALLEGRO_LINK_WITH};${AUDIO_LIBRARIES}"
)
target_link_options(allegro_audio PRIVATE ${AUDIO_LINK_OPTIONS})

install_our_headers(${AUDIO_INCLUDE_FILES})

Expand Down
2 changes: 2 additions & 0 deletions addons/image/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,14 @@ configure_file(
${PROJECT_BINARY_DIR}/include/allegro5/internal/aintern_image_cfg.h
)

DelayLoad(IMAGE_LIBRARIES IMAGE_LINK_OPTIONS)
add_our_addon_library(allegro_image
AllegroImage-${ALLEGRO_SOVERSION}
"${IMAGE_SOURCES};${IMAGE_INCLUDE_FILES}"
"-DALLEGRO_IIO_SRC"
"${ALLEGRO_LINK_WITH};${IMAGE_LIBRARIES}"
)
target_link_options(allegro_image PRIVATE ${IMAGE_LINK_OPTIONS})

install_our_headers(${IMAGE_INCLUDE_FILES})

Expand Down
112 changes: 112 additions & 0 deletions cmake/DelayLoad.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# TODO: move from addons/acodec/CMakeLists.txt
# as it is not available yet while building audio.
function(get_dll_name implib dllname_var)
if(MINGW)
# Guess the name of dlltool from gcc.
string(REGEX REPLACE "gcc.*" dlltool DLLTOOL ${CMAKE_C_COMPILER})
execute_process(
COMMAND ${DLLTOOL} -I ${implib}
OUTPUT_VARIABLE dllname
OUTPUT_STRIP_TRAILING_WHITESPACE
)
elseif(MSVC)
# Not sure this is the best way.
execute_process(
COMMAND lib /LIST ${implib}
OUTPUT_VARIABLE output
)
if(output STREQUAL "")
message("WARNING: Failed to execute lib /list")
else()
string(REGEX MATCH "[^\n]+[.]dll" dllname "${output}")
endif()
endif()
if(NOT dllname)
# Guess from the basename.
get_filename_component(basename "${implib}" NAME_WE)
set(dllname "${basename}.dll")
endif()
message(STATUS "DLL name for ${implib}: ${dllname}")
set(${dllname_var} ${dllname} PARENT_SCOPE)
endfunction(get_dll_name)

# Delay-load helper for Windows linkers.
#
# Brief: Receives the name of a CMake variable that holds one or more library
# names, and makes the corresponding DLLs to be delay-loaded on Windows.
#
# Details:
# - The argument is the variable's name (not its value). The variable should
# contain a single library or a list of libraries whose DLLs should be
# delay-loaded.
# - For each library listed, the function adds the appropriate linker options
# to request delay-loading of the matching DLL and ensures the delay-load
# support library is linked when required.
# - On non-Windows platforms, this function performs no action.
#
# Notes:
# - The delay-load mechanism (aka lazy loading) defers loading of the
# specified DLLs until the first time a symbol from each DLL is called at
# runtime.
function(DelayLoad libvar linkvar)
if(WANT_DELAYLOAD AND WIN32)
set(OUT "")
set(OPTS "")
foreach(lib ${${libvar}})
if(lib MATCHES "^[$].*") # Skip generator expressions
list(APPEND OUT "${lib}")
continue()
endif()
if(MSVC)
get_dll_name("${lib}" DLL)
list(APPEND OUT "${lib}")
if(NOT ${linkvar})
list(APPEND OUT "delayimp.lib")
endif()
list(APPEND OPTS "/DELAYLOAD:${DLL}")
set(${linkvar} ${OPTS} PARENT_SCOPE)
else(MSVC)
execute_process(
COMMAND sh -c "${PROJECT_SOURCE_DIR}/cmake/implib_delay.sh '${lib}'"
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/delayload"
OUTPUT_VARIABLE DELAYED_LIB
ERROR_VARIABLE ERR
RESULT_VARIABLE RES
OUTPUT_STRIP_TRAILING_WHITESPACE
)
list(APPEND OUT "${DELAYED_LIB}")
endif(MSVC)
if (RES)
message(FATAL_ERROR "DelayLoad: Failed to process library '${lib}': ${ERR}")
endif(RES)
endforeach()
set(${libvar} ${OUT} PARENT_SCOPE)
endif(WANT_DELAYLOAD AND WIN32)
endfunction(DelayLoad)

if(WANT_DELAYLOAD AND WIN32)
if(MSVC)
set(DELAYLOAD_LINKER_OPTIONS "delayimp.lib")
if(NOT DEFINED ENV{WindowsSdkDir})
# This should not happen if ran from a proper Visual Studio environment
# This is mostly a fallback for CI
block(SCOPE_FOR VARIABLES)
cmake_host_system_information(RESULT root QUERY WINDOWS_REGISTRY
"HKLM\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots"
VALUE "KitsRoot10"
)
cmake_host_system_information(RESULT sdks QUERY WINDOWS_REGISTRY
"HKLM\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots"
SUBKEYS
)
list(POP_BACK sdks ver)
set(ENV{WindowsSDKDir} ${root})
set(ENV{WindowsSDKVersion} ${ver})
message(STATUS "DelayLoad: inferred Windows SDK path: $ENV{WindowsSDKDir}, version: $ENV{WindowsSDKVersion}")
endblock()
endif()
else(MSVC)
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_BINARY_DIR}/delayload")
link_directories("${PROJECT_BINARY_DIR}/delayload")
endif(MSVC)
endif(WANT_DELAYLOAD AND WIN32)
2 changes: 2 additions & 0 deletions cmake/FindGDIPLUS.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ find_path(GDIPLUS_INCLUDE_DIR
Include
Include/um
Include/shared
um
PATHS
"${WINSDK_ROOT_DIR}"
${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}
)
if(EXISTS ${GDIPLUS_INCLUDE_DIR}/GdiPlus.h)
set(GDIPLUS_LOWERCASE 0 CACHE INTERNAL "Is GdiPlus.h spelt with lowercase?")
Expand Down
51 changes: 51 additions & 0 deletions cmake/implib_delay.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
# Create a delay-loaded import library in CWD for a given -l<name>
#
# Usage: ./implib_delay.sh -lgdiplus
set -euo pipefail

err() { printf '%s\n' "implib_delay.sh: $*" >&2; }
die() { err "$@"; exit 1; }

[[ $# -eq 1 ]] || die "exactly one argument required (e.g. -lgdiplus)"
arg="$1"

libname=""
case "$arg" in
-l*) libname="${arg#-l}" ;;
lib*.a) libname="${arg#lib}"; libname="${libname%.a}" ;;
*) libname="$arg" ;;
esac
[[ -n "$libname" ]] || die "could not parse library name from '$arg'"

if [[ -f "$arg" ]]; then
implib="$arg"
# Derive libname from provided file path (e.g. /path/libfoo.a -> foo)
bn="$(basename "$implib")"
t="${bn#lib}"
t="${t%.dll.a}"
t="${t%.a}"
[[ -n "$t" && "$t" != "$bn" ]] || die "could not derive library name from file '$implib'"
libname="$t"
else
implib="$("${CC:-gcc}" -print-file-name="lib${libname}.a")"
[[ "$implib" != "lib${libname}.a" ]] || die "import library lib${libname}.a not found in compiler search paths"
fi

implib_dir="$(dirname "$implib")"
out_delaylib="lib${libname}_delay.a"

if [[ ! -f "$out_delaylib" ]]; then
dllname="$(dlltool --identify "$implib" 2>/dev/null | tr -d '\r' | head -n1 || true)"
[[ -n "$dllname" ]] || exit 0 # Not a DLL import library; nothing to do
dllpath="$(which "$dllname" 2>/dev/null | tr -d '\r' | head -n1 || true)"
[[ -n "$dllpath" && -f "$dllpath" ]] || die "could not locate $dllname on disk"
gendef "$dllpath" > /dev/null
def_file="${dllname%.dll}.def"
[[ -f "$def_file" ]] || die "gendef did not produce $def_file"
dlltool -k -d "$def_file" -y "$out_delaylib"
[[ -f "$out_delaylib" ]] || die "failed to create delay import library: $out_delaylib"
fi

printf -- "-l%s_delay\n" "$libname"
# echo "$out_delaylib"
Loading