diff --git a/.github/actions/msvc-dev-cmd/action.yml b/.github/actions/msvc-dev-cmd/action.yml new file mode 100644 index 00000000..0040998a --- /dev/null +++ b/.github/actions/msvc-dev-cmd/action.yml @@ -0,0 +1,42 @@ +name: Setup MSVC dev environment +description: > + Setups the MSVC toolchain. Based on https://github.com/ilammy/msvc-dev-cmd +inputs: + arch: + description: Target architecture passed to vcvarsall.bat + required: false + default: x64 +runs: + using: composite + steps: + - name: Setup Environment + shell: pwsh + run: | + $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe" + $installPath = & $vswhere -latest -products * ` + -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 ` + -property installationPath + if (-not $installPath) { + throw "vswhere could not find a Visual Studio install with the VC++ toolset" + } + + $vcvarsall = Join-Path $installPath "VC\Auxiliary\Build\vcvarsall.bat" + if (-not (Test-Path $vcvarsall)) { + throw "vcvarsall.bat not found at $vcvarsall" + } + + $before = @{} + Get-ChildItem Env: | ForEach-Object { $before[$_.Name] = $_.Value } + + $output = & cmd.exe /c "`"$vcvarsall`" ${{ inputs.arch }} && set" + if ($LASTEXITCODE -ne 0) { + throw "vcvarsall.bat ${{ inputs.arch }} failed with exit code $LASTEXITCODE" + } + + foreach ($line in $output) { + if ($line -notmatch '^([^=]+)=(.*)$') { continue } + $name = $Matches[1] + $value = $Matches[2] + if ($before[$name] -eq $value) { continue } + Add-Content -Path $env:GITHUB_ENV -Value "$name=$value" + } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 73e66cbf..14d52ed4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,6 +59,11 @@ jobs: with: cmake-version: "3.22.x" + # use MSVC instead of MinGW + - name: Setup MSVC dev environment + if: ${{ matrix.os == 'windows-latest' }} + uses: ./.github/actions/msvc-dev-cmd + # Create a build directory to store all the CMake generated files - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build @@ -194,6 +199,161 @@ jobs: name: ${{ matrix.artifact }}-www path: proofing/ink-proof/out + verify-install: + name: Verify Install (${{ matrix.artifact }}, ${{ matrix.compiler }}) + needs: compilation + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + include: + - os: "ubuntu-22.04" + artifact: linux + compiler: gcc + cc: gcc + cxx: g++ + exe_ext: "" + - os: "ubuntu-22.04" + artifact: linux + compiler: clang + cc: clang + cxx: clang++ + exe_ext: "" + - os: macos-latest + artifact: macos + compiler: clang + cc: clang + cxx: clang++ + exe_ext: "" + - os: macos-latest + artifact: macos + compiler: gcc + cc: gcc-14 + cxx: g++-14 + exe_ext: "" + - os: macos-14 + artifact: macos-arm + compiler: clang + cc: clang + cxx: clang++ + exe_ext: "" + - os: macos-14 + artifact: macos-arm + compiler: gcc + cc: gcc-14 + cxx: g++-14 + exe_ext: "" + - os: windows-latest + artifact: win64 + compiler: msvc + cc: "" + cxx: "" + exe_ext: ".exe" + - os: windows-latest + artifact: win64 + compiler: clang + cc: clang + cxx: clang++ + exe_ext: ".exe" + steps: + - uses: actions/checkout@v6 + + - name: Download Lib artifact + uses: actions/download-artifact@v8 + with: + name: ${{ matrix.artifact }}-lib + path: install/lib + - name: Download CLib artifact + uses: actions/download-artifact@v8 + with: + name: ${{ matrix.artifact }}-clib + path: install/clib + - name: Download Cl artifact + uses: actions/download-artifact@v8 + with: + name: ${{ matrix.artifact }}-cl + path: install/cl + + - name: Make inkcpp_cl executable + shell: bash + run: chmod +x install/cl/inkcpp_cl${{ matrix.exe_ext }} + + - name: Setup MSVC dev environment + if: ${{ matrix.os == 'windows-latest' }} + uses: ./.github/actions/msvc-dev-cmd + + - name: Install pkg-config + if: ${{ matrix.os == 'windows-latest' && matrix.compiler == 'clang' }} + run: | + winget install pkgconf.pkgconf --source winget --accept-source-agreements --accept-package-agreements --disable-interactivity + $pkgconf = (Get-ChildItem -Path "C:\Program Files", "C:\Program Files (x86)" -Filter pkgconf.exe -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1).FullName + if (-not (Test-Path $pkgconf)) { throw "pkgconf.exe not found after winget install" } + + $bin = Split-Path $pkgconf -Parent + $alias = Join-Path $bin "pkg-config.exe" + if (-not (Test-Path $alias)) { Copy-Item $pkgconf $alias } + Add-Content -Path $env:GITHUB_PATH -Value $bin + + - name: Precompile test story + shell: bash + run: >- + install/cl/inkcpp_cl${{ matrix.exe_ext }} + -o Documentation/cmake_example/test.bin + Documentation/cmake_example/test.ink.json + + - name: CMake / lib + shell: bash + # the "lib" package is built with AppleClang/libc++ on macOS; GCC cannot link a C++ + # translation unit against that archive, so skip main_cpp for that combination + run: | + set -e + if [ -n "${{ matrix.cc }}" ]; then + export CC="${{ matrix.cc }}" + export CXX="${{ matrix.cxx }}" + fi + skip_cpp=OFF + if [[ "${{ matrix.os }}" == macos* && "${{ matrix.compiler }}" == "gcc" ]]; then + skip_cpp=ON + fi + cmake -S Documentation/cmake_example -B build-lib -GNinja \ + -DCMAKE_BUILD_TYPE="${{ env.BUILD_TYPE }}" \ + -DCMAKE_PREFIX_PATH="${{ github.workspace }}/install/lib" \ + -DINKCPP_EXAMPLE_SKIP_CPP="$skip_cpp" + cmake --build build-lib + cd Documentation/cmake_example + if [ "$skip_cpp" != "ON" ]; then + echo 0 | "${{ github.workspace }}/build-lib/main_cpp${{ matrix.exe_ext }}" + fi + echo 0 | "${{ github.workspace }}/build-lib/main_c${{ matrix.exe_ext }}" + + - name: CMake / clib + shell: bash + run: | + set -e + if [ -n "${{ matrix.cc }}" ]; then + export CC="${{ matrix.cc }}" + export CXX="${{ matrix.cxx }}" + fi + cmake -S Documentation/cmake_example -B build-clib -GNinja \ + -DCMAKE_BUILD_TYPE="${{ env.BUILD_TYPE }}" \ + -DCMAKE_PREFIX_PATH="${{ github.workspace }}/install/clib" + cmake --build build-clib + cd Documentation/cmake_example + echo 0 | "${{ github.workspace }}/build-clib/main_c${{ matrix.exe_ext }}" + + # cl.exe ignores pkg-configs linkage flags + - name: pkg-config / clib + if: ${{ matrix.compiler != 'msvc' }} + shell: bash + run: | + set -e + export CC="${{ matrix.cc }}" + export PKG_CONFIG_PATH="${{ github.workspace }}/install/clib/lib/pkgconfig" + pkg-config --define-prefix --cflags --libs inkcpp + cd Documentation/cmake_example + $CC main.c -o main_pkgconfig${{ matrix.exe_ext }} $(pkg-config --define-prefix --cflags --libs inkcpp) + echo 0 | ./main_pkgconfig${{ matrix.exe_ext }} + build-doc: name: Build Doxygen documentation needs: [compilation, build-python] @@ -271,7 +431,7 @@ jobs: - name: Build python release run: python3 -m build - - uses: suisei-cn/actions-download-file@v1.6.0 + - uses: suisei-cn/actions-download-file@v1.6.1 name: Download Inklecate id: download_inklecate with: @@ -281,6 +441,7 @@ jobs: shell: bash run: | cd inklecate + echo "26f4e188e02536d6e99e73e71d9b13e2c2144187f1368a87e82fd5066176cff8 *$(ls *.zip)" | sha256sum -c - unzip *.zip echo "INKLECATE=${{ matrix.inklecate_pre }}$GITHUB_WORKSPACE/inklecate/inklecate${{ matrix.inklecate_post }}" >> $GITHUB_ENV diff --git a/Documentation/cmake_example/CMakeLists.txt b/Documentation/cmake_example/CMakeLists.txt index 1271d230..ce62b17c 100644 --- a/Documentation/cmake_example/CMakeLists.txt +++ b/Documentation/cmake_example/CMakeLists.txt @@ -1,13 +1,25 @@ cmake_minimum_required(VERSION 3.16) -project(main) +project(main C CXX) find_package(inkcpp CONFIG REQUIRED) -# for CXX builds -add_executable(main_cpp main.cpp) -set_property(TARGET main_cpp PROPERTY CXX_STANDARD 17) -target_link_libraries(main_cpp inkcpp inkcpp_compiler) +# Set when linking a libc++-built "lib" package against a compiler using a different C++ ABI (e.g. +# GCC on macOS), where the C++ example can never link. +option(INKCPP_EXAMPLE_SKIP_CPP "Skip building the C++ example (main_cpp)" OFF) -# for C builds -# add_executable(main_c main.c) -# target_link_libraries(main_c inkcpp_c) +# for CXX builds - only available when the "lib" package was installed +if(NOT INKCPP_EXAMPLE_SKIP_CPP + AND TARGET inkcpp + AND TARGET inkcpp_compiler) + add_executable(main_cpp main.cpp) + set_property(TARGET main_cpp PROPERTY CXX_STANDARD 17) + target_link_libraries(main_cpp PRIVATE inkcpp inkcpp_compiler) +endif() + +# for C builds - available from both the "lib" and "clib" packages +if(TARGET inkcpp_c) + add_executable(main_c main.c) + set_property(TARGET main_c PROPERTY C_STANDARD 17) + set_property(TARGET main_c PROPERTY C_STANDARD_REQUIRED ON) + target_link_libraries(main_c PRIVATE inkcpp_c) +endif() diff --git a/Documentation/cmake_example/main.c b/Documentation/cmake_example/main.c index d2a76bd9..8a94e420 100644 --- a/Documentation/cmake_example/main.c +++ b/Documentation/cmake_example/main.c @@ -2,9 +2,11 @@ #include #include -#include // if -lib.zip was used for the installation - -// #include // if -clib.zip was used for the installation +#if __has_include() +# include // -lib.zip layout +#else +# include // -clib.zip layout +#endif InkValue ink_add(int argc, const InkValue argv[]) { @@ -15,7 +17,8 @@ InkValue ink_add(int argc, const InkValue argv[]) int main() { - ink_compile_json("test.ink.json", "test.bin", NULL); + // test.bin is precompiled ahead of time (e.g. via inkcpp_cl) since the clib + // package does not include the compiler HInkStory* story = ink_story_from_file("test.bin"); HInkRunner* runner = ink_story_new_runner(story, NULL); @@ -32,7 +35,9 @@ int main() } int id; - scanf("%i", &id); + while (scanf("%i", &id) != 1) { + printf("Failed to parse input!, try again\n"); + } ink_runner_choose(runner, id); } } diff --git a/Documentation/cmake_example/main.cpp b/Documentation/cmake_example/main.cpp index 4ab30388..cf8ceebf 100644 --- a/Documentation/cmake_example/main.cpp +++ b/Documentation/cmake_example/main.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include using namespace ink::runtime; diff --git a/inkcpp_c/CMakeLists.txt b/inkcpp_c/CMakeLists.txt index 0d3c0704..885346dc 100644 --- a/inkcpp_c/CMakeLists.txt +++ b/inkcpp_c/CMakeLists.txt @@ -54,13 +54,26 @@ install( COMPONENT clib EXCLUDE_FROM_ALL) -# configure in two steps to get the current installation prefix -set(PREFIX "@PREFIX@") -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/inkcpp_c.pc.in ${CMAKE_BINARY_DIR}/inkcpp_c.pc.in @ONLY) +set(INKCPP_C_PC_EXTRA_LIBS "") +foreach(lib IN LISTS CMAKE_CXX_IMPLICIT_LINK_LIBRARIES) + if(IS_ABSOLUTE "${lib}") + string(APPEND INKCPP_C_PC_EXTRA_LIBS " ${lib}") + else() + string(APPEND INKCPP_C_PC_EXTRA_LIBS " -l${lib}") + endif() +endforeach() + +set(INKCPP_C_PC_EXTRA_CFLAGS "") +if(MSVC) + # this archive is built with MSVC's default dynamic CRT (/MD in Release). + set(INKCPP_C_PC_EXTRA_CFLAGS "-fms-runtime-lib=dll -lmsvcrt -lvcruntime -lucrt -loldnames") +endif() + +set(PREFIX "/user/local") +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/inkcpp_c.pc.in ${CMAKE_CURRENT_BINARY_DIR}/inkcpp.pc + @ONLY) install( - CODE [[ - get_filename_component(PREFIX ${CMAKE_INSTALL_PREFIX} ABSOLUTE) - configure_file(inkcpp_c.pc.in ${PREFIX}/lib/pkgconfig/inkcpp.pc @ONLY) - ]] + FILES ${CMAKE_CURRENT_BINARY_DIR}/inkcpp.pc + DESTINATION lib/pkgconfig COMPONENT clib EXCLUDE_FROM_ALL) diff --git a/inkcpp_c/include/inkcpp.h b/inkcpp_c/include/inkcpp.h index 035cbae5..88bb60a7 100644 --- a/inkcpp_c/include/inkcpp.h +++ b/inkcpp_c/include/inkcpp.h @@ -47,13 +47,13 @@ typedef struct HInkSTory HInkStory; * page](https://github.com/JBenda/inkcpp/releases/latest) to `/MY/INKCPP/EXAMPLE_INSTALL/PATH`. *
To run the example do the following: * - * + change the `prefix=...` in `/MY/INKCPP/EXAMPLE_INSTALL/PATH/lib/pkgconfig/inkcpp.pc` - * to `prefix=/MY/INKCPP_EXAMPLE_INSTALL_PATH/` * + `export PKG_CONFIG_PATH=/MY/INKCPP/EXAMPLE_INSTALL/PATH/lib/pkgconfig` - * + `gcc -c main.c $(pkg-config --cflags inkcpp)` - * + `g++ -o main main.o $(pkg-config --libs inkcpp)` + * + `clang main.c -o main $(pkg-config --define-prefix --cflags --libs inkcpp)` * + `./main` * + * @note `clang` is used here since, unlike `gcc`, it is readily available on Windows, macOS and + * Linux alike. + * * As a sideproduct a file named `test.bin` should be created coaining the binary format used by * inkCPP. * @@ -456,18 +456,6 @@ typedef struct HInkSTory HInkStory; HInkStory* self, const HInkSnapshot* obj, HInkGlobals* store, int runner_id ); - /** - * @ingroup clib - * Compiles a .ink.json file to an inkCPP .bin file. - * @param input_filename path to file contaning input data (.ink.json) - * @param output_filename path to file output data will be written (.bin) - * @param error if not NULL will contain a error message if an error occures (else will be set to - * NULL) - */ - void - ink_compile_json(const char* input_filename, const char* output_filename, const char** error); - - #ifdef __cplusplus } #endif diff --git a/inkcpp_c/inkcpp_c.pc.in b/inkcpp_c/inkcpp_c.pc.in index b13beb58..c8257bdb 100644 --- a/inkcpp_c/inkcpp_c.pc.in +++ b/inkcpp_c/inkcpp_c.pc.in @@ -6,5 +6,5 @@ includedir=${prefix}/include Name: inkcpp Description: C Bindnigs for inkpp. @CMAKE_PROJECT_DESCRIPTION@ Version: @PROJECT_VERSION@ -CFlags: -I${includedir} -Libs: -L${libdir} -linkcpp_c +CFlags: -I${includedir} @INKCPP_C_PC_EXTRA_CFLAGS@ +Libs: -L${libdir} -linkcpp_c @INKCPP_C_PC_EXTRA_LIBS@ diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 988f87fa..3d089277 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -1,22 +1,22 @@ add_library(inkcpp_shared INTERFACE) -target_include_directories(inkcpp_shared - INTERFACE - $ - $ - $ -) -FILE(GLOB PUBLIC_HEADERS "public/*") +target_include_directories( + inkcpp_shared + INTERFACE $ + $ $) +file(GLOB PUBLIC_HEADERS "public/*") set_target_properties(inkcpp_shared PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}") # Unreal installation file(GLOB UNREAL_SOURCE_PUBLIC "public/*") -foreach(SRC_FILE IN LISTS UNREAL_SOURCE_PUBLIC) - get_filename_component(SRC_FILE "${SRC_FILE}" NAME) - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/public/${SRC_FILE}" "${CMAKE_BINARY_DIR}/unreal/inkcpp/Source/shared/Public/${SRC_FILE}" COPYONLY) +foreach(src_file IN LISTS UNREAL_SOURCE_PUBLIC) + get_filename_component(src_file "${src_file}" NAME) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/public/${src_file}" + "${CMAKE_BINARY_DIR}/unreal/inkcpp/Source/shared/Public/${src_file}" COPYONLY) endforeach() file(GLOB UNREAL_SOURCE_PRIVATE "private/*") -foreach(SRC_FILE IN LISTS UNREAL_SOURCE_PRIVATE) - get_filename_component(SRC_FILE "${SRC_FILE}" NAME) - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/private/${SRC_FILE}" "${CMAKE_BINARY_DIR}/unreal//inkcpp/Source/shared/Private/${SRC_FILE}" COPYONLY) +foreach(src_file IN LISTS UNREAL_SOURCE_PRIVATE) + get_filename_component(src_file "${src_file}" NAME) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/private/${src_file}" + "${CMAKE_BINARY_DIR}/unreal//inkcpp/Source/shared/Private/${src_file}" COPYONLY) endforeach()