diff --git a/.github/workflows/CompileCheck.yml b/.github/workflows/CompileCheck.yml new file mode 100644 index 0000000..c20a415 --- /dev/null +++ b/.github/workflows/CompileCheck.yml @@ -0,0 +1,32 @@ +name: Full Compile Check + +on: [workflow_dispatch, push] + +jobs: + full-compile-check: + name: ${{ matrix.platform.name }} + runs-on: ${{ matrix.platform.os }} + + strategy: + fail-fast: false + matrix: + platform: + - { name: Linux/GCC (ancient), os: ubuntu-18.04, extraflags: "--system-sdl" } + - { name: Linux/GCC, os: ubuntu-latest, extraflags: "--system-sdl" } + - { name: Linux/Clang, os: ubuntu-latest, extraflags: "--system-sdl" } + - { name: Windows/VS2022, os: windows-2022, extraflags: "-G 'Visual Studio 17 2022'" } + - { name: Windows/VS2019, os: windows-2019, extraflags: "-G 'Visual Studio 16 2019'" } + - { name: Mac, os: macos-11 } + + steps: + - name: Install Linux build dependencies + if: runner.os == 'Linux' + run: | + sudo apt update + sudo apt install libsdl2-dev + + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + + - run: python3 build.py --dependencies --configure --build ${{ matrix.platform.extraflags }} diff --git a/.github/workflows/ReleaseBuilds.yml b/.github/workflows/ReleaseBuilds.yml new file mode 100644 index 0000000..f4141a7 --- /dev/null +++ b/.github/workflows/ReleaseBuilds.yml @@ -0,0 +1,80 @@ +name: Make Release Builds + +on: [workflow_dispatch] + +jobs: + build-linux-appimage: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 # Checks out repository under $GITHUB_WORKSPACE so the job can access it + with: + submodules: 'recursive' + - name: Get artifact name + run: | + echo "GAME_ARTIFACT=$(python3 build.py --print-artifact-name)" >> $GITHUB_ENV + echo $GAME_ARTIFACT + - name: Get build dependencies for SDL from APT # cf. https://github.com/libsdl-org/SDL/blob/main/docs/README-linux.md + run: | + sudo apt update + sudo apt install libasound2-dev libpulse-dev libaudio-dev libjack-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxi-dev libxinerama-dev libxxf86vm-dev libxss-dev libgl1-mesa-dev libdbus-1-dev libudev-dev libgles2-mesa-dev libegl1-mesa-dev libibus-1.0-dev fcitx-libs-dev libsamplerate0-dev libsndio-dev libwayland-dev libxkbcommon-dev libdrm-dev libgbm-dev + - run: python3 build.py --dependencies + - run: python3 build.py --configure + - run: python3 build.py --build + - run: python3 build.py --package + - uses: actions/upload-artifact@v2 + with: + name: ${{ env.GAME_ARTIFACT }} + path: dist/${{ env.GAME_ARTIFACT }} + + build-windows: + runs-on: windows-2022 + steps: + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Get artifact name + run: | + echo ("GAME_ARTIFACT=" + (python3 build.py --print-artifact-name)) >> $env:GITHUB_ENV + echo $env:GAME_ARTIFACT + - run: python3 build.py --dependencies + - run: python3 build.py --configure -G 'Visual Studio 17 2022' + - run: python3 build.py --build + - run: python3 build.py --package + - uses: actions/upload-artifact@v2 + with: + name: ${{ env.GAME_ARTIFACT }} + path: dist/${{ env.GAME_ARTIFACT }} + + build-macos: + runs-on: macos-11 + env: + CODE_SIGN_IDENTITY: ${{ secrets.APPLE_CODE_SIGN_IDENTITY }} + steps: + - uses: apple-actions/import-codesign-certs@v1 + with: + p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }} + p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }} + - name: Install gon for notarization + run: | + brew tap mitchellh/gon + brew install mitchellh/gon/gon + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Get artifact name + run: | + echo "GAME_ARTIFACT=$(python3 build.py --print-artifact-name)" >> $GITHUB_ENV + echo $GAME_ARTIFACT + - run: python3 build.py --dependencies + - run: python3 build.py --configure + - run: python3 build.py --build + - run: python3 build.py --package + - name: Notarize + run: gon packaging/gon-config.json + env: + AC_USERNAME: ${{ secrets.APPLE_NOTARIZATION_USERNAME }} + AC_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_PASSWORD }} + - uses: actions/upload-artifact@v2 + with: + name: ${{ env.GAME_ARTIFACT }} + path: dist/${{ env.GAME_ARTIFACT }} diff --git a/.gitignore b/.gitignore index f34f911..35470ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ -cmake-build-* - +build-*/ +cache/ +cmake-build-*/ +dist/ +extern/ diff --git a/CMakeLists.txt b/CMakeLists.txt index af729c2..423fd47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,235 @@ -cmake_minimum_required(VERSION 3.16) -project(CandyCrisis) +cmake_minimum_required(VERSION 3.13) -set(CMAKE_CXX_STANDARD 17) +set(GAME_TARGET "CandyCrisis") +set(GAME_VERSION "3.0.0") +set(GAME_MAC_ICNS "packaging/${GAME_TARGET}.icns") +set(GAME_MAC_COPYRIGHT "https://github.com/jorio/candycrisis") +set(GAME_MAC_BUNDLE_ID "io.jor.candycrisis") -find_package(SDL2 REQUIRED) -include_directories(${SDL2_INCLUDE_DIRS}) +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +set(CMAKE_C_STANDARD 17) +set(CMAKE_CXX_STANDARD 17) -file(GLOB_RECURSE GAME_SOURCES CONFIGURE_DEPENDS src/*.cpp src/*.c src/*.h) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "Minimum OS X deployment version") +set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "Target macOS architectures") -add_executable(CandyCrisis ${GAME_SOURCES}) +project(${GAME_TARGET} + VERSION ${GAME_VERSION} + LANGUAGES C CXX +) -target_link_libraries(CandyCrisis ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} SDL2) +#------------------------------------------------------------------------------ +# GLOBAL OPTIONS (BEFORE ADDING SUBDIRECTORIES) +#------------------------------------------------------------------------------ + +if(MSVC) + add_compile_definitions(UNICODE _UNICODE) + + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${GAME_TARGET}) +endif() + +#------------------------------------------------------------------------------ +# DEPENDENCIES +#------------------------------------------------------------------------------ + +find_package(SDL2 REQUIRED COMPONENTS main) + +#------------------------------------------------------------------------------ +# GAME SOURCES +#------------------------------------------------------------------------------ + +set(GAME_SRCDIR ${CMAKE_CURRENT_SOURCE_DIR}/src) + +# Write header file containing version info +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.h.in ${GAME_SRCDIR}/version.h) + +file(GLOB_RECURSE GAME_SOURCES CONFIGURE_DEPENDS ${GAME_SRCDIR}/*.c ${GAME_SRCDIR}/*.cpp) + +file(GLOB_RECURSE GAME_HEADERS CONFIGURE_DEPENDS ${GAME_SRCDIR}/*.h) + +set(GAME_ALL_SOURCES + ${GAME_SOURCES} + ${GAME_HEADERS} +) + +#------------------------------------------------------------------------------ +# SOURCE GROUPS +#------------------------------------------------------------------------------ + +source_group(TREE ${GAME_SRCDIR} PREFIX "" FILES ${GAME_ALL_SOURCES}) + +#------------------------------------------------------------------------------ +# EXECUTABLE TARGET +#------------------------------------------------------------------------------ + +set(GAME_LIBRARIES ${SDL2_LIBRARIES}) + +if(WIN32) + # "WIN32" here is equivalent to /SUBSYSTEM:WINDOWS for MSVC + add_executable(${GAME_TARGET} WIN32 + ${GAME_ALL_SOURCES} + "${CMAKE_CURRENT_SOURCE_DIR}/packaging/${GAME_TARGET}.exe.rc" + ) +elseif(APPLE) + add_executable(${GAME_TARGET} MACOSX_BUNDLE + ${GAME_ALL_SOURCES} + ${GAME_MAC_ICNS} + ${SDL2_LIBRARIES} + ) +else() + # Math lib, explicitly required on some Linux systems + list(APPEND GAME_LIBRARIES m) + + add_executable(${GAME_TARGET} ${GAME_ALL_SOURCES}) +endif() + +target_include_directories(${GAME_TARGET} PRIVATE + ${SDL2_INCLUDE_DIRS} + ${GAME_SRCDIR} +) + +target_link_libraries(${GAME_TARGET} ${GAME_LIBRARIES}) + +#------------------------------------------------------------------------------ +# DEFINES +#------------------------------------------------------------------------------ + +add_compile_definitions( + "$<$:_DEBUG>" +) + +if(WIN32) + target_compile_definitions(${GAME_TARGET} PRIVATE + WIN32_LEAN_AND_MEAN + _CRT_SECURE_NO_WARNINGS # quit whining about snprintf_s + ) +endif() + +#------------------------------------------------------------------------------ +# COMPILER OPTIONS +#------------------------------------------------------------------------------ + +if(NOT MSVC) + target_compile_options(${GAME_TARGET} PRIVATE + -Wall + #-Wextra # TODO + #-Wshadow # TODO + -Wno-sign-compare # TODO + -Wno-multichar + -Wno-unknown-pragmas + -Werror=return-type + ) +else() + # By default, MSVC may add /EHsc to CMAKE_CXX_FLAGS, which we don't want (we use /EHs below) + string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + + # By default, MSVC may add /W3 to CMAKE_CXX_FLAGS, which we don't want (we use /W4 below) + # Note that this is not required with "cmake_minimum_required(VERSION 3.15)" or later + string(REPLACE "/W3" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + string(REPLACE "/W3" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) + + target_compile_options(${GAME_TARGET} PRIVATE + /EHs # synchronous exceptions; also, extern "C" functions may throw exceptions + /W4 + /wd4068 # ignore unrecognized pragmas + /wd4100 # unreferenced formal parameters + /wd4201 # nonstandard extension (nameless struct) + /wd4244 # conversion from double to float + /wd4305 # truncation from double to float + /wd5105 # see https://developercommunity.visualstudio.com/t/1249671 + ) +endif() + +#------------------------------------------------------------------------------ +# PLATFORM-SPECIFIC PACKAGING +#------------------------------------------------------------------------------ + +set_target_properties(${GAME_TARGET} PROPERTIES + #-------------------------------------------------------------------------- + # MSVC/WIN32 + #-------------------------------------------------------------------------- + + VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + VS_DPI_AWARE "PerMonitor" + + #-------------------------------------------------------------------------- + # APPLE + #-------------------------------------------------------------------------- + + # Set framework search path to (App bundle)/Contents/Frameworks so the game can use its embedded SDL2.framework + XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks" + + # Explicitly turn off code signing, otherwise downloaded app will be quarantined forever + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "" + + # Set up Info.plist values + MACOSX_BUNDLE_ICON_FILE "${GAME_TARGET}.icns" # CFBundleIconFile + MACOSX_BUNDLE_EXECUTABLE_NAME ${GAME_TARGET} # CFBundleExecutable - executable name inside the bundle + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION} # CFBundleShortVersionString + MACOSX_BUNDLE_COPYRIGHT ${GAME_MAC_COPYRIGHT} # NSHumanReadableCopyright (supersedes CFBundleGetInfoString (MACOSX_BUNDLE_INFO_STRING)) + MACOSX_BUNDLE_BUNDLE_NAME ${GAME_TARGET} # CFBundleName - user-visible (where??) short name for the bundle, up to 15 characters + MACOSX_BUNDLE_GUI_IDENTIFIER ${GAME_MAC_BUNDLE_ID} # CFBundleIdentifier - unique bundle ID in reverse-DNS format + + # Bundle ID required for code signing - must match CFBundleIdentifier otherwise xcode will complain + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER ${GAME_MAC_BUNDLE_ID} + + # Don't bother with universal builds when we're working on the debug version + XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] "YES" + + XCODE_EMBED_FRAMEWORKS "${SDL2_LIBRARIES}" + XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "YES" # frameworks must be signed by the same developer as the binary + XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES" # not strictly necessary, but that's cleaner + XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME "YES" # required for notarization to pass +) + +if(APPLE) + # If we have a code signing identity (CODE_SIGN_IDENTITY environment variable), + # set up the release build for proper code signing + if(NOT "$ENV{CODE_SIGN_IDENTITY}" STREQUAL "") + set_target_properties(${GAME_TARGET} PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Release] "$ENV{CODE_SIGN_IDENTITY}" + XCODE_ATTRIBUTE_DEVELOPMENT_TEAM[variant=Release] "$ENV{CODE_SIGN_IDENTITY}" + + # The following is to pass notarization requirements + XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS[variant=Release] "NO" + XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=Release] "--options=runtime --timestamp" + ) + message("Release build will be code signed!") + endif() + + # Copy stuff to app bundle contents + set_source_files_properties(${GAME_MAC_ICNS} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + #set_source_files_properties(${SDL2_LIBRARIES} PROPERTIES MACOSX_PACKAGE_LOCATION "Frameworks") + + set(BUNDLE_CONTENTS_DIR "$/..") + set(APP_PARENT_DIR "${BUNDLE_CONTENTS_DIR}/../..") + + add_custom_command(TARGET ${GAME_TARGET} POST_BUILD + # Copy assets to app bundle + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/CandyCrisisResources ${BUNDLE_CONTENTS_DIR}/Resources + + # High-DPI support in Info.plist +# plutil -replace NSHighResolutionCapable -bool true ${BUNDLE_CONTENTS_DIR}/Info.plist + ) +else() + set(APP_PARENT_DIR "$") + + # Copy assets besides executable + add_custom_command(TARGET ${GAME_TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/CandyCrisisResources ${APP_PARENT_DIR}/CandyCrisisResources) +endif() + +# Windows-specific libraries +if(WIN32) + # Copy SDL2 DLLs to output folder on Windows for convenience + foreach(DLL ${SDL2_DLLS}) + add_custom_command(TARGET ${GAME_TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DLL} ${APP_PARENT_DIR}) + endforeach() + + # When installing (cmake --install), copy Visual Studio redistributable DLLs to install location + include(InstallRequiredSystemLibraries) +endif() + +# Copy documentation to output folder +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/packaging/ReadMe.txt.in ${CMAKE_CURRENT_BINARY_DIR}/ReadMe.txt) diff --git a/build.py b/build.py new file mode 100644 index 0000000..39a28d0 --- /dev/null +++ b/build.py @@ -0,0 +1,454 @@ +#!/usr/bin/env python3 + +import argparse +import contextlib +import hashlib +import glob +import multiprocessing +import os +import os.path +import platform +import shutil +import stat +import subprocess +import sys +import tempfile +import urllib.request +import zipfile +from pathlib import Path + +#---------------------------------------------------------------- + +libs_dir = os.path.abspath("extern") +cache_dir = os.path.abspath("cache") +dist_dir = os.path.abspath("dist") + +game_name = "CandyCrisis" # no spaces +game_name_human = "Candy Crisis" # spaces and other special characters allowed +game_ver = "3.0.0" + +sdl_ver = "2.0.20" +appimagetool_ver = "13" + +lib_hashes = { # sha-256 + "SDL2-2.0.20.tar.gz": "c56aba1d7b5b0e7e999e4a7698c70b63a3394ff9704b5f6e1c57e0c16f04dd06", + "SDL2-2.0.20.dmg": "e46a3694f5008c4c5ffd33e1dfdffbee64179ad15088781f2f70806dd0102d4d", + "SDL2-devel-2.0.20-VC.zip": "5b1512ca6c9d2427bd2147da01e5e954241f8231df12f54a7074dccde416df18", + "appimagetool-x86_64.AppImage": "df3baf5ca5facbecfc2f3fa6713c29ab9cefa8fd8c1eac5d283b79cab33e4acb", # appimagetool v13 +} + +NPROC = multiprocessing.cpu_count() +SYSTEM = platform.system() + +if SYSTEM == "Windows": + os.system("") # hack to get ANSI color escapes to work + +#---------------------------------------------------------------- + +parser = argparse.ArgumentParser(description=F"Configure, build, and package {game_name_human}") + +if SYSTEM == "Darwin": + default_generator = "Xcode" + default_architecture = None + help_configure = "generate Xcode project" + help_build = "build app from Xcode project" + help_package = "package up the game into a DMG" +elif SYSTEM == "Windows": + default_generator = "Visual Studio 17 2022" + default_architecture = "x64" + help_configure = F"generate {default_generator} solution" + help_build = F"build exe from {default_generator} solution" + help_package = "package up the game into a ZIP" +else: + default_generator = None + default_architecture = None + help_configure = "generate project" + help_build = "build binary" + help_package = "package up the game into an AppImage" + +parser.add_argument("--dependencies", default=False, action="store_true", help="fetch and set up dependencies (SDL)") +parser.add_argument("--configure", default=False, action="store_true", help=help_configure) +parser.add_argument("--build", default=False, action="store_true", help=help_build) +parser.add_argument("--package", default=False, action="store_true", help=help_package) + +parser.add_argument("-G", metavar="", default=default_generator, + help=F"custom project generator for the CMake configure step (default: {default_generator})") + +parser.add_argument("-A", metavar="", default=default_architecture, + help=F"custom platform name for the CMake configure step (default: {default_architecture})") + +parser.add_argument("--print-artifact-name", default=False, action="store_true", + help="print artifact name and exit") + +if SYSTEM == "Linux": + parser.add_argument("--system-sdl", default=False, action="store_true", + help="use system SDL instead of building SDL from scratch") + +args = parser.parse_args() + +#---------------------------------------------------------------- + +class Project: + def __init__(self, dir_name, gen_args=[], gen_env={}, build_configs=[], build_args=[]): + self.dir_name = dir_name + self.gen_args = gen_args + self.gen_env = gen_env + self.build_configs = build_configs + self.build_args = build_args + +#---------------------------------------------------------------- + +@contextlib.contextmanager +def chdir(path): + origin = os.getcwd() + try: + os.chdir(path) + yield + finally: + os.chdir(origin) + +def die(message): + print(F"\x1b[1;31m{message}\x1b[0m", file=sys.stderr) + sys.exit(1) + +def log(message): + print(message, file=sys.stderr) + +def fatlog(message): + starbar = len(message) * '*' + print(F"\n{starbar}\n{message}\n{starbar}", file=sys.stderr) + +def hash_file(path): + hasher = hashlib.sha256() + with open(path, 'rb') as f: + while True: + chunk = f.read(64*1024) + if not chunk: + break + hasher.update(chunk) + return hasher.hexdigest() + +def get_package(url): + name = url[url.rfind('/')+1:] + + if name in lib_hashes: + reference_hash = lib_hashes[name] + else: + die(F"Build script lacks reference checksum for {name}") + + path = os.path.normpath(F"{cache_dir}/{name}") + if os.path.exists(path): + log(F"Not redownloading: {path}") + else: + log(F"Downloading: {url}") + os.makedirs(cache_dir, exist_ok=True) + urllib.request.urlretrieve(url, path) + + actual_hash = hash_file(path) + if reference_hash != actual_hash: + die(F"Bad checksum for {name}: expected {reference_hash}, got {actual_hash}") + + return path + +def call(cmd, **kwargs): + cmdstr = "" + for token in cmd: + cmdstr += " " + if " " in token: + cmdstr += F"\"{token}\"" + else: + cmdstr += token + + log(F">{cmdstr}") + try: + return subprocess.run(cmd, check=True, **kwargs) + except subprocess.CalledProcessError as e: + die(F"Aborting setup because: {e}") + +def rmtree_if_exists(path): + if os.path.exists(path): + shutil.rmtree(path) + +def zipdir(zipname, topleveldir, arc_topleveldir): + with zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zipf: + for root, dirs, files in os.walk(topleveldir): + for file in files: + filepath = os.path.join(root, file) + arcpath = os.path.join(arc_topleveldir, filepath[len(topleveldir)+1:]) + log(F"Zipping: {filepath} --> {arcpath}") + zipf.write(filepath, arcpath) + +#---------------------------------------------------------------- + +def prepare_dependencies_windows(): + rmtree_if_exists(F"{libs_dir}/SDL2-{sdl_ver}") + + sdl_zip_path = get_package(F"http://libsdl.org/release/SDL2-devel-{sdl_ver}-VC.zip") + shutil.unpack_archive(sdl_zip_path, libs_dir) + +def prepare_dependencies_macos(): + sdl2_framework = "SDL2.framework" + sdl2_framework_target_path = F"{libs_dir}/{sdl2_framework}" + + rmtree_if_exists(sdl2_framework_target_path) + + sdl_dmg_path = get_package(F"http://libsdl.org/release/SDL2-{sdl_ver}.dmg") + + # Mount the DMG and copy SDL2.framework to extern/ + with tempfile.TemporaryDirectory() as mount_point: + call(["hdiutil", "attach", sdl_dmg_path, "-mountpoint", mount_point, "-quiet"]) + shutil.copytree(F"{mount_point}/{sdl2_framework}", sdl2_framework_target_path, symlinks=True) + call(["hdiutil", "detach", mount_point, "-quiet"]) + + if "CODE_SIGN_IDENTITY" in os.environ: + call(["codesign", "--force", "--timestamp", "--sign", os.environ["CODE_SIGN_IDENTITY"], sdl2_framework_target_path]) + else: + print("SDL will not be codesigned. Set the CODE_SIGN_IDENTITY environment variable if you want to sign it.") + +def prepare_dependencies_linux(): + if not args.system_sdl: + sdl_source_dir = F"{libs_dir}/SDL2-{sdl_ver}" + sdl_build_dir = F"{sdl_source_dir}/build" + rmtree_if_exists(sdl_source_dir) + + sdl_zip_path = get_package(F"http://libsdl.org/release/SDL2-{sdl_ver}.tar.gz") + shutil.unpack_archive(sdl_zip_path, libs_dir) + + with chdir(sdl_source_dir): + call([F"{sdl_source_dir}/configure", F"--prefix={sdl_build_dir}", "--quiet"]) + call(["make", "-j", str(NPROC)], stdout=subprocess.DEVNULL) + call(["make", "install", "--silent"]) # install to configured prefix (sdl_build_dir) + +#---------------------------------------------------------------- + +def get_artifact_name(): + if SYSTEM == "Windows": + return F"{game_name}-{game_ver}-windows-x64.zip" + elif SYSTEM == "Darwin": + return F"{game_name}-{game_ver}-mac.dmg" + elif SYSTEM == "Linux": + return F"{game_name}-{game_ver}-linux-x86_64.AppImage" + else: + die("Unknown system for print_artifact_name") + +def copy_documentation(proj, appdir, full=True): + #shutil.copy(F"{proj.dir_name}/ReadMe.txt", F"{appdir}") + #shutil.copy(F"LICENSE.md", F"{appdir}/License.txt") + + if full: + pass + #shutil.copytree("docs", F"{appdir}/Documentation") + # os.remove(F"{appdir}/Documentation/logo.png") + #os.remove(F"{appdir}/Documentation/screenshot.png") + #os.remove(F"{appdir}/Documentation/screenshot2.png") + #for docfile in ["CHANGELOG.md"]: + # shutil.copy(docfile, F"{appdir}/Documentation") + +def package_windows(proj): + windows_dlls = ["SDL2.dll", "msvcp140.dll", "vcruntime140.dll", "vcruntime140_1.dll"] # C++ + + # Prep DLLs with cmake (copied to {cache_dir}/install/bin) + call(["cmake", "--install", proj.dir_name, "--prefix", F"{cache_dir}/install"]) + + appdir = F"{cache_dir}/{game_name}-{game_ver}" + rmtree_if_exists(appdir) + os.makedirs(F"{appdir}", exist_ok=True) + + # Copy executable, libs and assets + for dll in windows_dlls: + shutil.copy(F"{cache_dir}/install/bin/{dll}", appdir) + shutil.copy(F"{proj.dir_name}/Release/{game_name}.exe", appdir) + shutil.copytree("CandyCrisisResources", F"{appdir}/CandyCrisisResources") + + copy_documentation(proj, appdir) + + zipdir(F"{dist_dir}/{get_artifact_name()}", appdir, F"{game_name}-{game_ver}") + +def package_macos(proj): + appdir = F"{proj.dir_name}/Release" + + # Human-friendly name for .app + os.rename(F"{appdir}/{game_name}.app", F"{appdir}/{game_name_human}.app") + + copy_documentation(proj, appdir) + + #shutil.copy("packaging/dmg_DS_Store", F"{appdir}/.DS_Store") + + call(["hdiutil", "create", + "-fs", "HFS+", + "-srcfolder", appdir, + "-volname", F"{game_name_human} {game_ver}", + F"{dist_dir}/{get_artifact_name()}"]) + +def package_linux(proj): + appimagetool_path = get_package(F"https://github.com/AppImage/AppImageKit/releases/download/{appimagetool_ver}/appimagetool-x86_64.AppImage") + os.chmod(appimagetool_path, 0o755) + + appdir = F"{cache_dir}/{game_name}-{game_ver}.AppDir" + rmtree_if_exists(appdir) + + os.makedirs(F"{appdir}", exist_ok=True) + os.makedirs(F"{appdir}/usr/bin", exist_ok=True) + os.makedirs(F"{appdir}/usr/lib", exist_ok=True) + + # Copy executable and assets + shutil.copy(F"{proj.dir_name}/{game_name}", F"{appdir}/usr/bin") # executable + shutil.copytree("CandyCrisisResources", F"{appdir}/CandyCrisisResources") + copy_documentation(proj, appdir, full=False) + + # Copy XDG stuff + shutil.copy(F"packaging/{game_name.lower()}.desktop", appdir) + shutil.copy(F"packaging/{game_name.lower()}-desktopicon.png", appdir) + + # Copy AppImage kicker script + shutil.copy(F"packaging/AppRun", appdir) + os.chmod(F"{appdir}/AppRun", 0o755) + + # Copy SDL (if not using system SDL) + if not args.system_sdl: + for file in glob.glob(F"{libs_dir}/SDL2-{sdl_ver}/build/lib/libSDL2*.so*"): + shutil.copy(file, F"{appdir}/usr/lib", follow_symlinks=False) + + # Invoke appimagetool + call([appimagetool_path, "--no-appstream", appdir, F"{dist_dir}/{get_artifact_name()}"]) + +#---------------------------------------------------------------- + +if args.print_artifact_name: + print(get_artifact_name()) + sys.exit(0) + +fatlog(F"{game_name} {game_ver} build script") + +if not (args.dependencies or args.configure or args.build or args.package): + log("No build steps specified, running all of them.") + args.dependencies = True + args.configure = True + args.build = True + args.package = True + +# Make sure we're running from the correct directory... +if not os.path.exists("src/graymonitor.cpp"): # some file that's likely to be from the game's source tree + die(F"STOP - Please run this script from the root of the {game_name} source repo") + +#---------------------------------------------------------------- +# Set up project metadata + +projects = [] + +common_gen_args = [] +if args.G: + common_gen_args += ["-G", args.G] +if args.A: + common_gen_args += ["-A", args.A] + +if SYSTEM == "Windows": + + projects = [Project( + dir_name="build-msvc", + gen_args=common_gen_args, + build_configs=["Release", "Debug"], + build_args=["-m"] # multiprocessor compilation + )] + +elif SYSTEM == "Darwin": + projects = [Project( + dir_name="build-xcode", + gen_args=common_gen_args, + build_configs=["Release"], + build_args=["-j", str(NPROC)] + )] + +elif SYSTEM == "Linux": + gen_env = {} + if not args.system_sdl: + gen_env["SDL2DIR"] = F"{libs_dir}/SDL2-{sdl_ver}/build" + + projects.append(Project( + dir_name="build-relwithdebinfo", + gen_args=common_gen_args + ["-DCMAKE_BUILD_TYPE=RelWithDebInfo"], + gen_env=gen_env, + build_args=["-j", str(NPROC)] + )) + + projects.append(Project( + dir_name="build-debug", + gen_args=common_gen_args + ["-DCMAKE_BUILD_TYPE=Debug"], + gen_env=gen_env, + build_args=["-j", str(NPROC)] + )) +else: + die(F"Unsupported system for configure step: {SYSTEM}") + + +#---------------------------------------------------------------- +# Prepare dependencies + +if args.dependencies: + fatlog("Setting up dependencies") + + # Check that our submodules are here + #if not os.path.exists("extern/Pomme/CMakeLists.txt"): + # die("Submodules appear to be missing.\n" + # + "Did you clone the submodules recursively? Try this: git submodule update --init --recursive") + + if SYSTEM == "Windows": + prepare_dependencies_windows() + elif SYSTEM == "Darwin": + prepare_dependencies_macos() + elif SYSTEM == "Linux": + prepare_dependencies_linux() + else: + die(F"Unsupported system for dependencies step: {SYSTEM}") + +#---------------------------------------------------------------- +# Configure projects + +if args.configure: + for proj in projects: + fatlog(F"Configuring {proj.dir_name}") + + rmtree_if_exists(proj.dir_name) + + env = None + if proj.gen_env: + env = os.environ.copy() + env.update(proj.gen_env) + + call(["cmake", "-S", ".", "-B", proj.dir_name] + proj.gen_args, env=env) + +#---------------------------------------------------------------- +# Build the game + +proj = projects[0] + +if args.build: + fatlog(F"Building the game: {proj.dir_name}") + + build_command = ["cmake", "--build", proj.dir_name] + + if proj.build_configs: + build_command += ["--config", proj.build_configs[0]] + + if proj.build_args: + build_command += ["--"] + proj.build_args + + call(build_command) + +#---------------------------------------------------------------- +# Package the game + +if args.package: + fatlog(F"Packaging the game") + + rmtree_if_exists(dist_dir) + os.makedirs(dist_dir, exist_ok=True) + + if SYSTEM == "Darwin": + package_macos(proj) + elif SYSTEM == "Windows": + package_windows(proj) + elif SYSTEM == "Linux": + package_linux(proj) + else: + die(F"Unsupported system for package step: {SYSTEM}") diff --git a/cmake/FindSDL2.cmake b/cmake/FindSDL2.cmake new file mode 100644 index 0000000..27e773a --- /dev/null +++ b/cmake/FindSDL2.cmake @@ -0,0 +1,74 @@ +#------------------------------------------------------------------------------ +# Usage: find_package(SDL2 [REQUIRED] [COMPONENTS main]) +# +# Sets variables: +# SDL2_INCLUDE_DIRS +# SDL2_LIBRARIES +# SDL2_DLLS (Windows only) +#------------------------------------------------------------------------------ + +include(FindPackageHandleStandardArgs) + +set(SDL2_VERSION 2.0.20) + +# Check if "main" was specified as a component +set(_SDL2_use_main FALSE) +foreach(_SDL2_component ${SDL2_FIND_COMPONENTS}) + if(_SDL2_component STREQUAL "main") + set(_SDL2_use_main TRUE) + else() + message(WARNING "Unrecognized component \"${_SDL2_component}\"") + endif() +endforeach() + +if(WIN32) + find_path(SDL2_ROOT "include/SDL.h" + PATHS "${CMAKE_SOURCE_DIR}/extern/SDL2-${SDL2_VERSION}" + NO_DEFAULT_PATH + ) + + if(SDL2_ROOT) + set(SDL2_INCLUDE_DIRS "${SDL2_ROOT}/include") + set(_SDL2_ARCH "x64") + set(SDL2_LIBRARIES "${SDL2_ROOT}/lib/${_SDL2_ARCH}/SDL2.lib") + set(SDL2_DLLS "${SDL2_ROOT}/lib/${_SDL2_ARCH}/SDL2.dll") + if(_SDL2_use_main) + list(APPEND SDL2_LIBRARIES "${SDL2_ROOT}/lib/${_SDL2_ARCH}/SDL2main.lib") + endif() + + # When installing, copy DLLs to install location + install(FILES ${SDL2_DLLS} DESTINATION bin) + endif() + + mark_as_advanced(SDL2_ROOT) + find_package_handle_standard_args(SDL2 DEFAULT_MSG SDL2_INCLUDE_DIRS SDL2_LIBRARIES SDL2_DLLS) + +elseif(APPLE) + find_path(SDL2_INCLUDE_DIRS "SDL.h" + PATHS "${CMAKE_SOURCE_DIR}/extern/SDL2.framework/Versions/Current" + PATH_SUFFIXES "Headers" + REQUIRED + NO_DEFAULT_PATH + ) + + set(SDL2_LIBRARIES "${CMAKE_SOURCE_DIR}/extern/SDL2.framework") + + find_package_handle_standard_args(SDL2 DEFAULT_MSG SDL2_INCLUDE_DIRS SDL2_LIBRARIES) + +else() + find_path(SDL2_INCLUDE_DIRS "SDL.h" + HINTS $ENV{SDL2DIR} + PATH_SUFFIXES "include/SDL2" "include" + REQUIRED + ) + + find_library(SDL2_LIBRARIES + NAMES "SDL2" + HINTS $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + REQUIRED + ) + + find_package_handle_standard_args(SDL2 DEFAULT_MSG SDL2_INCLUDE_DIRS SDL2_LIBRARIES) + +endif() diff --git a/cmake/version.h.in b/cmake/version.h.in new file mode 100644 index 0000000..b3c7d05 --- /dev/null +++ b/cmake/version.h.in @@ -0,0 +1,7 @@ +#pragma once + +#define PROJECT_VERSION "@PROJECT_VERSION@" +#define PROJECT_VERSION_MAJOR "@PROJECT_VERSION_MAJOR@" +#define PROJECT_VERSION_MINOR "@PROJECT_VERSION_MINOR@" +#define PROJECT_VERSION_PATCH "@PROJECT_VERSION_PATCH@" + diff --git a/packaging/AppRun b/packaging/AppRun new file mode 100755 index 0000000..3a9836f --- /dev/null +++ b/packaging/AppRun @@ -0,0 +1,13 @@ +#!/bin/sh +SELF=$(readlink -f "$0") +HERE=${SELF%/*} +export PATH="${HERE}/usr/bin/:${HERE}/usr/sbin/:${HERE}/usr/games/:${HERE}/bin/:${HERE}/sbin/${PATH:+:$PATH}" +export LD_LIBRARY_PATH="${HERE}/usr/lib/:${HERE}/usr/lib/i386-linux-gnu/:${HERE}/usr/lib/x86_64-linux-gnu/:${HERE}/usr/lib32/:${HERE}/usr/lib64/:${HERE}/lib/:${HERE}/lib/i386-linux-gnu/:${HERE}/lib/x86_64-linux-gnu/:${HERE}/lib32/:${HERE}/lib64/${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +export PYTHONPATH="${HERE}/usr/share/pyshared/${PYTHONPATH:+:$PYTHONPATH}" +export XDG_DATA_DIRS="${HERE}/usr/share/${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}" +export PERLLIB="${HERE}/usr/share/perl5/:${HERE}/usr/lib/perl5/${PERLLIB:+:$PERLLIB}" +export GSETTINGS_SCHEMA_DIR="${HERE}/usr/share/glib-2.0/schemas/${GSETTINGS_SCHEMA_DIR:+:$GSETTINGS_SCHEMA_DIR}" +export QT_PLUGIN_PATH="${HERE}/usr/lib/qt4/plugins/:${HERE}/usr/lib/i386-linux-gnu/qt4/plugins/:${HERE}/usr/lib/x86_64-linux-gnu/qt4/plugins/:${HERE}/usr/lib32/qt4/plugins/:${HERE}/usr/lib64/qt4/plugins/:${HERE}/usr/lib/qt5/plugins/:${HERE}/usr/lib/i386-linux-gnu/qt5/plugins/:${HERE}/usr/lib/x86_64-linux-gnu/qt5/plugins/:${HERE}/usr/lib32/qt5/plugins/:${HERE}/usr/lib64/qt5/plugins/${QT_PLUGIN_PATH:+:$QT_PLUGIN_PATH}" +EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | head -n 1 | cut -d "=" -f 2 | cut -d " " -f 1) +cd $HERE +exec "${EXEC}" "$@" diff --git a/packaging/CandyCrisis.exe.rc b/packaging/CandyCrisis.exe.rc new file mode 100644 index 0000000..feaa1f6 --- /dev/null +++ b/packaging/CandyCrisis.exe.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "CandyCrisis.ico" diff --git a/packaging/ReadMe.txt.in b/packaging/ReadMe.txt.in new file mode 100644 index 0000000..106a843 --- /dev/null +++ b/packaging/ReadMe.txt.in @@ -0,0 +1,2 @@ +Candy Crisis source port @PROJECT_VERSION@ +https://github.com/jorio/candycrisis diff --git a/packaging/candycrisis-desktopicon.png b/packaging/candycrisis-desktopicon.png new file mode 100644 index 0000000..682ad5b Binary files /dev/null and b/packaging/candycrisis-desktopicon.png differ diff --git a/packaging/candycrisis.desktop b/packaging/candycrisis.desktop new file mode 100644 index 0000000..0544287 --- /dev/null +++ b/packaging/candycrisis.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Version=1.0 +Name=Candy Crisis +Comment=Candy Crisis +Icon=candycrisis-desktopicon +Exec=CandyCrisis +Terminal=false +Categories=Game; diff --git a/packaging/gon-config.json b/packaging/gon-config.json new file mode 100644 index 0000000..37543b5 --- /dev/null +++ b/packaging/gon-config.json @@ -0,0 +1,7 @@ +{ + "notarize": [{ + "path": "./dist/CandyCrisis-3.0.0-mac.dmg", + "bundle_id": "io.jor.candycrisis", + "staple": true + }] +} diff --git a/src/stdafx.h b/src/stdafx.h index c383217..4cc2dcc 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -4,15 +4,13 @@ #pragma once -#include -#include +#include +#include #if _WIN32 #define _CRT_SECURE_NO_WARNINGS 1 -#include "targetver.h" - #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files: #include @@ -23,7 +21,4 @@ #include #include -#include "SDL.h" -#include "SDL_endian.h" - #endif diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..e4386ce --- /dev/null +++ b/src/version.h @@ -0,0 +1,7 @@ +#pragma once + +#define PROJECT_VERSION "3.0.0" +#define PROJECT_VERSION_MAJOR "3" +#define PROJECT_VERSION_MINOR "0" +#define PROJECT_VERSION_PATCH "0" +