SDL_shader_tools: cmake: find lemon and re2c using CMake module + build lemon as subproject

From 08b3dcd745a845e847ae9657da61c7b56947fa14 Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Tue, 22 Nov 2022 21:02:20 +0100
Subject: [PATCH] cmake: find lemon and re2c using CMake module + build lemon
 as subproject

---
 CMakeLists.txt       | 74 +++++++++++++++++++++++++++++++-------------
 cmake/Findre2c.cmake | 19 ++++++++++++
 lemon/CMakeLists.txt | 14 +++++++++
 3 files changed, 86 insertions(+), 21 deletions(-)
 create mode 100644 cmake/Findre2c.cmake
 create mode 100644 lemon/CMakeLists.txt

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3df126e..2975cbf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,33 +1,67 @@
-CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
-PROJECT(SDL_shader_tools)
+cmake_minimum_required(VERSION 3.16)
+project(SDL_shader_tools LANGUAGES C)
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
+
+include(ExternalProject)
+
+if(CMAKE_CROSSCOMPILING)
+    set(BUILD_CMAKE_TOOLCHAIN_FILE "" CACHE FILEPATH "CMake toolchain file for build machine")
+    if(NOT BUILD_CMAKE_TOOLCHAIN_FILE)
+        message(FATAL_ERROR "When cross compiling, need a CMake toolchain file for the build machine in BUILD_CMAKE_TOOLCHAIN_FILE")
+    endif()
+endif()
 
 find_package(SDL2 REQUIRED)
+find_package(re2c)
 
-find_program(RE2C re2c DOC "Path to re2c command line app: https://re2c.org/")
-if(NOT RE2C)
+if(NOT TARGET SDL2::SDL2)
+    find_library(SDL2_LIBRARY NAMES SDL2 SDL2-static)
+    add_library(SDL2::SDL2 UNKNOWN IMPORTED)
+    set_target_properties(SDL2::SDL2 PROPERTIES IMPORTED_LOCATION "${SDL2_LIBRARY}")
+    set_target_properties(SDL2::SDL2 PROPERTIES INTERFACE_INCLUDE_DIRS "${SDL2_INCLUDE_DIRS};${SDL2_INCLUDE_DIR}")
+endif()
+
+if(NOT re2c_FOUND)
     message(STATUS "re2c missing. You can go on, but can't rebuild the lexer.")
 else()
-    mark_as_advanced(RE2C)
     add_custom_command(
         OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/SDL_shader_lexer.c"
         DEPENDS SDL_shader_lexer.re
-        COMMAND "${RE2C}"
-        ARGS -is --no-generation-date -o "${CMAKE_CURRENT_SOURCE_DIR}/SDL_shader_lexer.c" "${CMAKE_CURRENT_SOURCE_DIR}/SDL_shader_lexer.re"
+        COMMAND re2c::re2c ARGS -is --no-generation-date -o "${CMAKE_CURRENT_SOURCE_DIR}/SDL_shader_lexer.c" "${CMAKE_CURRENT_SOURCE_DIR}/SDL_shader_lexer.re"
     )
 endif()
 
-# We build lemon, then use it to generate parser C code.
-# !!! FIXME: this needs to build for the current platform if cross-compiling.
-add_executable(lemon "lemon/lemon.c")
+# lemon contains SDL_shader_tools-specific hacks
+if(CMAKE_CROSSCOMPILING)
+    ExternalProject_Add(lemon
+        SOURCE_DIR "${PROJECT_SOURCE_DIR}/lemon"
+        BINARY_DIR "${PROJECT_BINARY_DIR}/lemon"
+        PREFIX "${PROJECT_BINARY_DIR}/lemon"
+        CMAKE_ARGS
+            "-DCMAKE_BUILD_TYPE:STRING=Release"
+            "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${BUILD_CMAKE_TOOLCHAIN_FILE}"
+            "-DLEMON_INSTALL:BOOL=ON"
+            "-DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/lemon/prefix"
+        BUILD_ALWAYS 1
+        STEP_TARGETS install
+    )
+    add_executable(lemon::lemon IMPORTED)
+    set_target_properties(lemon::lemon PROPERTIES IMPORTED_LOCATION "${PROJECT_BINARY_DIR}/lemon/prefix/bin/lemon")
+    add_dependencies(lemon::lemon lemon-install)
+else()
+    add_subdirectory(lemon EXCLUDE_FROM_ALL)
+endif()
+
 add_custom_command(
     OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/SDL_shader_parser.h"
     MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/SDL_shader_parser.lemon"
-    DEPENDS lemon "${CMAKE_CURRENT_SOURCE_DIR}/lemon/lempar.c"
-    COMMAND lemon
-    ARGS -q "-T${CMAKE_CURRENT_SOURCE_DIR}/lemon/lempar.c" "${CMAKE_CURRENT_SOURCE_DIR}/SDL_shader_parser.lemon"
+    DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/lemon/lempar.c"
+    COMMAND lemon::lemon ARGS -q "-T${CMAKE_CURRENT_SOURCE_DIR}/lemon/lempar.c" "${CMAKE_CURRENT_SOURCE_DIR}/SDL_shader_parser.lemon"
 )
 
 add_executable(sdl-shader-compiler
+    "${CMAKE_CURRENT_SOURCE_DIR}/SDL_shader_parser.h"
     utils/sdl-shader-compiler.c
     SDL_shader_common.c
     SDL_shader_lexer.c
@@ -35,17 +69,15 @@ add_executable(sdl-shader-compiler
     SDL_shader_ast.c
     SDL_shader_compiler.c
 )
+target_include_directories(sdl-shader-compiler PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
+target_link_libraries(sdl-shader-compiler PRIVATE SDL2::SDL2)
 target_include_directories(sdl-shader-compiler PRIVATE ${SDL2_INCLUDE_DIRS} ${SDL2_INCLUDE_DIR})
-target_include_directories(sdl-shader-compiler PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(sdl-shader-compiler ${SDL2_LIBRARIES} ${SDL2_LIBRARY})
-
-SET_SOURCE_FILES_PROPERTIES(SDL_shader_ast.c PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/SDL_shader_parser.h")
+target_compile_definitions(sdl-shader-compiler PRIVATE SDL_MAIN_HANDLED)
 
 add_executable(sdl-shader-bytecode-dumper
     utils/sdl-shader-bytecode-dumper.c
 )
-target_include_directories(sdl-shader-bytecode-dumper PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(sdl-shader-bytecode-dumper PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
+target_link_libraries(sdl-shader-bytecode-dumper PRIVATE SDL2::SDL2)
 target_include_directories(sdl-shader-bytecode-dumper PRIVATE ${SDL2_INCLUDE_DIRS} ${SDL2_INCLUDE_DIR})
-
-# end of CMakeLists.txt ...
-
+target_compile_definitions(sdl-shader-bytecode-dumper PRIVATE SDL_MAIN_HANDLED)
diff --git a/cmake/Findre2c.cmake b/cmake/Findre2c.cmake
new file mode 100644
index 0000000..af4599b
--- /dev/null
+++ b/cmake/Findre2c.cmake
@@ -0,0 +1,19 @@
+find_program(RE2C_BINARY
+    NAMES re2c
+    DOC "Path to re2c command line app: https://re2c.org/"
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(re2c
+    FOUND_VAR re2c_FOUND
+    REQUIRED_VARS RE2C_BINARY
+)
+
+if(re2c_FOUND)
+    if(NOT TARGET re2c::re2c)
+        add_executable(re2c::re2c IMPORTED)
+        set_property(TARGET re2c::re2c PROPERTY IMPORTED_LOCATION "${RE2C_BINARY}")
+    endif()
+endif()
+
+mark_as_advanced(RE2C_BINARY)
diff --git a/lemon/CMakeLists.txt b/lemon/CMakeLists.txt
new file mode 100644
index 0000000..d6289b1
--- /dev/null
+++ b/lemon/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.16)
+project(lemon LANGUAGES C)
+
+option(LEMON_INSTALL "Install lemon")
+
+add_executable(lemon lemon.c)
+add_executable(lemon::lemon ALIAS lemon)
+
+if(LEMON_INSTALL)
+    include(GNUInstallDirs)
+    install(TARGETS lemon
+        RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+    )
+endif()