## AUTOGENERATED COPYRIGHT HEADER START
# Copyright (C) 2017-2024 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
# AUTOGENERATED COPYRIGHT HEADER END
cmake_minimum_required(VERSION 3.26...3.29.2 FATAL_ERROR)

################################################################################
# CMake Setup
################################################################################
project(BlitzLLVM)
list(APPEND CMAKE_MESSAGE_INDENT "[${PROJECT_NAME}] ")

# Module Search Paths
list(APPEND CMAKE_MODULE_PATH
	"${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules"
	"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake-clang"
	"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake-version"
	"${CMAKE_CURRENT_SOURCE_DIR}/cmake"
)

# Include default modules
#- Interprocedural optimizations
include("CheckIPOSupported")

#- CMake-based Versioning
include("version")

#- CMake-based Clang integration
include("clang")

SET_PROPERTY( GLOBAL PROPERTY USE_FOLDERS ON)

################################################################################
# Versioning
################################################################################

version(GENERATE _VERSION COMPRESSED MAJOR 0 MINOR 0 PATCH 0 TWEAK 0 REQUIRE "PATCH;")
version(PARSE _VERSION "${_VERSION}" REQUIRE "PATCH;TWEAK")

# If possible, automatically generate versions from git.
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git")
	find_program(GIT
		NAMES
			git
			git.exe
	)

	if(EXISTS "${GIT}")
		# Try and calculate the exist version using git.
		execute_process(COMMAND "${GIT}" describe --tags --long --abbrev=8 HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} RESULT_VARIABLE GIT_RESULT OUTPUT_VARIABLE GIT_OUTPUT ERROR_VARIABLE GIT_ERROR OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
		if((GIT_RESULT EQUAL 0) AND (NOT "${GIT_OUTPUT}" STREQUAL ""))
			# Result will be MAJOR.MINOR.PATCH-TWEAK-gHASH
			string(REPLACE "-" ";" GIT_OUTPUT "${GIT_OUTPUT}")
			string(REPLACE "." ";" GIT_OUTPUT "${GIT_OUTPUT}")

			# Split into components
			list(GET GIT_OUTPUT 0 GIT_OUTPUT_MAJOR)
			list(GET GIT_OUTPUT 1 GIT_OUTPUT_MINOR)
			list(GET GIT_OUTPUT 2 GIT_OUTPUT_PATCH)
			list(GET GIT_OUTPUT 3 GIT_OUTPUT_TWEAK)
			list(GET GIT_OUTPUT 4 GIT_OUTPUT_BUILD)

			# Special case: Tag contains prerelease
			if(GIT_OUTPUT_PATCH MATCHES "([0-9]+)([a-zA-Z]+)([0-9]*)")
				# Patch requires special parsing.
				set(GIT_OUTPUT_PATCH "${CMAKE_MATCH_1}")
				if(CMAKE_MATCH_3 GREATER 0)
					set(GIT_OUTPUT_PRERELEASE "${CMAKE_MATCH_2}")
				else()
					set(GIT_OUTPUT_PRERELEASE "a")
				endif()
				MATH(EXPR GIT_OUTPUT_TWEAK "${GIT_OUTPUT_TWEAK} + ${CMAKE_MATCH_3}")

				# Modify the global version
				version(MODIFY _VERSION "${_VERSION}" COMPRESS
					MAJOR "${GIT_OUTPUT_MAJOR}"
					MINOR "${GIT_OUTPUT_MINOR}"
					PATCH "${GIT_OUTPUT_PATCH}"
					TWEAK "${GIT_OUTPUT_TWEAK}"
					BUILD "${GIT_OUTPUT_BUILD}"
					PRERELEASE "${GIT_OUTPUT_PRERELEASE}"
					REQUIRE "PATCH;TWEAK"
				)
			else()
				# Modify the global version
				version(MODIFY _VERSION "${_VERSION}" COMPRESS
					MAJOR "${GIT_OUTPUT_MAJOR}"
					MINOR "${GIT_OUTPUT_MINOR}"
					PATCH "${GIT_OUTPUT_PATCH}"
					TWEAK "${GIT_OUTPUT_TWEAK}"
					BUILD "${GIT_OUTPUT_BUILD}"
					PRERELEASE "a"
					REQUIRE "PATCH;TWEAK"
				)
			endif()
		else()
			execute_process(COMMAND "${GIT}" rev-list --count HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} RESULT_VARIABLE GIT_RESULT OUTPUT_VARIABLE GIT_OUTPUT ERROR_VARIABLE GIT_ERROR OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
			if((GIT_RESULT EQUAL 0) AND (NOT "${GIT_OUTPUT}" STREQUAL ""))
				version(MODIFY _VERSION "${_VERSION}" COMPRESS
					TWEAK "${GIT_OUTPUT}"
					PRERELEASE "a"
					REQUIRE "PATCH;TWEAK"
				)

				# Determine the build using git.
				execute_process(COMMAND "${GIT}" log -1 "--pretty=format:g%h" --abbrev=8 HEAD WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} RESULT_VARIABLE GIT_RESULT OUTPUT_VARIABLE GIT_OUTPUT ERROR_VARIABLE GIT_ERROR OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
				if((GIT_RESULT EQUAL 0) AND (NOT "${GIT_OUTPUT}" STREQUAL ""))
					version(MODIFY _VERSION "${_VERSION}" COMPRESS
						BUILD "${GIT_OUTPUT}"
						REQUIRE "PATCH;TWEAK"
					)
				else()
					message(WARNING "Failed to detect build version with 'git'.")
				endif()
			else()
				message(WARNING "Failed to automatically detect version with 'git'.")
			endif()
		endif()
	else()
		message(WARNING "'git' not found, automatic version detection disabled.")
	endif()
else()
	message(STATUS "Not a git repository, automatic version detection disabled.")
endif()

# Allow manual overrides of the detected version.
if(${PREFIX}VERSION)
	version(PARSE _VERSION_CFG "${${PREFIX}VERSION}" REQUIRE "PATCH;TWEAK")
	if("${_VERSION_CFG_BUILD}" STREQUAL "")
		set(_VERSION_CFG_BUILD "${_VERSION_BUILD}")
	endif()
	version(GENERATE _VERSION COMPRESS
		MAJOR "${_VERSION_CFG_MAJOR}"
		MINOR "${_VERSION_CFG_MINOR}"
		PATCH "${_VERSION_CFG_PATCH}"
		TWEAK "${_VERSION_CFG_TWEAK}"
		PRERELEASE "${_VERSION_CFG_PRERELEASE}"
		BUILD "${_VERSION_CFG_BUILD}"
	)
endif()

# Fix up missing parts of the Version
version(PARSE _VERSION "${_VERSION}" REQUIRE "PATCH;TWEAK")

set(_VERSION_THIN "${_VERSION_MAJOR}.${_VERSION_MINOR}.${_VERSION_PATCH}")
if(NOT (_VERSION_PRERELEASE STREQUAL ""))
	set(_VERSION_THIN "${_VERSION_THIN}${_VERSION_PRERELEASE}${_VERSION_TWEAK}")
endif()
if(NOT (VERSION_COMMIT STREQUAL ""))
	set(_VERSION_THIN "${_VERSION_THIN}-${_VERSION_BUILD}")
endif()

# Parse & Log the detected version.
message(STATUS "Version ${_VERSION_THIN}")

################################################################################
# Project
################################################################################

# Metadata
version(GENERATE PROJECT_VERSION
	MAJOR "${_VERSION_MAJOR}"
	MINOR "${_VERSION_MINOR}"
	PATCH "${_VERSION_PATCH}"
	TWEAK "${_VERSION_TWEAK}"
	REQUIRE "PATCH;TWEAK"
)
project(
	${PROJECT_NAME}
	VERSION ${PROJECT_VERSION}
)
set(PROJECT_IDENTIFER "com.xaymar.BlitzLLVM")
set(PROJECT_TITLE "BlitzLLVM")
set(PROJECT_AUTHORS "See AUTHORS file")
set(PROJECT_COPYRIGHT "All Rights Reserved. See LICENSE file for more information")
set(PROJECT_TRADEMARKS "")

function(init_project TARGET)
	set_target_properties(${TARGET} PROPERTIES
		# Always generate position independent code.
		POSITION_INDEPENDENT_CODE ON

		# Set C++ Standard and Extensions
		C_STANDARD 17
		C_STANDARD_REQUIRED ON
		CXX_STANDARD 20
		CXX_STANDARD_REQUIRED ON
		CXX_EXTENSIONS OFF

		# Remove prefix from generated files.
		PREFIX ""
		IMPORT_PREFIX ""

		# Never treat warnings as errors.
		COMPILE_WARNING_AS_ERROR OFF
	)
	target_compile_definitions(${TARGET} PRIVATE
		__STDC_WANT_LIB_EXT1__=1
	)

	if(WIN32)
		target_compile_definitions(${TARGET} PRIVATE
			# windows.h
			#- Disable MIN/MAX macro as this breaks a lot of code.
			NOMINMAX
			#- Disable IN/OUT macro as this breaks a lot of code.
			NOINOUT
		)
	endif()

	if(MSVC) # Microsoft Visual C/C++		
		target_compile_options(${TARGET} PRIVATE
			# Disable useless/terrible behavior from MSVC
			_CRT_SECURE_NO_WARNINGS
			_ENABLE_EXTENDED_ALIGNED_STORAGE

			# Dynamically link Microsoft C/C++ Redistributable.
			$<$<CONFIG:>:/MD>
			$<$<CONFIG:Debug>:/MDd>
			$<$<CONFIG:Release>:/MD>
			$<$<CONFIG:RelWithDebInfo>:/MD>
			$<$<CONFIG:MinSizeRel>:/MD>

			# Always compile using multiple processors. Why is this defaulting off anyway?!
			/MP

			# We want full exception support all the time, not conditionally.
			/EHa
		)
	endif()
endfunction()

################################################################################
# Sub-projects
################################################################################
ADD_SUBDIRECTORY("code_compiler")
