#!/usr/bin/env sh
# This file is part of nvml-sys. It is subject to the license terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/nvml-sys/master/COPYRIGHT. No part of nvml-sys, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
# Copyright © 2016 The developers of nvml-sys. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/nvml-sys/master/COPYRIGHT.


set -e
set -u
set -f


_program_path_find()
{
    if [ "${_program_fattening_program_path+set}" = 'set' ]; then
        printf '%s\n' "$_program_fattening_program_path"

    elif [ "${0%/*}" = "$0" ]; then

        # We've been invoked by the interpreter as, say, bash program
        if [ -r "$0" ]; then
            pwd -P
        # Clutching at straws; probably run via a download, anonymous script, etc, weird execve, etc
        else
            printf '\n'
        fi

    else

        # We've been invoked with a relative or absolute path (also when invoked via PATH in a shell)

        _program_path_find_parentPath()
        {
            parentPath="${scriptPath%/*}"
            if [ -z "$parentPath" ]; then
                parentPath='/'
            fi
            cd "$parentPath" 1>/dev/null
        }

        # pdksh / mksh have problems with unsetting a variable that was never set...
        if [ "${CDPATH+set}" = 'set' ]; then
            unset CDPATH
        fi

        if command -v realpath 1>/dev/null 2>/dev/null; then
            (
                scriptPath="$(realpath "$0")"

                _program_path_find_parentPath
                pwd -P
            )
        elif command -v readlink 1>/dev/null 2>/dev/null; then
            (
                scriptPath="$0"

                while [ -L "$scriptPath" ]
                do
                    _program_path_find_parentPath
                    scriptPath="$(readlink "$scriptPath")"
                done

                _program_path_find_parentPath
                pwd -P
            )
        else
            # This approach will fail in corner cases where the script itself is a symlink in a path not parallel with the concrete script
            (
                scriptPath="$0"

                _program_path_find_parentPath
                pwd -P
            )
        fi

    fi
}

compile_nvml_fail()
{
	local message="$1"

	printf 'compile-nvml:FAIL:%s\n' "$message" 1>&2
	exit 1
}

compile_nvml_ensureRequiredBinariesArePresent()
{
	local reason="$1"
	shift 1

	local binary
	local missing=false
	for binary in "$@"
	do
		if ! command -v "$binary" 1>/dev/null 2>/dev/null; then
			printf 'compile-nvml:%s\n' "The binary '$binary' needs to be in the path" 1>&2
			missing=true
		fi
	done

	if $missing; then
		compile_nvml_fail "Please make sure that the missing binaries are installed because '$reason'"
	fi
}

_compile_nvml_prepareForMacOSX_brewInstall()
{
	compile_nvml_ensureRequiredBinariesArePresent brew

	local packageName="$1"
	if ! brew ls --versions "$packageName" 1>/dev/null 2>/dev/null; then
		brew install "$packageName" 1>&2
	fi
}

compile_nvml_prepareForMacOSX()
{
	_compile_nvml_prepareForMacOSX_brewInstall gnu-sed
	_compile_nvml_prepareForMacOSX_brewInstall make
	_compile_nvml_prepareForMacOSX_brewInstall libelf
	_compile_nvml_prepareForMacOSX_brewInstall coreutils
	_compile_nvml_prepareForMacOSX_brewInstall FiloSottile/musl-cross/musl-cross

	_compile_nvml_prepareForMacOSX_brewInstall pkg-config
	_compile_nvml_prepareForMacOSX_brewInstall grep
	_compile_nvml_prepareForMacOSX_brewInstall autoconf@2.69
}

compile_nvml_parseCommandLine()
{
	case "$#" in

		0)
			:
		;;

		1)
			case "$1" in

				-h|--help)
					printf './compile-nvml\n'
					printf './compile-nvml -h|--help\n'
					printf 'Pass the environment variable NUM_JOBS to control the number of make jobs\n'
					exit 0
				;;

				*)
					compile_nvml_fail "Does not take any arguments"
				;;

			esac
		;;

		*)
			compile_nvml_fail "Does not take any arguments"
		;;

	esac
}

compile_nvml_setCargoManifestDir()
{	
	if [ -n "${CARGO_MANIFEST_DIR+set}" ]; then
		printf 'compile-nvml:%s\n' "Building with CARGO_MANIFEST_DIR '$CARGO_MANIFEST_DIR'" 1>&2
	else
		local programPath="$(_program_path_find)"
		cd "$programPath"/.. 1>/dev/null 2>/dev/null
			local homeFolder="$(pwd)"
		cd - 1>/dev/null 2>/dev/null
		
		printf 'compile-nvml:%s\n' "Whilst this script is designed to be run under cargo, it can run independently. We're setting CARGO_MANIFEST_DIR to '$homeFolder'" 1>&2
		export CARGO_MANIFEST_DIR="$homeFolder"
	fi
}

compile_nvml_findFolderPaths()
{
	configurationFolderPath="$CARGO_MANIFEST_DIR"/compile-nvml.conf.d
}

compile_nvml_createTemporaryFolder()
{
	temporaryFolderPath="$configurationFolderPath"/temporary
	rm -rf "$temporaryFolderPath"
	mkdir -m 0700 -p "$temporaryFolderPath"
}

compile_nvml_createTemporaryBinariesPath()
{
	rm -rf "$additionalPath"
	mkdir -m 0700 -p "$additionalPath"
	export PATH="$additionalPath":"$PATH"
}

compile_nvml_platformSpecificPreparation()
{
	compile_nvml_ensureRequiredBinariesArePresent uname
	platform="$(uname)"

	if [ -z "${NUM_JOBS+undefined}" ]; then
		numberOfMakeJobs=0
	else
		numberOfMakeJobs="$NUM_JOBS"
	fi

	case "$platform" in

		Darwin)
			compile_nvml_prepareForMacOSX

			compile_nvml_ensureRequiredBinariesArePresent brew

			export PATH="$(brew --prefix coreutils)"/libexec/gnubin:"$(brew --prefix gnu-sed)"/libexec/gnubin:"$PATH"

			ln -s /usr/local/bin/ggrep "$additionalPath"/grep

			muslIncludeFolderPath="$(brew --prefix musl-cross)"/libexec/x86_64-linux-musl/include

			if [ $numberOfMakeJobs -eq 0 ]; then
				compile_nvml_ensureRequiredBinariesArePresent sysctl
				numberOfMakeJobs="$(sysctl -n hw.ncpu)"
			fi
		;;

		Linux)
			compile_nvml_ensureRequiredBinariesArePresent make sed x86_64-linux-musl-gcc x86_64-linux-musl-ar rm mkdir rsync cat
			muslIncludeFolderPath='/usr/include'

			if [ $numberOfMakeJobs -eq 0 ]; then
				compile_nvml_ensureRequiredBinariesArePresent grep
				numberOfMakeJobs="$(grep -c '^processor' /proc/cpuinfo)"
			fi
		;;

		*)
			compile_nvml_fail "Only Darwin (Mac OS X) and Linux (specifically, Alpine Linux) are supported at this time"
		;;

	esac
}

compile_nvml_makeCopyToAlter()
{
	rsync --archive --quiet --exclude=.git "$CARGO_MANIFEST_DIR"/lib/nvml/ "$temporaryFolderPath"/
}

compile_nvml_fixHardcodedFileNameBugs()
{
	sed -i -e 's/nm/'"$compilerPrefix"nm'/g' "$temporaryFolderPath"/utils/check-os.sh

	sed -i -e 's/objcopy/'"$compilerPrefix"objcopy'/g' \
		"$temporaryFolderPath"/src/benchmarks/Makefile \
		"$temporaryFolderPath"/src/common.inc
}

compile_nvml_fixMuslBugs()
{
	sed -i -e 's/#include <stddef.h>/#include <stddef.h>\n#include <limits.h>/g' "$temporaryFolderPath"/src/common/file.h
}

compile_nvml_fixJemallocForCrossCompiling()
{
	cp "$temporaryFolderPath"/src/jemalloc/jemalloc.cfg "$temporaryFolderPath"/src/jemalloc/jemalloc.cfg.orig
	{
		cat <<-EOF
			--host=${configureHost}
			--target=${configureHost}
		EOF
		cat "$temporaryFolderPath"/src/jemalloc/jemalloc.cfg.orig
	} >"$temporaryFolderPath"/src/jemalloc/jemalloc.cfg
}

compile_nvml_fixWarningsToNotBeErrors()
{
	sed -i \
		-e '/-Werror/d' \
		"$temporaryFolderPath"/src/Makefile.inc \
		"$temporaryFolderPath"/src/tools/Makefile.inc
}

compile_nvml_fixDoNotCompileBenchmarks()
{
	sed -i -e 's/ examples benchmarks//g' "$temporaryFolderPath"/src/Makefile
}

compile_nvml_fixDynamicLibraries()
{
	sed -i -e 's;DYNAMIC_LIBS += \$(LIBSDIR_DEBUG)/libpmemcommon.a;DYNAMIC_LIBS += -lpmemcommon;g' "$temporaryFolderPath"/src/tools/Makefile.inc
}

compile_nvml_unsetSomeCargoEnvironmentVariables()
{
	unset DEBUG
	unset RELEASE
	unset HOST
	unset TARGET
	
	# CARGO=/path.to/cargo
	# CARGO_CFG_DEBUG_ASSERTIONS=
	# CARGO_CFG_TARGET_ARCH=x86_64
	# CARGO_CFG_TARGET_ENDIAN=little
	# CARGO_CFG_TARGET_ENV=
	# CARGO_CFG_TARGET_FAMILY=unix
	# CARGO_CFG_TARGET_FEATURE=sse,sse2,sse3,ssse3
	# CARGO_CFG_TARGET_HAS_ATOMIC=128,16,32,64,8,ptr
	# CARGO_CFG_TARGET_OS=macos
	# CARGO_CFG_TARGET_POINTER_WIDTH=64
	# CARGO_CFG_TARGET_THREAD_LOCAL=
	# CARGO_CFG_TARGET_VENDOR=apple
	# CARGO_CFG_UNIX=
	# CARGO_HOME=/path/to/HOME/.cargo
	# CARGO_MANIFEST_DIR=/path/to/parent/folder-then/target/package/nvml-sys-0.0.2
	# CARGO_PKG_AUTHORS=Raphael Cohn <raphael.cohn@stormmq.com>
	# CARGO_PKG_DESCRIPTION=nvml-sys
	# CARGO_PKG_HOMEPAGE=https://github.com/lemonrock/nvml-sys
	# CARGO_PKG_NAME=nvml-sys
	# CARGO_PKG_VERSION=0.0.2
	# CARGO_PKG_VERSION_MAJOR=0
	# CARGO_PKG_VERSION_MINOR=0
	# CARGO_PKG_VERSION_PATCH=2
	# CARGO_PKG_VERSION_PRE=
}

compile_nvml_make()
{
	export CXX="${compilerPrefix}"c++
	export OBJCOPY="${compilerPrefix}"objcopy
	export CC="${compilerPrefix}"cc
	export LD="${compilerPrefix}"ld
	export AR="${compilerPrefix}"ar
	export OBJCOPY="${compilerPrefix}"objcopy
	export NM="${compilerPrefix}"nm
	unset LIBS
	unset OBJS
	unset CFLAGS
	unset CXXFLAGS
	unset LDFLAGS
	unset CPPFLAGS
	unset CPP
	
	cd "$temporaryFolderPath" 1>/dev/null 2>/dev/null
		make -j "$numberOfMakeJobs" EXTRA_CFLAGS="-DPMEMOBJ_DIRECT_NON_INLINE" 1>&2
		make -j "$numberOfMakeJobs" install prefix=/usr DESTDIR="$temporaryFolderPath" 1>&2
	cd - 1>/dev/null 2>/dev/null
}

compile_nvml_installRemoteMemoryHeadersEvenThoughLibrpmemIsNotBuiltBecauseLibfabricIsMissing()
{
	cp "$temporaryFolderPath"/src/include/librpmem.h "$temporaryFolderPath"/usr/include
}

compile_nvml_main()
{
	local configureHost='x86_64-linux-musl'
	local compilerPrefix="${configureHost}"-

	compile_nvml_parseCommandLine "$@"

	compile_nvml_setCargoManifestDir
	
	local configurationFolderPath
	compile_nvml_findFolderPaths

	local temporaryFolderPath
	compile_nvml_createTemporaryFolder

	local additionalPath="$temporaryFolderPath"/PATH
	compile_nvml_createTemporaryBinariesPath

	local platform
	local muslIncludeFolderPath
	local numberOfMakeJobs
	compile_nvml_platformSpecificPreparation

	compile_nvml_makeCopyToAlter

	compile_nvml_fixHardcodedFileNameBugs
	compile_nvml_fixMuslBugs
	compile_nvml_fixJemallocForCrossCompiling
	compile_nvml_fixWarningsToNotBeErrors
	compile_nvml_fixDoNotCompileBenchmarks
	compile_nvml_fixDynamicLibraries
	
	compile_nvml_unsetSomeCargoEnvironmentVariables

	compile_nvml_make

	compile_nvml_installRemoteMemoryHeadersEvenThoughLibrpmemIsNotBuiltBecauseLibfabricIsMissing

	printf '\n\n\nFINISHED\n\n\n' 1>&2
}

compile_nvml_main "$@"
