TERMUX_PKG_HOMEPAGE=https://emscripten.org
TERMUX_PKG_DESCRIPTION="Emscripten: An LLVM-to-WebAssembly Compiler"
TERMUX_PKG_LICENSE="MIT"
TERMUX_PKG_MAINTAINER="@truboxl"
TERMUX_PKG_VERSION="3.1.24"
TERMUX_PKG_SRCURL=https://github.com/emscripten-core/emscripten.git
TERMUX_PKG_GIT_BRANCH=$TERMUX_PKG_VERSION
TERMUX_PKG_PLATFORM_INDEPENDENT=true
TERMUX_PKG_RECOMMENDS="emscripten-llvm, emscripten-binaryen, python, nodejs-lts | nodejs"
TERMUX_PKG_HOSTBUILD=true
TERMUX_PKG_NO_STATICSPLIT=true
TERMUX_PKG_AUTO_UPDATE=true

# remove files according to emsdk/upstream directory after running
# ./emsdk install latest
TERMUX_PKG_RM_AFTER_INSTALL="
opt/emscripten-llvm/bin/clang-check
opt/emscripten-llvm/bin/clang-cl
opt/emscripten-llvm/bin/clang-cpp
opt/emscripten-llvm/bin/clang-extdef-mapping
opt/emscripten-llvm/bin/clang-format
opt/emscripten-llvm/bin/clang-func-mapping
opt/emscripten-llvm/bin/clang-import-test
opt/emscripten-llvm/bin/clang-linker-wrapper
opt/emscripten-llvm/bin/clang-nvlink-wrapper
opt/emscripten-llvm/bin/clang-offload-bundler
opt/emscripten-llvm/bin/clang-offload-packager
opt/emscripten-llvm/bin/clang-offload-wrapper
opt/emscripten-llvm/bin/clang-pseudo
opt/emscripten-llvm/bin/clang-refactor
opt/emscripten-llvm/bin/clang-rename
opt/emscripten-llvm/bin/clang-repl
opt/emscripten-llvm/bin/clang-scan-deps
opt/emscripten-llvm/bin/diagtool
opt/emscripten-llvm/bin/git-clang-format
opt/emscripten-llvm/bin/hmaptool
opt/emscripten-llvm/bin/ld.lld
opt/emscripten-llvm/bin/ld64.lld
opt/emscripten-llvm/bin/ld64.lld.darwin*
opt/emscripten-llvm/bin/lld-link
opt/emscripten-llvm/bin/llvm-cov
opt/emscripten-llvm/bin/llvm-lib
opt/emscripten-llvm/bin/llvm-ml
opt/emscripten-llvm/bin/llvm-pdbutil
opt/emscripten-llvm/bin/llvm-profdata
opt/emscripten-llvm/bin/llvm-rc
opt/emscripten-llvm/bin/llvm-strings
opt/emscripten-llvm/lib/libclang.so*
opt/emscripten-llvm/share
opt/emscripten/LICENSE
"

# https://github.com/emscripten-core/emscripten/issues/11362
# can switch to stable LLVM to save space once above is fixed
_LLVM_COMMIT=277c382760bf9575cfa2eac73d5ad1db91466d3f
_LLVM_TGZ_SHA256=31612bebdd21208691413ef2da785ec919f4116b716ba12dcd1cc07983901e9b

# https://github.com/emscripten-core/emscripten/issues/12252
# upstream says better bundle the right binaryen revision for now
_BINARYEN_COMMIT=2f722471e55b3bc7c013763d6dc9043fba7317d8
_BINARYEN_TGZ_SHA256=24fb40dda91912a0a54731503a8759cc437ca4d864e0f2e2228ccb946fbc8a0d

# https://github.com/emscripten-core/emsdk/blob/main/emsdk.py
# https://chromium.googlesource.com/emscripten-releases/+/refs/heads/main/src/build.py
# https://github.com/llvm/llvm-project
_LLVM_BUILD_ARGS="
-DCMAKE_BUILD_TYPE=MinSizeRel
-DCMAKE_CROSSCOMPILING=ON
-DCMAKE_INSTALL_PREFIX=$TERMUX_PREFIX/opt/emscripten-llvm

-DDEFAULT_SYSROOT=$(dirname $TERMUX_PREFIX)
-DGENERATOR_IS_MULTI_CONFIG=ON
-DLLVM_ENABLE_ASSERTIONS=ON
-DLLVM_ENABLE_BINDINGS=OFF
-DLLVM_ENABLE_LIBEDIT=OFF
-DLLVM_ENABLE_LIBPFM=OFF
-DLLVM_ENABLE_LIBXML2=OFF
-DLLVM_ENABLE_PROJECTS=clang;compiler-rt;lld
-DLLVM_ENABLE_TERMINFO=OFF
-DLLVM_INCLUDE_EXAMPLES=OFF
-DLLVM_INCLUDE_TESTS=OFF
-DLLVM_INSTALL_TOOLCHAIN_ONLY=ON
-DLLVM_LINK_LLVM_DYLIB=ON
-DLLVM_TABLEGEN=$TERMUX_PKG_HOSTBUILD_DIR/bin/llvm-tblgen

-DCLANG_DEFAULT_LINKER=lld
-DCLANG_ENABLE_ARCMT=OFF
-DCLANG_ENABLE_STATIC_ANALYZER=OFF
-DCLANG_TABLEGEN=$TERMUX_PKG_HOSTBUILD_DIR/bin/clang-tblgen

-DCOMPILER_RT_BUILD_CRT=OFF
-DCOMPILER_RT_BUILD_LIBFUZZER=OFF
-DCOMPILER_RT_BUILD_MEMPROF=OFF
-DCOMPILER_RT_BUILD_PROFILE=OFF
-DCOMPILER_RT_BUILD_SANITIZERS=OFF
-DCOMPILER_RT_BUILD_XRAY=OFF
-DCOMPILER_RT_INCLUDE_TESTS=OFF
"

# https://github.com/WebAssembly/binaryen/blob/main/CMakeLists.txt
_BINARYEN_BUILD_ARGS="
-DCMAKE_INSTALL_PREFIX=$TERMUX_PREFIX/opt/emscripten-binaryen
-DBUILD_TESTS=OFF
"

# based on scripts/updates/internal/termux_github_auto_update.sh
termux_pkg_auto_update() {
	local latest_tag
	latest_tag=$(termux_github_api_get_tag "${TERMUX_PKG_SRCURL}" "${TERMUX_PKG_UPDATE_TAG_TYPE}")

	if [ -z "$latest_tag" ]; then
		termux_error_exit "ERROR: Unable to get tag from ${TERMUX_PKG_SRCURL}"
	fi

	if [ "$latest_tag" = "$TERMUX_PKG_VERSION" ]; then
		echo "INFO: No update needed. Already at version '$TERMUX_PKG_VERSION'."
		return 0
	fi

	# https://github.com/emscripten-core/emscripten/blob/main/docs/packaging.md
	# https://github.com/archlinux/svntogit-community/tree/packages/emscripten/trunk
	# below generates commit hash for the deps according to emscripten releases
	local releases_tags release_tag deps_revision deps_json llvm_commit binaryen_commit llvm_tgz_sha256 binaryen_tgz_sha256
	releases_tags=$(curl -s https://raw.githubusercontent.com/emscripten-core/emsdk/main/emscripten-releases-tags.json)
	release_tag=$(echo "$releases_tags" | python3 -c "import json,sys;print(json.load(sys.stdin)[\"releases\"][\"$latest_tag\"])")
	deps_revision=$(curl -s "https://chromium.googlesource.com/emscripten-releases/+/$release_tag/DEPS?format=text" | base64 -d | grep "_revision':" | sed -e "s|'|\"|g")
	deps_json=$(echo -e "{\n${deps_revision}EOL" | sed -e "s|,EOL|\n}|")
	llvm_commit=$(echo "$deps_json" | python3 -c "import json,sys;print(json.load(sys.stdin)[\"llvm_project_revision\"])")
	binaryen_commit=$(echo "$deps_json" | python3 -c "import json,sys;print(json.load(sys.stdin)[\"binaryen_revision\"])")
	curl -LC - "https://github.com/llvm/llvm-project/archive/$llvm_commit.tar.gz" -o "${TMPDIR:-/tmp}/$llvm_commit.tar.gz"
	curl -LC - "https://github.com/WebAssembly/binaryen/archive/$binaryen_commit.tar.gz" -o "${TMPDIR:-/tmp}/$binaryen_commit.tar.gz"
	llvm_tgz_sha256=$(sha256sum "${TMPDIR:-/tmp}/$llvm_commit.tar.gz" | sed -e "s| .*$||")
	binaryen_tgz_sha256=$(sha256sum "${TMPDIR:-/tmp}/$binaryen_commit.tar.gz" | sed -e "s| .*$||")

	echo "INFO: Generated *.tar.gz checksum for:"
	echo "_LLVM_COMMIT     $llvm_commit = $llvm_tgz_sha256"
	echo "_BINARYEN_COMMIT $binaryen_commit = $binaryen_tgz_sha256"

	sed -i "$TERMUX_PKG_BUILDER_DIR/build.sh" -e "s|^_LLVM_COMMIT=.*|_LLVM_COMMIT=${llvm_commit}|"
	sed -i "$TERMUX_PKG_BUILDER_DIR/build.sh" -e "s|^_LLVM_TGZ_SHA256=.*|_LLVM_TGZ_SHA256=${llvm_tgz_sha256}|"
	sed -i "$TERMUX_PKG_BUILDER_DIR/build.sh" -e "s|^_BINARYEN_COMMIT=.*|_BINARYEN_COMMIT=${binaryen_commit}|"
	sed -i "$TERMUX_PKG_BUILDER_DIR/build.sh" -e "s|^_BINARYEN_TGZ_SHA256=.*|_BINARYEN_TGZ_SHA256=${binaryen_tgz_sha256}|"

	rm -f "${TMPDIR:-/tmp}/$llvm_commit.tar.gz" "${TMPDIR:-/tmp}/$binaryen_commit.tar.gz"

	termux_pkg_upgrade_version "$latest_tag"
}

termux_step_post_get_source() {
	termux_download \
		"https://github.com/llvm/llvm-project/archive/$_LLVM_COMMIT.tar.gz" \
		"$TERMUX_PKG_CACHEDIR/llvm.tar.gz" \
		"$_LLVM_TGZ_SHA256"
	termux_download \
		"https://github.com/WebAssembly/binaryen/archive/$_BINARYEN_COMMIT.tar.gz" \
		"$TERMUX_PKG_CACHEDIR/binaryen.tar.gz" \
		"$_BINARYEN_TGZ_SHA256"
	tar -xf "$TERMUX_PKG_CACHEDIR/llvm.tar.gz" -C "$TERMUX_PKG_CACHEDIR"
	tar -xf "$TERMUX_PKG_CACHEDIR/binaryen.tar.gz" -C "$TERMUX_PKG_CACHEDIR"

	local llvm_patches=$(find "$TERMUX_PKG_BUILDER_DIR" -mindepth 1 -maxdepth 1 -type f -name 'llvm-project-*.patch.diff')
	if [ -n "$llvm_patches" ]; then
		cd "$TERMUX_PKG_CACHEDIR/llvm-project-$_LLVM_COMMIT"
		for patch in $llvm_patches; do
			patch -p1 -i "$patch" || :
		done
		local llvm_patches_rej=$(find . -type f -name '*.rej')
		if [ -n "$llvm_patches_rej" ]; then
			echo "INFO: Patch failed! Printing *.rej files ..."
			for rej in $llvm_patches_rej; do
				echo -e "\n\n${rej}"
				cat "$rej"
			done
			termux_error_exit "Patch failed! Please check patch errors above!"
		fi
	fi

	local binaryen_patches=$(find "$TERMUX_PKG_BUILDER_DIR" -mindepth 1 -maxdepth 1 -type f -name 'binaryen-*.patch.diff')
	if [ -n "$binaryen_patches" ]; then
		cd "$TERMUX_PKG_CACHEDIR/binaryen-$_BINARYEN_COMMIT"
		for patch in $binaryen_patches; do
			patch -p1 -i "$patch" || :
		done
		local binaryen_patches_rej=$(find . -type f -name '*.rej')
		if [ -n "$binaryen_patches_rej" ]; then
			echo "INFO: Patch failed! Printing *.rej files ..."
			for rej in $binaryen_patches_rej; do
				echo -e "\n\n${rej}"
				cat "$rej"
			done
			termux_error_exit "Patch failed! Please check patch errors above!"
		fi
	fi
}

termux_step_host_build() {
	termux_setup_cmake
	termux_setup_ninja

	cmake \
		-G Ninja \
		-S "$TERMUX_PKG_CACHEDIR/llvm-project-$_LLVM_COMMIT/llvm" \
		-DCMAKE_BUILD_TYPE=Release \
		-DLLVM_ENABLE_PROJECTS=clang
	cmake \
		--build "$TERMUX_PKG_HOSTBUILD_DIR" \
		-j "$TERMUX_MAKE_PROCESSES" \
		--target llvm-tblgen clang-tblgen
}

termux_step_make() {
	termux_setup_cmake
	termux_setup_ninja

	# from packages/libllvm/build.sh
	export _LLVM_DEFAULT_TARGET_TRIPLE=${CCTERMUX_HOST_PLATFORM/-/-unknown-}
	export _LLVM_TARGET_ARCH
	if [ "$TERMUX_ARCH" = arm ]; then
		_LLVM_TARGET_ARCH=ARM
	elif [ "$TERMUX_ARCH" = aarch64 ]; then
		_LLVM_TARGET_ARCH=AArch64
	elif [ "$TERMUX_ARCH" = i686 ] || [ "$TERMUX_ARCH" = x86_64 ]; then
		_LLVM_TARGET_ARCH=X86
	else
		termux_error_exit "Invalid arch: $TERMUX_ARCH"
	fi

	_LLVM_BUILD_ARGS+=" -DLLVM_TARGET_ARCH=$_LLVM_TARGET_ARCH"
	_LLVM_BUILD_ARGS+=" -DLLVM_TARGETS_TO_BUILD=WebAssembly;$_LLVM_TARGET_ARCH"
	_LLVM_BUILD_ARGS+=" -DLLVM_HOST_TRIPLE=$_LLVM_DEFAULT_TARGET_TRIPLE"

	cmake \
		-G Ninja \
		-S "$TERMUX_PKG_CACHEDIR/llvm-project-$_LLVM_COMMIT/llvm" \
		-B "$TERMUX_PKG_BUILDDIR/build-llvm" \
		$_LLVM_BUILD_ARGS
	cmake \
		--build "$TERMUX_PKG_BUILDDIR/build-llvm" \
		-j "$TERMUX_MAKE_PROCESSES" \
		--target install

	cmake \
		-G Ninja \
		-S "$TERMUX_PKG_CACHEDIR/binaryen-$_BINARYEN_COMMIT" \
		-B "$TERMUX_PKG_BUILDDIR/build-binaryen" \
		$_BINARYEN_BUILD_ARGS
	cmake \
		--build "$TERMUX_PKG_BUILDDIR/build-binaryen" \
		-j "$TERMUX_MAKE_PROCESSES" \
		--target install
}

termux_step_make_install() {
	cd "$TERMUX_PKG_SRCDIR"

	# https://github.com/emscripten-core/emscripten/pull/15840
	sed -e "s|-git||" -i "$TERMUX_PKG_SRCDIR/emscripten-version.txt"

	# skip using Makefile which does host npm install
	rm -fr "$TERMUX_PREFIX/opt/emscripten"
	./tools/install.py "$TERMUX_PREFIX/opt/emscripten"

	# subpackage optional third party test suite files
	cp -fr "$TERMUX_PKG_SRCDIR/test/third_party" "$TERMUX_PREFIX/opt/emscripten/test/third_party"

	# first run generates .emscripten and exits immediately
	rm -f "$TERMUX_PKG_SRCDIR/.emscripten"
	./emcc --generate-config
	sed -i .emscripten -e "s|^EMSCRIPTEN_ROOT.*|EMSCRIPTEN_ROOT = '$TERMUX_PREFIX/opt/emscripten' # directory|"
	sed -i .emscripten -e "s|^LLVM_ROOT.*|LLVM_ROOT = '$TERMUX_PREFIX/opt/emscripten-llvm/bin' # directory|"
	sed -i .emscripten -e "s|^BINARYEN_ROOT.*|BINARYEN_ROOT = '$TERMUX_PREFIX/opt/emscripten-binaryen' # directory|"
	sed -i .emscripten -e "s|^NODE_JS.*|NODE_JS = '$TERMUX_PREFIX/bin/node' # executable|"
	grep "$TERMUX_PREFIX" "$TERMUX_PKG_SRCDIR/.emscripten"
	install -Dm644 "$TERMUX_PKG_SRCDIR/.emscripten" "$TERMUX_PREFIX/opt/emscripten/.emscripten"

	# add emscripten directory to PATH var
	cat <<- EOF > "$TERMUX_PKG_TMPDIR/emscripten.sh"
	#!$TERMUX_PREFIX/bin/sh
	export PATH=\$PATH:$TERMUX_PREFIX/opt/emscripten
	EOF
	install -Dm644 "$TERMUX_PKG_TMPDIR/emscripten.sh" "$TERMUX_PREFIX/etc/profile.d/emscripten.sh"

	# add useful tools not installed by LLVM_INSTALL_TOOLCHAIN_ONLY=ON
	for tool in llc llvm-{addr2line,dwarfdump,dwp,link,mc,nm,objdump,ranlib,readobj,size} opt; do
		install -Dm755 "$TERMUX_PKG_BUILDDIR/build-llvm/bin/$tool" "$TERMUX_PREFIX/opt/emscripten-llvm/bin/$tool"
	done

	# wasm32 triplets
	ln -fsT "clang"   "$TERMUX_PREFIX/opt/emscripten-llvm/bin/wasm32-clang"
	ln -fsT "clang++" "$TERMUX_PREFIX/opt/emscripten-llvm/bin/wasm32-clang++"
	ln -fsT "clang"   "$TERMUX_PREFIX/opt/emscripten-llvm/bin/wasm32-wasi-clang"
	ln -fsT "clang++" "$TERMUX_PREFIX/opt/emscripten-llvm/bin/wasm32-wasi-clang++"
	ln -fsT "lld"     "$TERMUX_PREFIX/opt/emscripten-llvm/bin/wasm-ld"
}

termux_step_create_debscripts() {
	# emscripten's package-lock.json is generated with nodejs v12.13.0
	# which comes with npm v6 which used lockfile version 1
	# which isn't compatible with lockfile version 2 used in npm v7 and v8
	cat <<- EOF > postinst
	#!$TERMUX_PREFIX/bin/bash
	if [ -n "\$(command -v npm)" ]; then
	cd "$TERMUX_PREFIX/opt/emscripten"
	NPM_VERSION=\$(npm --version)
	NPM_MAJOR_VERSION=\${NPM_VERSION:0:1}
	if [ "\$NPM_MAJOR_VERSION" = 6 ]; then
	echo 'Running "npm ci --production --no-optional" in $TERMUX_PREFIX/opt/emscripten ...'
	npm ci --production --no-optional
	else
	echo 'Running "npm install --omit=dev --omit=optional" in $TERMUX_PREFIX/opt/emscripten ...'
	rm package-lock.json
	npm install --omit=dev --omit=optional
	fi
	else
	echo '
	WARNING: npm is not installed! Emscripten may not work properly without installing node modules!
	' >&2
	fi
	echo '
	====================
	Post-install notice:
	If this is the first time installing Emscripten,
	please start a new session to take effect.
	If you are upgrading, you may want to clear the
	cache by running the command below to fix issues.

	emcc --clear-cache

	===================='
	EOF

	cat <<- EOF > postrm
	#!$TERMUX_PREFIX/bin/sh
	case "\$1" in
	purge|remove)
	rm -fr "$TERMUX_PREFIX/opt/emscripten"
	esac
	EOF
}

# Emscripten Test Suite (Optional)
# Some preparations need to be made in Emscripten directory before running
# test suite on Android / Termux. Refer docs below:
# https://emscripten.org/docs/getting_started/test-suite.html
# https://github.com/emscripten-core/emscripten/pull/13493
# https://github.com/emscripten-core/emscripten/issues/9098
#
# Steps:
# - pkg install cmake emscripten-tests-third-party ndk-sysroot openjdk-17
# - cd $PREFIX/opt/emscripten
# - MATHLIB="m" pip install -r requirements-dev.txt
# - npm install --omit=optional
# - export EMTEST_SKIP_V8=1
# - test/runner {test_name}
