#!/usr/bin/env make -f

# Pip-Licenses Makefile
# ..................................
# Copyright (c) 2018-2024, raimon
# Copyright (c) 2024-2026, Mr. Walls
# ..................................
# Licensed under MIT (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# ..........................................
# https://github.com/raimon49/pip-licenses/tree/HEAD/LICENSE
# ..........................................
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

ifeq "$(LANG)" ""
	LANG="en_US.UTF-8"
endif

ifeq "$(LC_CTYPE)" ""
	LC_CTYPE="en_US.UTF-8"
endif

ifndef SHELL
	SHELL:=command -pv bash
endif

ifeq "$(ERROR_LOG_PATH)" ""
	ERROR_LOG_PATH="/dev/null"
endif

# find correct python in cross-platform way
ifeq "$(COMMAND)" ""
	COMMAND_CMD!=`command -v xcrun || command which which || command -v which || command -v command`
	ifeq "$(COMMAND_CMD)" "*xcrun"
		COMMAND_ARGS=--find
	endif
	ifeq "$(COMMAND_CMD)" "*command"
		COMMAND_ARGS=-pv
	endif
	COMMAND=$(COMMAND_CMD) $(COMMAND_ARGS)
endif

ifeq "$(MAKE)" ""
	#  just no cmake please
	MAKEFLAGS=$(MAKEFLAGS) -s
	MAKE!=`$(COMMAND) make 2>$(ERROR_LOG_PATH) || $(COMMAND) gnumake 2>$(ERROR_LOG_PATH)`
endif

ifeq "$(ECHO)" ""
	ECHO=printf "%s\n"
endif

ifndef PYTHONUTF8
	PYTHONUTF8 := 1
endif

ifeq "$(PYTHON)" ""
	PY_CMD=$(COMMAND) python3
	ifneq "$(PY_CMD)" ""
		PY_ARGS=-B
	else
		PY_CMD=$(COMMAND) python
	endif
	PYTHON=$(PY_CMD) $(PY_ARGS)
endif

# SUPPORT PEP-517 with --use-pep517 when available

ifndef PIP_COMMON_FLAGS
	# Define probable pip install flags based on python command
	ifneq "$(PY_CMD)" ""
		# Define probable pip install flags
		PIP_PREFIX_FLAGS := --no-input
	else
		# Define common pip install flags
		PIP_PREFIX_FLAGS := --no-input
	endif
	# Define common pip install flags
	PIP_COMMON_FLAGS := --exists-action s --upgrade --upgrade-strategy eager
endif

ifeq "$(DEV_PAT)" ""
	# Replaces 'filelock==*' with 'filelock>=3.19.1' for Python 3.9 support
	DEV_PAT=s/^\(filelock\)\(=+\)\(\[0-9.\]+\)\$$/\\1\>\=3.19.1/g
endif

ifeq "$(LOG)" ""
	LOG=no
endif

ifeq "$(LOG)" "no"
	QUIET=@
	ifeq "$(DO_FAIL)" ""
		DO_FAIL=$(COMMAND) true
	endif
endif

# else
ifeq "$(DO_FAIL)" ""
	DO_FAIL=$(ECHO) "ok"
endif

ifeq "$(SED)" ""
	SED=$(COMMAND) sed -r
endif

ifeq "$(TEST)" ""
	TEST=builtin test
endif

ifeq "$(RM)" ""
	RM=$(COMMAND) rm -f
endif

ifeq "$(RMDIR)" ""
	RMDIR=$(RM)rd
endif

ifeq "$(VENV_NAME)" ""
	ifeq "$(REPO_NAME)" ""
		# based on remote (requires remote named origin)
		# REPO_NAME:=$(shell basename -s .git `git remote get-url origin`)
		# get the local clone directory name (always will exist in clones, even if you call your remote fork on github "github" and setup the remote "upstream")
		REPO_NAME := $(shell basename `git rev-parse --show-toplevel`)
	endif
	# create a venv for THIS git clone (allows developers to have multiple clones if needed)
	VENV_NAME := venv/$(REPO_NAME)
endif

# file path to extra developer requirements generated by pip-compile
DEV_DEPENDS:='dev-requirements'

.SUFFIXES: .py .pyc .pyi

.DEFAULT_GOAL:= help
.PHONY: help, setup, local-install, local-uninstall, update-depends, lint, test, deploy, test-deploy, build, clean, full-clean, un-setup

help:
	$(QUIET)$(ECHO) 'Usage: make <subcommand>'
	$(QUIET)$(ECHO) ''
	$(QUIET)$(ECHO) 'Subcommands:'
	$(QUIET)$(ECHO) '    setup           Setup for development'
	$(QUIET)$(ECHO) '    local-install   Install locally'
	$(QUIET)$(ECHO) '    local-uninstall Uninstall locally'
	$(QUIET)$(ECHO) '    local-ci-check  Run acceptance tests and linting checks locally'
	$(QUIET)$(ECHO) '    update-depends  Re-compile requirements for development (requires pip-tools)'
	$(QUIET)$(ECHO) '    update-style    Re-lint and auto-format with pyproject.toml'
	$(QUIET)$(ECHO) '    build           Build package'
	$(QUIET)$(ECHO) '    lint            Re-lint formatting and typing with pyproject.toml'
	$(QUIET)$(ECHO) '    test            Run project tests'
	$(QUIET)$(ECHO) '    clean           Clean directories'

# utility for some developer environments
venv:
	test -d $@ || mkdir -m 755 ./$@
	$(QUIET)test -d $@ || exit 1 ;

$(VENV_NAME): venv
	test -d ${VENV_NAME} || $(PYTHON) -m venv --without-pip --clear $(VENV_NAME) ;
	$(QUIET)test -d $@ || exit 1 ;

setup: $(VENV_NAME) $(VENV_NAME)/bin/python
	$(QUIET)$(VENV_NAME)/bin/python -B -m ensurepip -vvv || exit 2 ;
	$(VENV_NAME)/bin/python -B -m pip $(PIP_PREFIX_FLAGS) install $(PIP_COMMON_FLAGS) -r $(DEV_DEPENDS).txt

local-install: $(VENV_NAME)/bin/python
	$(VENV_NAME)/bin/python -m pip $(PIP_PREFIX_FLAGS) install $(PIP_COMMON_FLAGS) -e .

local-uninstall:
	$(QUIET){ test ! -e $(VENV_NAME)/bin/python && test ! -e $(VENV_NAME)/bin/pip ; } || \
	$(VENV_NAME)/bin/python -B -m pip $(PIP_PREFIX_FLAGS) uninstall -y pip-licenses ;

local-ci-check: build lint test
	$(QUIET)$(DO_FAIL)  # does nothing successfully (if reached)

update-depends:
	$(QUITE)$(RM) dev-requirements.txt 2>$(ERROR_LOG_PATH) || true ;
	pip-compile --extra dev --no-strip-extras -o dev-requirements.in --quiet --rebuild -U pyproject.toml
	sed -r -e $(DEV_PAT) dev-requirements.in | tee dev-requirements.txt
	$(QUITE)$(RM) dev-requirements.in 2>$(ERROR_LOG_PATH) || true ;

# developer workflow targets

build: clean
	$(VENV_NAME)/bin/python -m build

lint:
	$(VENV_NAME)/bin/python -m ruff --config pyproject.toml check --output-format=github .
	$(VENV_NAME)/bin/python -m ruff --config pyproject.toml format --check .
	$(VENV_NAME)/bin/python -m mypy --install-types --non-interactive .

update-style: lint
	$(VENV_NAME)/bin/python -m ruff --config pyproject.toml check --output-format=github --fix .
	$(VENV_NAME)/bin/python -m ruff --config pyproject.toml format .

test:
	$(VENV_NAME)/bin/python -m pytest

# cleanup and reset

clean:
	$(QUIET)$(RM) -r dist

full-clean:: local-uninstall clean
	$(RM) -vr *.egg-info 2>$(ERROR_LOG_PATH) || true ;
	$(RMDIR) -v ./__pycache__ 2>$(ERROR_LOG_PATH) || true ;
	$(RMDIR) -v ./{piplicenses,tests}/__pycache__ 2>$(ERROR_LOG_PATH) || true ;
	$(RMDIR) -v ./.coverage 2>$(ERROR_LOG_PATH) || true ;
	$(RMDIR) -v ./.mypy_cache 2>$(ERROR_LOG_PATH) || true ;
	$(RMDIR) -v ./.ruff_cache 2>$(ERROR_LOG_PATH) || true ;
	$(RMDIR) -v ./.pytest_cache 2>$(ERROR_LOG_PATH) || true ;

un-setup:: full-clean
	$(RMDIR) -v ./venv 2>$(ERROR_LOG_PATH) || true ;

# dynamic intermediate targets (use 'make setup' to actually make these targets)

$(VENV_NAME)/bin/%: venv $(VENV_NAME) $(VENV_NAME)/bin/
	$(QUIET)test -x $(VENV_NAME)/bin/$* || exit 4 ; # Hint: use $(VENV_NAME) target to generate venv/bin/python

$(VENV_NAME)/bin/: venv $(VENV_NAME)
	$(QUIET)test -d $(VENV_NAME)/bin/ || exit 3 ; # Hint: use $(VENV_NAME) target to generate venv/*/

# historical targets, no-longer supported

deploy:
	$(QUIET)$(ECHO) "Manual Deployment has been deprecated."
	exit 125

test-deploy:
	$(QUIET)$(ECHO) "Test deployments are deprecated."
	$(QUIET)$(ECHO) "Testing is now automatically performed prior to any deployment."
	exit 125
