diff --git a/.gitignore b/.gitignore index 47b965b..f55728e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -build +/build* docs/html diff --git a/BUILDING b/BUILDING index 0fe4c36..016ff1d 100644 --- a/BUILDING +++ b/BUILDING @@ -1,17 +1,70 @@ How to build cerritos 1. Install dependencies -cmake libsdl2 doxygen graphviz libsdl2-image-dev libsdl2-ttf-dev +Linux (package names may vary), in Terminal: +> {your packager} libsdl2 doxygen graphviz libsdl2-image-dev libsdl2-ttf-dev freealut-dev +For example, on Arch: +> sudo pacman -S cmake make gcc sdl2 sdl2_ttf sdl2_image openal freealut doxygen graphviz +On Ubuntu: +> sudo apt install cmake make gcc libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libopenal-dev doxygen graphviz + +For the optional Conan path, install python, then pip (https://pip.pypa.io/en/stable/installation/) +> python -m ensurepip --upgrade +then install conan over pip (https://docs.conan.io/en/2.0/installation.html): +> pip install 'conan<2.0' --upgrade + +macOS: +Get XCode: https://developer.apple.com/support/xcode/ +Get Homebrew via https://brew.sh/; then, in Terminal: +> brew install cmake make sdl2 sdl2_image sdl2_ttf freealut doxygen graphviz + +For the optional Conan path, install python, pip (https://pip.pypa.io/en/stable/installation/) +> python -m ensurepip --upgrade +then install conan over pip (https://docs.conan.io/en/2.0/installation.html): +> pip install 'conan<2.0' --upgrade + +Windows via Visual Studio and Conan (will not have OpenAL support): +Install Visual Studio, for example the 2022 Community Edition: https://visualstudio.microsoft.com/de/vs/community/ +Only the C++ Desktop Development package should be required. +In cmd (all a bit more involved as Windows is lacking some basic tools): +> winget install Git.Git +> winget install cmake +> winget install python +optional: Visual Studio Code editor, not required for building: +> winget install code + +disable python alias via Settings -> Manage App Execution Aliases (use search) +DO NOT install Microsoft store version of python, its path settings seem broken + +In Git Bash (installed with git), install pip (https://pip.pypa.io/en/stable/installation/) +> py -m ensurepip --upgrade +then install conan over pip (https://docs.conan.io/en/2.0/installation.html): +> pip install 'conan<2.0' --upgrade + +Windows via MSYS2, a more Unix-like environment (not yet fully functional): +Install MSYS2 (https://www.msys2.org/) +Start a shell in the MINGW32 environment (or, should you want 64 bit binaries, MINGW64) +Install pactoys for easier typing: +> pacman -S pactoys +Install dependencies: +> pacboy -S cmake:p gcc:p SDL2:p SDL2_ttf:p SDL2_image:p openal:p freealut:p doxygen:p graphviz:p +Optionally also make:p for makefile based compilation if you prefer that; the system defaults to ninja. + +Cross Compilation: +Look at the scripts and documentation in the crosscompile directory 2. Create build directory -mkdir build -cd build +> mkdir build +> cd build + +3. Get Dependencies via Conan (Optional on Linux, macOS and MSYS2 if nothing went wrong in step 1, required for Visual Studio): +> conan install .. --build=missing -3. Run cmake -cmake ../ +4. Run cmake +> cmake .. -4. Build! -make +5. Build! +> cmake --build . TODO: Make this document a lot more detailed. diff --git a/CMakeLists.txt b/CMakeLists.txt index 05a1254..d4e3b22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,25 +6,47 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# create .lib file on Windows (alternative: export specifically all desired symbols with dllexport, make that a NOP on Unix platforms) +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + # set the project name project(cerritos VERSION 0.1.0) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) -include(sdl) -INCLUDE(FindPkgConfig) -#find_package(SDL_ttf REQUIRED) -#PKG_SEARCH_MODULE(SDL2 REQUIRED sdl2) -PKG_SEARCH_MODULE(SDL2IMAGE REQUIRED SDL2_image) -PKG_SEARCH_MODULE(SDL2TTF REQUIRED SDL2_ttf) - -# The actual cerritos sources -add_subdirectory(src) -# support libraries in thirdparty +# optionally include conan generated file +set(CONAN_FILE_TO_INCLUDE ${CMAKE_BINARY_DIR}/conan_paths.cmake) +set(USE_CONAN OFF) +if(EXISTS ${CONAN_FILE_TO_INCLUDE}) + include(${CONAN_FILE_TO_INCLUDE}) + set(USE_CONAN ON) +endif() # Build documentation option(BUILD_DOC "Build documentation" ON) option(USE_SDL "Use SDL" ON) +option(USE_OPENAL "Use OpenAL" ON) + +#include(sdl) +find_package(SDL2 REQUIRED) +find_package(SDL2_image REQUIRED) +find_package(SDL2_ttf REQUIRED) +if(USE_OPENAL) + find_package(OpenAL) +endif(USE_OPENAL) + +if(OPENAL_FOUND) + find_path(ALUT_INCLUDE_DIR + NAMES AL/alut.h) + find_library(ALUT_LIBRARY + NAMES alut + HINTS lib lib64 + PATH /usr /usr/local /opt/local /sw) +endif(OPENAL_FOUND) + +# The actual cerritos sources +add_subdirectory(src) +# support libraries in thirdparty if(USE_SDL) target_compile_definitions(cerritos PUBLIC USING_SDL) diff --git a/cmake/sdl.cmake b/cmake/sdl.cmake index 18fb4e1..74275ec 100644 --- a/cmake/sdl.cmake +++ b/cmake/sdl.cmake @@ -1,4 +1,4 @@ find_package(SDL2 REQUIRED) -include_directories(${SDL2_INCLUDE_DIRS}) +include_directories(SYSTEM ${SDL2_INCLUDE_DIRS}) diff --git a/conanfile.py b/conanfile.py new file mode 100755 index 0000000..3385a86 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,37 @@ +# standard conan instructions: +# make a new build directory, change into it +# install dependencies with +# conan install --build=missing + +from conans import ConanFile, CMake + +class CerritosConan(ConanFile): + settings = "os", "compiler", "build_type", "arch" + + generators = \ + "cmake_find_package", \ + "cmake_paths", \ + "CMakeDeps", \ + "VirtualBuildEnv" + + requires = \ + "sdl/[^2.0 >=2.26.1]", \ + "sdl_ttf/[^2.0 >=2.0.15]", \ + "sdl_image/[^2.0 >=2.0.5]", \ + "libpng/[^1]", \ + "xz_utils/[^5]" + + # libpng and xy_utils are for conflict resolution; apparently it is enough to just state the conflicting transitive + # dependencies with a broad version specification. + + + # graphviz is not in Conan. Figures, it is a perl program. + # Hope it is optional or can be manually installed if needed. + + def configure(self): + if self.settings.os != "Macos" and self.settings.compiler != "Visual Studio": + self.options["sdl"].iconv = False + + def imports(self): + self.copy("*.dll", dst="bin", src="bin") + self.copy("*.dylib", dst="bin", src="lib") diff --git a/crosscompile/linux_to_windows.sh b/crosscompile/linux_to_windows.sh new file mode 100755 index 0000000..fa399ab --- /dev/null +++ b/crosscompile/linux_to_windows.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# Usage: linux_to_windows.sh +# call from your build directory. So, for example, you would do starting from the main source: +# > mkdir build_win64 +# > cd build_win64 +# > ../crosscompile/linux_to_windows.sh + +# Prerequisites (aside from conan and cmake): +# Install mingw-w64 +# Ubunutu: sudo apt-get install g++-mingw-w64 gcc-mingw-w64 +# Arch: sudo pacman -S mingw-w64-crt + +# Documentation this is based on: +# https://docs.conan.io/1/systems_cross_building/cross_building.html#cross-building-examples-profiles +# https://docs.conan.io/2.0/tutorial/consuming_packages/cross_building_with_conan.html +# https://www.aroeira.io/post/cross-compile/ + +set -x + +SOURCE_PATH=$(dirname $(dirname $0)) + +# see if we need to update the conan configuration +if test "${SOURCE_PATH}"/conanfile.txt -nt conan_paths.cmake || test "$0" -nt conan_paths.cmake; then + rm -f CMakeCache.txt conan.lock conan_paths.cmake + + # determine mingw version + + # generate profile data + toolchain=/usr/x86_64-w64-mingw32 # Maybe adjust this path + target_host=x86_64-w64-mingw32 + cc_compiler=gcc + cxx_compiler=g++ + + # somehow, one has to set this manually to a cross compiler, or some conan builds fail + export CC=$target_host-$cc_compiler + export CXX=$target_host-$cxx_compiler + + # horrible way to fetch conan compliant version from mingw. + # conan expects at most two versions segments, no trailing zero. So 1, 1.1, 2, 2.1, 2.2 + # # to here: line with version at the end + # # to here: just the version, bits before cut off + # # to here: cut off any more than two version segments + # # cut off trailing 0 + compiler_version=$($target_host-$cc_compiler --version | head -n 1 | sed -e 's/.* //g' | sed -E 's/([^\.]*)\.([^\.]*)\..*/\1.\2/' | sed -e 's/^{0}/\0/') + + # compose profle + cat > win64.profile << EOF + toolchain=$toolchain + target_host=$target_host + cc_compiler=$cc_compiler + cxx_compiler=$cxx_compiler + + [buildenv] + CONAN_CMAKE_FIND_ROOT_PATH=\$toolchain # Optional, for CMake to find things in that folder + CONAN_CMAKE_SYSROOT=\$toolchain # Optional, if we want to define sysroot + CHOST=\$target_host + AR=\$target_host-ar + AS=\$target_host-as + RANLIB=\$target_host-ranlib + CC=\$target_host-\$cc_compiler + CXX=\$target_host-\$cxx_compiler + STRIP=\$target_host-strip + RC=\$target_host-windres + + [settings] + # We are cross-building to Windows + os=Windows + arch=x86_64 + compiler=gcc + + compiler.version=$compiler_version + #compiler.version=12 + compiler.libcxx=libstdc++11 + build_type=Release +EOF + + #conan install "${SOURCE_PATH}" -pr:b=default -pr:h=win64.profile --no-imports || exit $? + + # use the generated profile to make conan install the correct cross compilation libraries + #conan install "${SOURCE_PATH}" --build=missing -s:b os_build=Linux -pr=win64.profile || exit $? + conan install "${SOURCE_PATH}" --build=missing -pr:b=default -pr:h=win64.profile || exit $? +fi + +# use the conan provided build environment from here on +source ./conanbuild.sh || exit $? + +# build configuration with CMAKE. All further arguments are passed to it +# USE_OPENAL=OFF is required because we can't install OpenAL over conan and +# cmake would pick up a native version, if available +cmake -DUSE_OPENAL=OFF "${SOURCE_PATH}" "$@" || exit $? + +# build with maximum paralellism +cmake --build . -j $(( `nproc` + 1 )) || exit $? + diff --git a/crosscompile/wine.md b/crosscompile/wine.md new file mode 100644 index 0000000..b47ed65 --- /dev/null +++ b/crosscompile/wine.md @@ -0,0 +1,138 @@ +# How to compile for Windows under Wine + +## Why? I mean, why? Why not just use cross compilation? + +You want the deployed build to be as similar as possible to a build you can actually debug. So, +ideally, you would want to deploy builds done on a Windows machine with Windows development tools, +because that is where your hordes of Windows developers would be working. +But you may not have one readily available, so the next best thing is to use Windows build tools +on Wine. + +Also, using this way, all the build tools know they are compiling on Windows for Windows and will +name their output files accordingly. + +## Problem + +Conan can in principle build all our dependencies. On Wine, however, that fails, because Conan wants +to use msys2 to be able to use Unix-like build methods, and that does not work in Wine. There are no +usable prebuilt packages available. + +## Workaround + +Conan can use packages prebuilt with the cross compiler, provided the used compilers are similar enough. +A suitable environment with all the right tools on the Windows side is [w64devkit](https://github.com/skeeto/w64devkit). +It uses the same MinGW base as the cross compiler recommended in linux_to_windows.sh. + +## Setup + +Basically, follow the instructions for native Windows builds. +However, winget does not work in wine, so you have to install everything by hand or with winetricks. +* Git: Not required, just use git and your favorite shell from the Linux side +* VS Code: Not required + +* CMake: + * Comfortable: + > winetricks cmake + * Manual: + [Download](https://cmake.org/download/), pick the .msi installer + + + Choose 'Add CMake to system PATH' (current user or all users should not matter) + +* w64devkit: + [Download](https://github.com/skeeto/w64devkit/releases), Pick the most basic w64devkit-*.zip + + Unzip to your Windows C drive (~/.wine/drive_c) directly, it will go to the folder + w64devkit. Nothing more is required. + +* Python: + + [Download](https://www.python.org/downloads/windows/), pick the 64 bit zip (embeddable) archive + + Unzip to your Windows C drive (~/.wine/drive_c), directory Python + + Unzip the contained python*.zip directly there, too + + Add that folder to the Windows search path: execute + > wine regedit + + navigate to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment, + edit the value PATH; append ';C:\Python;C:\Python\Scripts;C:\w64devkit\bin'. + (the w64devkit bit also makes w64devkit your default dev environment) + +* Pip: Via the get-pip.py method: https://pip.pypa.io/en/stable/installation/#get-pip-py + > wget https://bootstrap.pypa.io/get-pip.py + > wine python get-pip.py + + This installs the pip module in C:\Python\Lib\site-packages... where it is not found. + Setting the PTHONPATH by any means to include it did not work for me (advice welcome), + so I ended up just doing in ~/.wine/drive_c/Python: + > ln -s Lib/site-packages/* . + +* Conan: easy peasy if everything above went right: + > wine pip install 'conan<2.0' + + Followed by another + > ln -s Lib/site-packages/* . + + in ~/.wine/drive_c/Python. + +## Now comes the magic bit + +In Linux, at least once, do a cross compilation build with linux_to_windows.sh. It does not +have to be a complete build, getting the Conan-managed dependencies compiled is enough. +These dependencies are residing in ~/.conan/data. Important: Make sure that the cross compilation +MinGW you installed is compatible with the MinGW version of w64devkit. If the cross compilation +one has the same or a lower major version, that usually is good enough. + +The commands for that are, from the cerritos main directory: +> mkdir build_cross + +> cd build_cross + +> ../crosscompile/linux_to_windows.sh --cmake-should-fail + +Keep the directory around for now, we will need it. + +Then, also at least once, can be from the same directory, try to install +the dependencies via Conan. This will fail: + +> cd .. + +> mkdir build_win64 + +> cd build_win64 + +> wine conan install .. + +But now you have Conan set up in your wine environment. It expects its dependencies +in C:\Users\\your unix username\\.conan\\data. So, you simply link that directory +and the one from the cross compilation together: + +> rm -rf ~/.wine/drive_c/users/your unix username/.conan/data + +> ln -sf ~/.conan/data ~/.wine/drive_c/users/your unix username/.conan/ + +That is not quite enough, though; Conan only uses compiled packages of the +correct configuration. We have to convince it that the cross compilation +configuration is identical to the Wine configuration. Conan uses profile files +for that. The one for cross compilation is still in `../build_cross/win64.profile`. +The one for the Wine compilation is in +`~/.wine/drive_c/users/your unix username/.conan/profiles/default`. Open them both, +copy the values in the [settings] section from win64.profile to default. +The relevant ones would be os, arch, compiler, compiler.version, compiler.libcxx and +build_type. + +Here is also your chance to check whether the MinGW versions were compatible. In default, +Conan has detected the version of the one installed in Wine. So if you are making that +bigger in the major version, expect failures. + +Then it is time for another go. This time, Conan should find and accept the precompiled +libraries. + +> wine conan install .. + +CMake needs a special parameter to create usable makefiles: +> wine cmake .. -G "MinGW Makefiles" + +> wine cmake --build . diff --git a/examples/dadsteroids.cmake b/examples/dadsteroids.cmake index 074adb7..a516825 100644 --- a/examples/dadsteroids.cmake +++ b/examples/dadsteroids.cmake @@ -10,3 +10,10 @@ target_include_directories(dadsteroids PUBLIC target_link_libraries(dadsteroids cerritos ) + +# copy dll on windows into binary directory +if(WIN32) + add_custom_command(TARGET dadsteroids POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $ $ + ) +endif(WIN32) diff --git a/examples/drumTest.cmake b/examples/drumTest.cmake index d3c9fe9..1687f13 100644 --- a/examples/drumTest.cmake +++ b/examples/drumTest.cmake @@ -10,3 +10,10 @@ target_include_directories(drumTest PUBLIC target_link_libraries(drumTest cerritos ) + +# copy dll on windows into binary directory +if(WIN32) + add_custom_command(TARGET drumTest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $ $ + ) +endif(WIN32) diff --git a/examples/eventTest.cmake b/examples/eventTest.cmake index ea856a2..9e378b5 100644 --- a/examples/eventTest.cmake +++ b/examples/eventTest.cmake @@ -10,3 +10,9 @@ target_link_libraries(eventTest cerritos ) +# copy dll on windows into binary directory +if(WIN32) + add_custom_command(TARGET eventTest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $ $ +) +endif(WIN32) diff --git a/examples/fontTest.cmake b/examples/fontTest.cmake index 4a3105d..5f14bc3 100644 --- a/examples/fontTest.cmake +++ b/examples/fontTest.cmake @@ -10,3 +10,10 @@ target_include_directories(fontTest PUBLIC target_link_libraries(fontTest cerritos ) + +# copy dll on windows into binary directory +if(WIN32) + add_custom_command(TARGET fontTest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $ $ + ) +endif(WIN32) diff --git a/examples/hello.cmake b/examples/hello.cmake index 7cd4d9c..acde522 100644 --- a/examples/hello.cmake +++ b/examples/hello.cmake @@ -11,3 +11,9 @@ target_link_libraries(hello cerritos ) +# copy dll on windows into binary directory +if(WIN32) + add_custom_command(TARGET hello POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $ $ + ) +endif(WIN32) diff --git a/examples/pathTest.cmake b/examples/pathTest.cmake index 59f2ae5..a2b9323 100644 --- a/examples/pathTest.cmake +++ b/examples/pathTest.cmake @@ -10,3 +10,10 @@ target_include_directories(pathTest PUBLIC target_link_libraries(pathTest cerritos ) + +# copy dll on windows into binary directory +if(WIN32) + add_custom_command(TARGET pathTest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $ $ + ) +endif(WIN32) diff --git a/examples/spriteTest.cmake b/examples/spriteTest.cmake index 7f31ec5..4c674d4 100644 --- a/examples/spriteTest.cmake +++ b/examples/spriteTest.cmake @@ -11,3 +11,9 @@ target_link_libraries(spriteTest cerritos ) +# copy dll on windows into binary directory +if(WIN32) + add_custom_command(TARGET spriteTest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $ $ + ) +endif(WIN32) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 24421d4..2d08bf8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -69,11 +69,7 @@ install(FILES DESTINATION include ) -target_include_directories(cerritos PUBLIC - ${SDL2_LIBRARIES} - ${SDL2IMAGE_INCLUDE_DIRS} - ${SDL2TTF_INCLUDE_DIRS} - ${OPENAL_INCLUDE_DIRS} +target_include_directories(cerritos BEFORE PUBLIC . backend core @@ -85,16 +81,35 @@ target_include_directories(cerritos PUBLIC widgets ) -target_link_libraries(cerritos - ${SDL2_LIBRARIES} - ${SDL2IMAGE_LIBRARIES} - ${SDL2TTF_LIBRARIES} - ${OPENAL_LIBRARIES} - alut - stdc++fs - stdc++ +target_include_directories(cerritos SYSTEM PUBLIC + ${SDL2_INCLUDE_DIRS} + ${SDL2IMAGE_INCLUDE_DIRS} + ${SDL2TTF_INCLUDE_DIRS} +) + +target_link_libraries(cerritos PUBLIC + SDL2::SDL2 + SDL2_image::SDL2_image + SDL2_ttf::SDL2_ttf + $<$,$,9.1>>:stdc++fs> ) +if(OPENAL_FOUND) + # having this PUBLIC is probably bad style, but the ALUT definitions + # are required in a header right now... + target_compile_definitions(cerritos PUBLIC USING_OPENAL) + + target_include_directories(cerritos SYSTEM PUBLIC + ${OPENAL_INCLUDE_DIR} + ${ALUT_INCLUDE_DIR} + ) + + target_link_libraries(cerritos PUBLIC + ${OPENAL_LIBRARY} + ${ALUT_LIBRARY} + ) +endif(OPENAL_FOUND) + install(TARGETS cerritos EXPORT cerritos LIBRARY DESTINATION lib diff --git a/src/core/sound.cpp b/src/core/sound.cpp index bb3dd52..a05cff5 100644 --- a/src/core/sound.cpp +++ b/src/core/sound.cpp @@ -4,8 +4,10 @@ using namespace cerritos; Sound::Sound(int numSources) { +#ifdef USING_OPENAL alutInit(NULL, NULL); - +#endif + for (int i = 0; i < numSources; i++) { ALuint buffer; m_Buffers.push_back(buffer); @@ -17,15 +19,21 @@ Sound::Sound(int numSources) { } } Sound::~Sound() { +#ifdef USING_OPENAL alutExit(); +#endif } void Sound::LoadSoundFromFile(const char *filename, int index) { +#ifdef USING_OPENAL m_Buffers[index] = alutCreateBufferFromFile(filename); alGenSources((ALuint)1, &m_Sources[index]); alSourcei(m_Sources[index], AL_BUFFER, m_Buffers[index]); +#endif } void Sound::PlaySound(int index) { +#ifdef USING_OPENAL alSourcePlay(m_Sources[index]); +#endif } diff --git a/src/core/sound.h b/src/core/sound.h index 64d83c8..dc3880c 100644 --- a/src/core/sound.h +++ b/src/core/sound.h @@ -1,4 +1,12 @@ +#ifdef USING_OPENAL #include +#else +// rudimentary type replacements +using ALvoid = void; +using ALuint = unsigned int; +// this probably needs competely different abstractions... good enough for just having sound disabled. +#endif + #include "types.h" namespace cerritos { diff --git a/src/thirdparty/binreloc/binreloc.c b/src/thirdparty/binreloc/binreloc.c index 84c22c4..437718c 100644 --- a/src/thirdparty/binreloc/binreloc.c +++ b/src/thirdparty/binreloc/binreloc.c @@ -28,7 +28,11 @@ #include #include #include +#ifdef __APPLE__ +#include +#else #include +#endif #include "binreloc.h"