include config.mk

# make sure pushd/popd will be available
SHELL := /bin/bash

ENV_PYTHON = $(AUTHPROXY_BUILD_ENV)/usr/local/bin/python3 -s
# Set PIP_CONFIG_FILE and `--isolated` to ignore any other PIP configs/env vars on the system
ENV_PIP = PIP_CONFIG_FILE=/dev/null $(ENV_PYTHON) -m pip --isolated
PYTHONPATH = $(AUTHPROXY_BUILD_ENV)/usr/local/lib/python$(PY_MAJOR_MINOR)

PIP_INSTALL = $(ENV_PIP) install -qq --no-index --no-deps --no-build-isolation --root-user-action=ignore

# The build directory OpenSSL
OPENSSL_MODULE_DIST=$(AUTHPROXY_BUILD_ENV)/usr/local/openssl
OPENSSL_MODULE_ARTIFACTS=$(OPENSSL_MODULE_DIST)/fips.so $(OPENSSL_MODULE_DIST)/openssl.cnf

# Set appropriate architecture-dependent wheels
# Default to AMD64 since this is the much more likely/safer case
OPENSSL_MODULE = $(OPENSSL_MODULE_AMD64)
CRYPTOGRAPHY = $(CRYPTOGRAPHY_AMD64)
CFFI = $(CFFI_AMD64)
ZOPE_INTERFACE = $(ZOPE_INTERFACE_AMD64)
PSUTIL = $(PSUTIL_AMD64)

# Override them to the aarch64 version only if we're certain it's an aarch64 system
ifeq ($(shell uname -m),aarch64)
OPENSSL_MODULE = $(OPENSSL_MODULE_AARCH64)
CRYPTOGRAPHY = $(CRYPTOGRAPHY_AARCH64)
CFFI = $(CFFI_AARCH64)
ZOPE_INTERFACE = $(ZOPE_INTERFACE_AARCH64)
PSUTIL = $(PSUTIL_AARCH64)
endif

CFLAGS = -O2 -Wall -fstack-protector --param=ssp-buffer-size=4 -D_FORTIFY_SOURCE=2 -fomit-frame-pointer -fPIC
LDFLAGS = -Wl,-z,relro -Wl,-z,now

ifeq ($(shell uname -s),Linux)
ifeq ($(shell uname -m),x86_64)
CFLAGSFORSHARED = -fPIC
LINKFORSHARED = -Xlinker -export-dynamic -pie
endif
endif

ifeq ($(shell uname),Darwin)
OPENSSL_MODULE = $(OPENSSL_MODULE_AARCH64)
CRYPTOGRAPHY = $(CRYPTOGRAPHY_MACOS_AARCH64)
CFFI = $(CFFI_MACOS_AARCH64)
ZOPE_INTERFACE = $(ZOPE_INTERFACE_MACOS_AARCH64)
PSUTIL = $(PSUTIL_MACOS_AARCH64)
CFLAGS = ""
LDFLAGS = ""
CFLAGSFORSHARED = ""
LINKFORSHARED = ""
endif

.PHONY: all dist proxy python rebuild
all: dist

rebuild: clean all

# Print out a more helpful error message if git modules are missing.
# (Just check for the most-recently-added git submodule)
ifeq ("$(wildcard $(DRPC)/setup.py)","")
    $(error Missing authproxy submodules. Run `git submodule update --init` on host machine)
endif

$(PYTHON_DIR)/.regen:
	pushd $(PYTHON_DIR) && \
	touch .regen && \
	find . -exec touch {} \; && \
	popd

# Calls configure to generate the Makefile
# ac_cv_header_sqlite3_h=no disables SQLite3 in Python
$(PYTHON_DIR)/Makefile: $(PYTHON_DIR)/.regen
	pushd $(PYTHON_DIR) && \
	OPT="" \
	CFLAGS="$(CFLAGS)" \
	LDFLAGS="$(LDFLAGS)" \
	LINKFORSHARED="$(LINKFORSHARED)" \
	CFLAGSFORSHARED="$(CFLAGSFORSHARED)" \
	ac_cv_header_sqlite3_h=no ./configure \
		--with-ensurepip=no \
		--with-openssl=/dev/null \
		$(EXTRA_CONFIGURE_FLAGS) && \
	popd

python $(PYTHON_ARTIFACTS): $(PYTHON_DIR)/Makefile
# Python 3.12's makefile (incorrectly) thinks that blake2s_impl.c is older than its prequisites
# when building in Github Actions, so we touch it to prevent a rebuild of a source file which is already
# "prebuilt" as part of the core python source.
	pushd $(PYTHON_DIR) && \
	touch Modules/_blake2/blake2s_impl.c && \
	$(MAKE) all && \
	env DESTDIR=$(AUTHPROXY_BUILD_ENV) $(MAKE) install && \
	popd
# Python's ensurepip installs an old version of setuptools with known vulerabilities.
# Instead, manually bootstrap pip/setuptools with recent versions.
# Python can directly run pip from the wheel; use that to have pip install itself
	$(ENV_PYTHON) $(PIP)/pip install -qq --no-index --no-deps --force-reinstall --no-build-isolation $(PIP)
	$(PIP_INSTALL) $(SETUPTOOLS)
	$(PIP_INSTALL) $(WHEEL)

third_party: $(PYTHON_ARTIFACTS) $(OPENSSL_MODULE_ARTIFACTS)
	$(PIP_INSTALL) $(PYCPARSER) \
	$(CFFI) \
	$(ASN1CRYPTO) \
	$(IDNA) \
	$(SIX) \
	$(DUO_CLIENT_PYTHON) \
	$(PYOPENSSL) \
	$(ZOPE_INTERFACE) \
	$(ATTRS) \
	$(HYPERLINK) \
	$(SETUPTOOLS_SCM) \
	$(AUTOMAT) \
	$(INCREMENTAL) \
	$(CONSTANTLY) \
	$(TYPING_EXTENSIONS) \
	$(TWISTED) \
	$(DECORATOR) \
	$(DPKT) \
	$(PYPARSING) \
	$(LDAPTOR) \
	$(PYRAD) \
	$(TWISTEDCONNECTPROXY) \
	$(DRPC) \
	$(PYASN1) \
	$(PYASN1_MODULES) \
	$(SERVICE_IDENTITY) \
	$(CRYPTOGRAPHY) \
	$(COLORAMA) \
	$(PACKAGING) \
	$(PSUTIL) \
	$(NETADDR)

# Confirm we don't have any missing/conflicting dependencies
	$(ENV_PIP) check


$(OPENSSL_MODULE_ARTIFACTS): $(SITE_ARTIFACTS)
	mkdir -p $(OPENSSL_MODULE_DIST)
	cp $(OPENSSL_MODULE)/fips.so $(OPENSSL_MODULE_DIST)/fips.so
	cp $(OPENSSL_MODULE)/openssl.cnf $(OPENSSL_MODULE_DIST)/openssl.cnf


third_party_test: $(PYTHON_ARTIFACTS) third_party
	$(PIP_INSTALL) $(BEHAVE) \
	$(PARSE_TYPE) \
	$(PARSE) \
	$(ENTRYPOINTS) \
	$(MCCABE) \
	$(PYCODESTYLE) \
	$(PYFLAKES) \
	$(FLAKE8) \
	$(DLINT) \
	$(WCWIDTH) \
	$(PYTEST_RANDOM_ORDER) \
	$(PYTEST) \
	$(PLUGGY) \
	$(MORE_ITERTOOLS) \
	$(INICONFIG) \
	$(AUTHPROXY)

	$(ENV_PIP) check



site $(SITE_ARTIFACTS): $(PYTHON_ARTIFACTS) $(SITE_FILES) $(FEATURE_FLAG_ARTIFACT)
	install -m 644 $(SITE_FILES) $(AUTHPROXY_BUILD)/usr/local/lib/python$(PY_MAJOR_MINOR)

# marked as wildcard because it may not exist and that's okay
feature_flags $(FEATURE_FLAG_ARTIFACT): $(wildcard $(FEATURE_FLAG_FILE))
	# it's fine if this fails because it may not exist
	- install -m 644 $(FEATURE_FLAG_FILE) $(FEATURE_FLAG_ARTIFACT)

proxy $(DUOAUTHPROXY_ARTIFACT): third_party $(shell find $(PROXY_MODULE_DIR) -type f ! -name '*.pyc') $(SITE_ARTIFACTS)
proxy $(DUOAUTHPROXY_ARTIFACT):
	$(PIP_INSTALL) $(AUTHPROXY)

$(AUTHPROXY_BUILD_ENV)/conf:
	mkdir -p $(AUTHPROXY_BUILD_ENV)/conf

$(AUTHPROXY_BUILD_ENV)/log:
	mkdir -p $(AUTHPROXY_BUILD_ENV)/log

$(AUTHPROXY_BUILD_ENV)/run:
	mkdir -p $(AUTHPROXY_BUILD_ENV)/run

$(AUTHPROXY_BUILD_ENV)/bin:
	mkdir -p $(AUTHPROXY_BUILD_ENV)/bin

$(AUTHPROXY_BUILD_ENV)/doc:
	mkdir -p $(AUTHPROXY_BUILD_ENV)/doc

$(AUTHPROXY_BUILD_ENV)/usr/local/bin:
	mkdir -p $(AUTHPROXY_BUILD_ENV)/usr/local/bin

$(TAPFILE_ARTIFACT): $(AUTHPROXY)/scripts/duoauthproxy.tap | $(AUTHPROXY_BUILD_ENV)/bin
	cp $(AUTHPROXY)/scripts/duoauthproxy.tap $(TAPFILE_ARTIFACT)

$(AUTHPROXY)/build/scripts-$(PY_MAJOR_MINOR)/install: $(PYTHON_ARTIFACTS)

$(AUTHPROXY_BUILD_ENV)/install: $(AUTHPROXY)/scripts/install
	cp $(AUTHPROXY)/scripts/install $(AUTHPROXY_BUILD_ENV)/install

$(AUTHPROXY_BUILD_ENV)/doc/%: doc/% | $(AUTHPROXY_BUILD_ENV)/doc
	cp $< $@

$(AUTHPROXY_BUILD_ENV)/conf/%: conf/% | $(AUTHPROXY_BUILD_ENV)/conf
	cp $< $@

$(AUTHPROXY_BUILD_ENV)/usr/local/bin/%: scripts/% | $(AUTHPROXY_BUILD_ENV)/usr/local/bin
	cp $< $@


ifndef TERM_PROGRAM
dist-guard: dist
else
dist-guard:
	$(error Not building outside of development environment. Please re-run command inside VM/docker)
endif

# the patsubst/shell combination allows for two directories to stay in sync
# between the source and build directory when combined with the above wildcard
# rules for doc/ and conf/
dist: $(DUOAUTHPROXY_ARTIFACT) $(AUTHPROXY_BUILD_ENV)/install $(TAPFILE_ARTIFACT)
dist: $(patsubst %,$(AUTHPROXY_BUILD_ENV)/%,$(shell find doc -type f))
dist: $(patsubst %,$(AUTHPROXY_BUILD_ENV)/%,$(shell find conf -type f))
dist: $(patsubst %,$(AUTHPROXY_BUILD_ENV)/usr/local/bin/%,$(SCRIPT_ARTIFACTS))
dist: | $(AUTHPROXY_BUILD_ENV)/conf $(AUTHPROXY_BUILD_ENV)/run
dist: | $(AUTHPROXY_BUILD_ENV)/bin
dist: | $(AUTHPROXY_BUILD_ENV)/log


.PHONY: clean
clean:
	- pushd $(LDAPTOR) && \
	$(ENV_PYTHON) setup.py clean -a && \
	popd

	- pushd $(PYRAD) && \
	$(ENV_PYTHON) setup.py clean -a && \
	popd

	# twisted-connect-proxy depends on twisted
	# which isn't available if we run setup.py clean
	# just remove the build directory ourselves
	- pushd $(TWISTEDCONNECTPROXY) && \
	$(ENV_PYTHON) setup.py clean -a && \
	popd

	- pushd $(AUTHPROXY) && \
	$(ENV_PYTHON) setup.py clean -a && \
	popd

	- pushd $(PYTHON_DIR) && \
	rm -f *.pyc *.pyo *~ && \
	rm -f .regen && \
	$(MAKE) distclean && \
	rm -f Makefile && \
	rm -f Makefile.pre && \
	rm -f config.status && \
	popd

	- pushd $(DRPC) && \
	$(ENV_PYTHON) setup.py clean -a && \
	popd

	- rm -rf $(AUTHPROXY_BUILD_ENV)

.PHONY: test audits dlint-audits integrations

UTESTS=test_duoauthproxy
test: dist third_party_test
	pushd $(AUTHPROXY) ; \
	PYTHONPATH=$(AUTHPROXY_BUILD_ENV) $(ENV_PYTHON) -m pytest -rA $(UTESTS) --random-order \
		--junit-xml=reports/pytest.xml || exit 1; \
	PYTHONPATH=$(AUTHPROXY_BUILD_ENV) $(ENV_PYTHON) -m pytest -rA -p test_duoauthproxy.plugins.fips \
		$(UTESTS) --random-order --junit-xml=reports/pytest_fips.xml --junit-prefix=fips|| exit 1 ; \
	popd


# Copy over test packages when running tests from sdist.
# We should only ever run this _after_ the sdist has been created, so that these aren't included in our prod tgz
copy-test-pkgs:
	cp ../third-party/$(ENTRYPOINTS_VER)-py2.py3-none-any.whl pkgs/
	cp ../third-party/$(MCCABE_VER)-py2.py3-none-any.whl pkgs/
	cp ../third-party/$(PYCODESTYLE_VER)-py2.py3-none-any.whl pkgs/
	cp ../third-party/$(PYFLAKES_VER)-py2.py3-none-any.whl pkgs/
	cp ../third-party/$(FLAKE8_VER)-py2.py3-none-any.whl pkgs/
	cp ../third-party/$(DLINT_VER)-py2.py3-none-any.whl pkgs/
	cp ../third-party/$(INICONFIG_VER)-py3-none-any.whl pkgs/
	cp ../third-party/$(PACKAGING_VER)-py3-none-any.whl pkgs/
	cp ../third-party/$(MORE_ITERTOOLS_VER)-py3-none-any.whl pkgs/
	cp ../third-party/$(PLUGGY_VER)-py2.py3-none-any.whl pkgs/
	cp ../third-party/$(WCWIDTH_VER)-py2.py3-none-any.whl pkgs/
	cp ../third-party/$(PYTEST_VER)-py3-none-any.whl pkgs/
	cp ../third-party/$(PYTEST_RANDOM_ORDER_VER)-py3-none-any.whl pkgs/
	cp ../third-party/$(BEHAVE_VER)-py2.py3-none-any.whl pkgs/
	cp ../third-party/$(COLORAMA_VER)-py2.py3-none-any.whl pkgs/
	cp ../third-party/$(PARSE_TYPE_VER)-py2.py3-none-any.whl pkgs/
	cp ../third-party/$(PARSE_VER)-py2.py3-none-any.whl pkgs/

dlint-audits: copy-test-pkgs third_party_test
	# Run the Dlint audits from the src directory
	cd .. && \
	$(ENV_PYTHON) -m flake8 --select="DUO" duoauthproxy/

ci-unittests: copy-test-pkgs third_party_test
	# These need to be one command so that the exit status of the first command
	# can be used as the exit status at the end of the subshell. Otherwise the
	# first line would fail and the rest of the command wouldn't be processed
	$(MAKE) test ;\
	TEST_EXIT_CODE=$$?;\
	cp -R pkgs/duoauthproxy/reports ../;\
	exit $$TEST_EXIT_CODE

integrations: all copy-test-pkgs third_party_test
# copy over test for integrations
	cp -R ../test_duoauthproxy pkgs/$(DUOAUTHPROXY_VER)/test_duoauthproxy
	# Run the behave tests from the src directory
	cd .. && \
	$(AUTHPROXY_BUILD_ENV)/usr/local/bin/behave test_duoauthproxy/features --tags=-windows --tags=-non_batched --junit  --junit-directory ./reports
