cmake_minimum_required (VERSION 2.8.12)

# nsync provides portable synchronization primitives, such as mutexes and
# condition variables.
project (nsync)

# Set variable NSYNC_LANGUAGE to "c++11" to build with C++11
# rather than C.

# Some builds need position-independent code.
set (CMAKE_POSITION_INDEPENDENT_CODE ON)

# -----------------------------------------------------------------
# Platform dependencies

# Many platforms use these posix related sources; even Win32.
set (NSYNC_POSIX_SRC
	"platform/posix/src/nsync_panic.c"
	"platform/posix/src/per_thread_waiter.c"
	"platform/posix/src/time_rep.c"
	"platform/posix/src/yield.c"
)

# Many of the string matches below use a literal "X" suffix on both sides.
# This is because some versions of cmake treat (for example) "MSVC" (in quotes)
# as a reference to the variable MSVC, thus the expression
#      "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC"
# is false when ${CMAKE_C_COMPILER_ID} has the value "MSVC"!  See
#    https://cmake.org/cmake/help/v3.1/policy/CMP0054.html

# Pick the include directory for the operating system.
if ("${NSYNC_LANGUAGE}X" STREQUAL "c++11X")
	include_directories ("${PROJECT_SOURCE_DIR}/platform/c++11")
	add_definitions ("-DNSYNC_USE_CPP11_TIMEPOINT -DNSYNC_ATOMIC_CPP11")
	set (NSYNC_OS_CPP_SRC
		"platform/c++11/src/per_thread_waiter.cc"
		"platform/c++11/src/yield.cc"
		"platform/c++11/src/time_rep_timespec.cc"
		"platform/c++11/src/nsync_panic.cc"
	)
	if ("${CMAKE_SYSTEM_NAME}X" STREQUAL "WindowsX")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/win32")
		add_compile_options ("/TP")
		set (NSYNC_OS_SRC
			"platform/c++11/src/nsync_semaphore_mutex.cc"
			"platform/win32/src/clock_gettime.c"
			"platform/win32/src/pthread_key_win32.cc"
			${NSYNC_OS_CPP_SRC}
		)
		set (NSYNC_TEST_OS_SRC
			"platform/win32/src/start_thread.c"
		)
	elseif ("${CMAKE_SYSTEM_NAME}X" STREQUAL "DarwinX")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/macos")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/posix")
		# Some versions of MacOS, such as Sierra, require _DARWIN_C_SOURCE
		# when including certin C++ standard header files, such as <mutex>.
		add_definitions ("-D_DARWIN_C_SOURCE")
		add_compile_options ("-std=c++11")
		set (NSYNC_OS_SRC
			${NSYNC_OS_CPP_SRC}
			"platform/c++11/src/nsync_semaphore_mutex.cc"
			"platform/posix/src/clock_gettime.c"
			"platform/posix/src/nsync_semaphore_mutex.c"
		)
		set (NSYNC_TEST_OS_SRC
			"platform/posix/src/start_thread.c"
		)
	elseif ("${CMAKE_SYSTEM_NAME}X" STREQUAL "LinuxX")
		include_directories (BEFORE "${PROJECT_SOURCE_DIR}/platform/c++11.futex")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/posix")
		add_compile_options ("-std=c++11")
		set (NSYNC_OS_SRC
			"platform/linux/src/nsync_semaphore_futex.c"
			${NSYNC_OS_CPP_SRC}
		)
		set (NSYNC_TEST_OS_SRC
			"platform/posix/src/start_thread.c"
		)
	elseif ("${CMAKE_SYSTEM_NAME}X" STREQUAL "NetBSDX")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/posix")
		add_compile_options ("-std=c++11")
		set (NSYNC_OS_SRC
			"platform/c++11/src/nsync_semaphore_mutex.cc"
			${NSYNC_OS_CPP_SRC}
		)
		set (NSYNC_TEST_OS_SRC
			"platform/posix/src/start_thread.c"
		)
	elseif ("${CMAKE_SYSTEM_NAME}X" STREQUAL "FreeBSDX")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/posix")
		add_compile_options ("-std=c++11")
		set (NSYNC_OS_SRC
			"platform/c++11/src/nsync_semaphore_mutex.cc"
			${NSYNC_OS_CPP_SRC}
		)
		set (NSYNC_TEST_OS_SRC
			"platform/posix/src/start_thread.c"
		)
	elseif ("${CMAKE_SYSTEM_NAME}X" STREQUAL "OpenBSDX")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/posix")
		add_compile_options ("-std=c++11")
		set (NSYNC_OS_SRC
			"platform/c++11/src/nsync_semaphore_mutex.cc"
			${NSYNC_OS_CPP_SRC}
		)
		set (NSYNC_TEST_OS_SRC
			"platform/posix/src/start_thread.c"
		)
	endif ()
endif ()

# Pick the include directory for the compiler.
if ("${CMAKE_C_COMPILER_ID}X" STREQUAL "GNUX")
	include_directories ("${PROJECT_SOURCE_DIR}/platform/gcc")
	set (THREADS_HAVE_PTHREAD_ARG ON)
elseif ("${CMAKE_C_COMPILER_ID}X" STREQUAL "ClangX")
	include_directories ("${PROJECT_SOURCE_DIR}/platform/clang")
	set (THREADS_HAVE_PTHREAD_ARG ON)
elseif ("${CMAKE_C_COMPILER_ID}X" STREQUAL "MSVCX")
	include_directories ("${PROJECT_SOURCE_DIR}/platform/msvc")
else ()
	message (WARNING "CMAKE_C_COMPILER_ID (${CMAKE_C_COMPILER_ID}) matched NOTHING")
endif ()

if (NOT "${NSYNC_LANGUAGE}X" STREQUAL "c++11X")
	if ("${CMAKE_SYSTEM_NAME}X" STREQUAL "WindowsX")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/win32")
		set (NSYNC_OS_SRC
			${NSYNC_POSIX_SRC}
			"platform/win32/src/clock_gettime.c"
			"platform/win32/src/init_callback_win32.c"
			"platform/win32/src/nanosleep.c"
			"platform/win32/src/nsync_semaphore_win32.c"
			"platform/win32/src/pthread_cond_timedwait_win32.c"
			"platform/win32/src/pthread_key_win32.cc"
		)
		set (NSYNC_TEST_OS_SRC
			"platform/win32/src/start_thread.c"
		)
	elseif ("${CMAKE_SYSTEM_NAME}X" STREQUAL "DarwinX")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/macos")
		set (NSYNC_POSIX ON)
		set (NSYNC_OS_EXTRA_SRC
			"platform/posix/src/clock_gettime.c"
			"platform/posix/src/nsync_semaphore_mutex.c"
		)
		include_directories ("${PROJECT_SOURCE_DIR}/platform/posix")
	elseif ("${CMAKE_SYSTEM_NAME}X" STREQUAL "LinuxX")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/linux")
		set (NSYNC_POSIX ON)
		set (NSYNC_OS_EXTRA_SRC
		     "platform/linux/src/nsync_semaphore_futex.c"
		)
	elseif ("${CMAKE_SYSTEM_NAME}X" STREQUAL "NetBSDX")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/netbsd")
		set (NSYNC_POSIX ON)
		set (NSYNC_OS_EXTRA_SRC
			"platform/posix/src/nsync_semaphore_mutex.c"
		)
	elseif ("${CMAKE_SYSTEM_NAME}X" STREQUAL "FreeBSDX")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/freebsd")
		set (NSYNC_POSIX ON)
		set (NSYNC_OS_EXTRA_SRC
			"platform/posix/src/nsync_semaphore_mutex.c"
		)
	elseif ("${CMAKE_SYSTEM_NAME}X" STREQUAL "OpenBSDX")
		include_directories ("${PROJECT_SOURCE_DIR}/platform/openbsd")
		set (NSYNC_POSIX ON)
		set (NSYNC_OS_EXTRA_SRC
			"platform/posix/src/nsync_semaphore_mutex.c"
		)
	endif ()
endif ()

if (NSYNC_POSIX)
	include_directories ("${PROJECT_SOURCE_DIR}/platform/posix")
	set (NSYNC_OS_SRC
		${NSYNC_POSIX_SRC}
		${NSYNC_OS_EXTRA_SRC}
	)
	set (NSYNC_TEST_OS_SRC
		"platform/posix/src/start_thread.c"
	)
endif ()

# Pick the include directory for the architecture.
if (("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "x86_64X") OR
    ("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "amd64X") OR
    ("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "AMD64X"))
	include_directories ("${PROJECT_SOURCE_DIR}/platform/x86_64")
elseif (("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "x86_32X") OR
	("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "i386X") OR
        ("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "i686X"))
	include_directories ("${PROJECT_SOURCE_DIR}/platform/x86_32")
elseif (("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "armv6lX") OR
	("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "armv7lX") OR
	("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "armX"))
	include_directories ("${PROJECT_SOURCE_DIR}/platform/arm")
elseif (("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "aarch64X") OR
	("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "arm64X"))
	include_directories ("${PROJECT_SOURCE_DIR}/platform/aarch64")
elseif (("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "ppcX") OR
	("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "ppc32X"))
	include_directories ("${PROJECT_SOURCE_DIR}/platform/ppc32")
elseif (("${CMAKE_SYSTEM_PROCESSOR}X" STREQUAL "ppc64X"))
	include_directories ("${PROJECT_SOURCE_DIR}/platform/ppc64")
endif ()

# Windows uses some include files from the posix directory also.
if ("${CMAKE_SYSTEM_NAME}X" STREQUAL "WindowsX")
	include_directories ("${PROJECT_SOURCE_DIR}/platform/posix")
endif ()

# -----------------------------------------------------------------

include_directories ("${PROJECT_SOURCE_DIR}/public")
include_directories ("${PROJECT_SOURCE_DIR}/internal")

set (NSYNC_SRC
	"internal/common.c"
	"internal/counter.c"
	"internal/cv.c"
	"internal/debug.c"
	"internal/dll.c"
	"internal/mu.c"
	"internal/mu_wait.c"
	"internal/note.c"
	"internal/once.c"
	"internal/sem_wait.c"
	"internal/time_internal.c"
	"internal/wait.c"
	${NSYNC_OS_SRC}
)
add_library (nsync ${NSYNC_SRC})

set (NSYNC_TEST_SRC
	"testing/array.c"
	"testing/atm_log.c"
	"testing/closure.c"
	"testing/smprintf.c"
	"testing/testing.c"
	"testing/time_extra.c"
	${NSYNC_TEST_OS_SRC}
)
add_library (nsync_test ${NSYNC_TEST_SRC})

set (NSYNC_TESTS
	"counter_test"
	"cv_mu_timeout_stress_test"
	"cv_test"
	"cv_wait_example_test"
	"dll_test"
	"mu_starvation_test"
	"mu_test"
	"mu_wait_example_test"
	"mu_wait_test"
	"note_test"
	"once_test"
	"pingpong_test"
	"wait_test"
)

if ("${NSYNC_LANGUAGE}X" STREQUAL "c++11X")
	foreach (s IN ITEMS ${NSYNC_SRC} ${NSYNC_TEST_SRC})
		SET_SOURCE_FILES_PROPERTIES ("${s}" PROPERTIES LANGUAGE CXX)
	endforeach (s)
	foreach (t IN ITEMS ${NSYNC_TESTS})
		SET_SOURCE_FILES_PROPERTIES ("testing/${t}.c" PROPERTIES LANGUAGE CXX)
	endforeach (t)
endif ()

enable_testing ()
foreach (t IN ITEMS ${NSYNC_TESTS})
	add_executable (${t} "testing/${t}.c")
endforeach (t)

find_package (Threads REQUIRED)
set (THREADS_PREFER_PTHREAD_FLAG ON)
foreach (t IN ITEMS "nsync" "nsync_test" ${NSYNC_TESTS})
	if (THREADS_HAVE_PTHREAD_ARG)
		target_compile_options (${t} PUBLIC "-pthread")
	endif ()
	if (CMAKE_THREAD_LIBS_INIT)
		target_link_libraries (${t} "${CMAKE_THREAD_LIBS_INIT}")
	endif ()
endforeach (t)

foreach (t IN ITEMS ${NSYNC_TESTS})
	target_link_libraries (${t} nsync_test nsync)
	add_test (NAME ${t} COMMAND ${t})
endforeach (t)

install (TARGETS nsync
	LIBRARY DESTINATION lib COMPONENT RuntimeLibraries
	ARCHIVE DESTINATION lib COMPONENT Development)

set (NSYNC_INCLUDES
	"public/nsync.h"
	"public/nsync_atomic.h"
	"public/nsync_counter.h"
	"public/nsync_cpp.h"
	"public/nsync_cv.h"
	"public/nsync_debug.h"
	"public/nsync_mu.h"
	"public/nsync_mu_wait.h"
	"public/nsync_note.h"
	"public/nsync_once.h"
	"public/nsync_time.h"
	"public/nsync_time_internal.h"
	"public/nsync_waiter.h"
)

foreach (NSYNC_INCLUDE ${NSYNC_INCLUDES})
	install (FILES ${NSYNC_INCLUDE} DESTINATION include COMPONENT Development)
endforeach ()
