# PSn00bSDK main build script
# (C) 2021-2022 spicyjpeg - MPL licensed

# NOTE: CMake doesn't support using multiple toolchains in a single project,
# so we can't use add_subdirectory() to build both the libraries and tools. A
# workaround is to use ExternalProject_Add() to launch multiple independent
# CMake instances, creating what's known as a "superbuild".

cmake_minimum_required(VERSION 3.21)
include(ExternalProject)

project(
	PSn00bSDK
	LANGUAGES    NONE
	VERSION      0.24
	DESCRIPTION  "Open source PlayStation 1 SDK"
	HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk"
)

# Including this without initializing at least one language throws a warning and
# there's no way to mute it.
include(GNUInstallDirs)

## Settings

# These are passed through to libpsn00b and the examples (they are defined in
# the toolchain file).
set(
	PSN00BSDK_TC ""
	CACHE PATH   "Path to the GCC toolchain's installation directory (if not in PATH)"
)
set(
	PSN00BSDK_TARGET mipsel-none-elf
	CACHE STRING     "GCC toolchain target triplet"
)

set(
	MKPSXISO_NO_LIBFLAC OFF
	CACHE BOOL          "Disable libflac integration when building mkpsxiso"
)
set(
	SKIP_EXAMPLES OFF
	CACHE BOOL    "Skip building SDK examples (not required for installation)"
)
set(
	LIBPSN00B_GENERATOR ${CMAKE_GENERATOR}
	CACHE STRING        "CMake generator to use for building libpsn00b and examples"
)
set(
	PSN00BSDK_GIT_TAG ""
	CACHE STRING      "Git tag or branch name (used by CI)"
)
set(
	PSN00BSDK_GIT_COMMIT ""
	CACHE STRING         "Git commit hash (used by CI)"
)

# Attempt to automatically select a suitable CMake generator to build libpsn00b
# and the examples. Only Ninja and makefile-based generators can be used, as
# other generators (VS and Xcode) do not allow custom toolchains to be used.
if(NOT LIBPSN00B_GENERATOR MATCHES ".*(Make|Makefiles|Ninja)( Multi-Config)?$")
	find_program(_ninja ninja NO_CACHE)

	if(_ninja STREQUAL "_ninja-NOTFOUND")
		message(FATAL_ERROR "LIBPSN00B_GENERATOR must be set to a CMake generator which supports custom toolchains (Ninja or make). The current default generator (${CMAKE_GENERATOR}) will be used to build tools.")
	else()
		set(
			LIBPSN00B_GENERATOR Ninja
			CACHE STRING        "CMake generator to use for building libpsn00b and examples"
			FORCE
		)
		message(WARNING "The current default generator (${CMAKE_GENERATOR}) will be used to build tools but does not support custom toolchains. Ninja will be used instead for building libpsn00b and examples.")
	endif()
endif()

string(TIMESTAMP PSN00BSDK_BUILD_DATE UTC)

# Forward some important variables to mkpsxiso and to the subprojects (they are
# not inherited automatically as they are not environment variables). This also
# sets all subprojects to "install" everything to a temporary directory in the
# build tree, so they don't actually get installed until "cmake --install" is
# invoked (ExternalProject_Add() runs the subprojects' install step at build
# time).
set(
	_common_args
	-DPSN00BSDK_TC:PATH=${PSN00BSDK_TC}
	-DPSN00BSDK_TARGET:STRING=${PSN00BSDK_TARGET}
	-DPSN00BSDK_VERSION:STRING=${PROJECT_VERSION}
	-DPSN00BSDK_BUILD_DATE:STRING=${PSN00BSDK_BUILD_DATE}
	-DPSN00BSDK_GIT_TAG:STRING=${PSN00BSDK_GIT_TAG}
	-DPSN00BSDK_GIT_COMMIT:STRING=${PSN00BSDK_GIT_COMMIT}
)
set(
	_tools_args
	${_common_args}
	-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE}
	-DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/tree
	-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
	-DMKPSXISO_NO_LIBFLAC:BOOL=${MKPSXISO_NO_LIBFLAC}
)
set(
	_libpsn00b_args
	${_common_args}
	-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE}
	-DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/tree
)
set(
	_examples_args
	${_common_args}
	-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${PROJECT_BINARY_DIR}/tree/${CMAKE_INSTALL_LIBDIR}/libpsn00b/cmake/sdk.cmake
	-DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/examples
	-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
)

# Ensure PSn00bSDK isn't being built using the toolchain file from PSn00bSDK
# itself (or from another version of it).
if(CMAKE_TOOLCHAIN_FILE MATCHES ".*libpsn00b[/\\]cmake[/\\]sdk\.cmake$")
	unset(CMAKE_TOOLCHAIN_FILE CACHE)
	message(WARNING "CMAKE_TOOLCHAIN_FILE is currently set to the toolchain file of an existing PSn00bSDK installation. It will be ignored.")
endif()

## Subprojects

if(NOT EXISTS ${PROJECT_SOURCE_DIR}/tools/mkpsxiso/CMakeLists.txt)
	message(FATAL_ERROR "The mkpsxiso directory is empty. Run 'git submodule update --init --recursive' to populate it.")
endif()

ExternalProject_Add(
	tools
	PREFIX           subprojects
	SOURCE_DIR       ${PROJECT_SOURCE_DIR}/tools
	CMAKE_CACHE_ARGS ${_tools_args}
	INSTALL_DIR      tree
)
ExternalProject_Add(
	mkpsxiso
	PREFIX           subprojects
	SOURCE_DIR       ${PROJECT_SOURCE_DIR}/tools/mkpsxiso
	CMAKE_CACHE_ARGS ${_tools_args}
	INSTALL_DIR      tree
)
ExternalProject_Add(
	libpsn00b-debug
	PREFIX           subprojects
	SOURCE_DIR       ${PROJECT_SOURCE_DIR}/libpsn00b
	CMAKE_GENERATOR  ${LIBPSN00B_GENERATOR}
	CMAKE_CACHE_ARGS ${_libpsn00b_args} -DCMAKE_BUILD_TYPE:STRING=Debug
	INSTALL_DIR      tree
)
ExternalProject_Add(
	libpsn00b-release
	PREFIX           subprojects
	SOURCE_DIR       ${PROJECT_SOURCE_DIR}/libpsn00b
	CMAKE_GENERATOR  ${LIBPSN00B_GENERATOR}
	CMAKE_CACHE_ARGS ${_libpsn00b_args} -DCMAKE_BUILD_TYPE:STRING=Release
	INSTALL_DIR      tree
)
ExternalProject_Add(
	examples
	PREFIX           subprojects
	SOURCE_DIR       ${PROJECT_SOURCE_DIR}/examples
	CMAKE_GENERATOR  ${LIBPSN00B_GENERATOR}
	CMAKE_CACHE_ARGS ${_examples_args}
	INSTALL_DIR      examples
	DEPENDS          libpsn00b-debug libpsn00b-release tools mkpsxiso
	EXCLUDE_FROM_ALL ${SKIP_EXAMPLES}
)

# Install all files in the temporary installation tree, as well as static files
# from the source tree, when "cmake --install" is invoked.
foreach(
	_subdir IN ITEMS
		${CMAKE_INSTALL_BINDIR}
		${CMAKE_INSTALL_LIBDIR}
		${CMAKE_INSTALL_INCLUDEDIR}
		${CMAKE_INSTALL_DATADIR}
)
	install(
		# THE TRAILING SLASH IS IMPORTANT
		DIRECTORY   ${PROJECT_BINARY_DIR}/tree/${_subdir}/
		DESTINATION ${_subdir}
		COMPONENT   sdk
		USE_SOURCE_PERMISSIONS
	)
endforeach()

install(
	FILES       README.md LICENSE.md
	DESTINATION ${CMAKE_INSTALL_DATADIR}/psn00bsdk/doc
	COMPONENT   docs
)
install(
	DIRECTORY   doc template
	DESTINATION ${CMAKE_INSTALL_DATADIR}/psn00bsdk
	COMPONENT   docs
)
install(
	DIRECTORY   examples
	DESTINATION ${CMAKE_INSTALL_DATADIR}/psn00bsdk
	COMPONENT   examples
)
