# Copyright 2025 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

EAPI=8

inherit cmake flag-o-matic toolchain-funcs

DESCRIPTION="Memory debugging tool for Linux and Windows"
HOMEPAGE="https://drmemory.org/ https://github.com/DynamoRIO/drmemory"

# DynamoRIO submodule commit for release_2.6.0
DRIO_COMMIT="bbd4ffadd03a7630b6143035c369bf7714357eeb"
# Googletest submodule commit
GTEST_COMMIT="12ddfca458945b8fcffa7ed416076ed55bf669b1"
# DynamoRIO's submodules (libipt and zlib)
LIBIPT_COMMIT="c848a85c3104e2f5780741f85de5c9e65476ece2"
ZLIB_COMMIT="21767c654d31d2dccdde4330529775c6c5fd5389"

SRC_URI="
	https://github.com/DynamoRIO/${PN}/archive/release_${PV}.tar.gz
		-> ${P}.tar.gz
	https://github.com/DynamoRIO/dynamorio/archive/${DRIO_COMMIT}.tar.gz
		-> dynamorio-${DRIO_COMMIT:0:7}.tar.gz
	https://github.com/DynamoRIO/googletest/archive/${GTEST_COMMIT}.tar.gz
		-> googletest-${GTEST_COMMIT:0:7}.tar.gz
	https://github.com/intel/libipt/archive/${LIBIPT_COMMIT}.tar.gz
		-> libipt-${LIBIPT_COMMIT:0:7}.tar.gz
	https://github.com/madler/zlib/archive/${ZLIB_COMMIT}.tar.gz
		-> zlib-${ZLIB_COMMIT:0:7}.tar.gz
"

S="${WORKDIR}/${PN}-release_${PV}"

LICENSE="LGPL-2.1"
SLOT="0"
KEYWORDS="~amd64"
IUSE="doc test"
# DynamoRIO uses special ELF structures for dynamic binary instrumentation
# Stripping corrupts these structures and causes SIGILL at runtime
RESTRICT="strip !test? ( test )"

RDEPEND="
	sys-libs/libunwind:=
	dev-lang/perl
"
DEPEND="${RDEPEND}"
BDEPEND="
	doc? ( app-text/doxygen )
"

PATCHES=(
	"${FILESDIR}/${PN}-2.6.0-cmake-cmp0043.patch"
	"${FILESDIR}/${PN}-2.6.0-gcc-asm-flags.patch"
	"${FILESDIR}/${PN}-2.6.0-gcc15.patch"
	"${FILESDIR}/${PN}-2.6.0-stdbool.patch"
	"${FILESDIR}/${PN}-2.6.0-cxx20-destructor.patch"
	"${FILESDIR}/${PN}-2.6.0-strcasestr.patch"
	"${FILESDIR}/${PN}-2.6.0-uninitialized.patch"
	"${FILESDIR}/${PN}-2.6.0-enum-mismatch.patch"
	"${FILESDIR}/${PN}-2.6.0-options-bool.patch"
	"${FILESDIR}/${PN}-2.6.0-memlayout-bool.patch"
)

QA_FLAGS_IGNORED="
	opt/drmemory/.*
"

src_prepare() {
	# Setup bundled DynamoRIO
	rmdir "${S}/dynamorio" || die
	mv "${WORKDIR}/dynamorio-${DRIO_COMMIT}" "${S}/dynamorio" || die

	# Setup DynamoRIO's bundled libipt
	rmdir "${S}/dynamorio/third_party/libipt" || die
	mv "${WORKDIR}/libipt-${LIBIPT_COMMIT}" "${S}/dynamorio/third_party/libipt" || die

	# Setup DynamoRIO's bundled zlib
	rmdir "${S}/dynamorio/third_party/zlib" || die
	mv "${WORKDIR}/zlib-${ZLIB_COMMIT}" "${S}/dynamorio/third_party/zlib" || die

	# Setup bundled googletest
	rmdir "${S}/third_party/googletest" || die
	mv "${WORKDIR}/googletest-${GTEST_COMMIT}" "${S}/third_party/googletest" || die

	# Fix cmake install commands to respect DESTDIR
	# The file(WRITE/MAKE_DIRECTORY/APPEND) and configure_file() commands
	# do not automatically respect the DESTDIR environment variable
	sed -i \
		-e 's|"\\${CMAKE_INSTALL_PREFIX}/|"\\$ENV{DESTDIR}\\${CMAKE_INSTALL_PREFIX}/|g' \
		"${S}/CMakeLists.txt" "${S}/docs/CMakeLists.txt" || die

	cmake_src_prepare
}

src_configure() {
	# DynamoRIO needs specific compiler flags
	filter-flags -fstack-protector-strong -fstack-clash-protection
	append-flags -fno-stack-protector

	# Avoid AVX-512 for portability
	append-flags -mno-avx512f

	# Static link libstdc++ and libgcc to avoid runtime library path issues
	# DynamoRIO uses a private loader that doesn't respect standard library paths
	# This is the recommended approach per DynamoRIO documentation
	append-ldflags -static-libgcc -static-libstdc++

	local mycmakeargs=(
		-DCMAKE_INSTALL_PREFIX="${EPREFIX}/opt/${PN}"
		-DBUILD_TOOL_TESTS=$(usex test ON OFF)
	)

	# Disable doxygen search when doc USE is disabled
	# drmemory unconditionally runs find_package(Doxygen) and builds docs if found
	if ! use doc; then
		mycmakeargs+=( -DCMAKE_DISABLE_FIND_PACKAGE_Doxygen=ON )
	fi

	cmake_src_configure
}

src_install() {
	cmake_src_install

	# Remove incorrectly installed DynamoRIO build artifacts
	# These are cmake install targets that don't properly respect DESTDIR
	rm -rf "${ED}/var" || die

	# Install DynamoRIO extension libraries that DRMF depends on
	# These are built but not installed by DrMemory's cmake install
	local ext_src="${BUILD_DIR}/dynamorio/ext/lib64/release"
	local ext_dst="${ED}/opt/${PN}/dynamorio/ext/lib64/release"
	if [[ -d "${ext_src}" ]]; then
		einfo "Installing DynamoRIO extension libraries"
		dodir "/opt/${PN}/dynamorio/ext/lib64/release"
		local lib
		for lib in "${ext_src}"/*.so; do
			if [[ -f "${lib}" ]]; then
				cp -a "${lib}" "${ext_dst}/" || die "Failed to install ${lib##*/}"
			fi
		done
	else
		ewarn "DynamoRIO extension directory not found: ${ext_src}"
	fi

	# Create .drpath files for DynamoRIO's private loader to find GCC libraries
	# DynamoRIO uses its own loader that doesn't respect standard library paths
	# The .drpath file is DynamoRIO's cross-platform mechanism for library search paths
	# Note: patchelf is NOT used because it can break DynamoRIO's private loader
	local gcc_libdir
	gcc_libdir="$($(tc-getCC) -print-file-name=libstdc++.so.6)"
	gcc_libdir="${gcc_libdir%/*}"
	if [[ -d "${gcc_libdir}" ]]; then
		einfo "Creating .drpath files with GCC library path: ${gcc_libdir}"
		local f basename
		while IFS= read -r -d '' f; do
			if [[ -f "${f}" ]] && file "${f}" | grep -q "ELF.*shared object"; then
				basename="${f##*/}"
				basename="${basename%.so*}"
				echo "${gcc_libdir}" > "${f%/*}/${basename}.drpath" || \
					ewarn "Failed to create .drpath for ${f}"
			fi
		done < <(find "${ED}/opt/${PN}" -name "*.so" -print0)
	fi

	# Create symlinks in /usr/bin for DrMemory tools
	dodir /usr/bin
	local tool
	for tool in drmemory drltrace drstrace symquery; do
		if [[ -x "${ED}/opt/${PN}/bin64/${tool}" ]]; then
			dosym "../../opt/${PN}/bin64/${tool}" "/usr/bin/${tool}"
		fi
	done

	# Create symlinks in /usr/bin for bundled DynamoRIO tools
	for tool in drrun drconfig drinject drdisas; do
		if [[ -x "${ED}/opt/${PN}/dynamorio/bin64/${tool}" ]]; then
			dosym "../../opt/${PN}/dynamorio/bin64/${tool}" "/usr/bin/${tool}"
		fi
	done

	dodoc README.md
}