# Compatibility with GNU/Linux [i.e. Debian] based distros UNAME_OS_GNU := $(shell if uname -o | grep -q "GNU/Linux" ; then echo true; else echo false; fi) DISTRO_DEBIAN := $(shell if [ -e /etc/debian_version ] ; then echo true; else echo false; fi) IS_DEBIAN=false DISTRO_DEBIAN_VER_8=false ifeq ($(UNAME_OS_GNU),true) ifeq ($(DISTRO_DEBIAN),true) IS_DEBIAN=true DISTRO_DEBIAN_VER_8 := $(shell if grep -q -i "^8\|jessie" /etc/debian_version ; then echo true; else echo false; fi) endif endif ifeq ($(IS_DEBIAN),true) EXTRA_SETUP_OPTS="--install-layout=deb" endif # Check for systemd presence ifeq ($(SYSTEMCTL_OVERRIDE),true) IS_SYSTEMCTL=true else ifeq ($(SYSTEMCTL_OVERRIDE),false) IS_SYSTEMCTL=false else IS_SYSTEMCTL = $(shell if [ -d /run/systemd/system ] || [ -d /var/run/systemd/system ] ; then echo true ; else echo false; fi) endif endif # VARIABLES OVERRIDABLE FROM OUTSIDE # ================================== ifndef PYTHON # some distros ship python3 as python PYTHON := $(shell which python3 || which python) endif # PYTHON_SITELIB is a path (relative to DESTDIR, e.g. # /usr/local/lib/python3.7/site-packages) where the command # `python setup.py install` puts pcs python files. The reasons to know the path # in makefile are that: # 1) There is a need to modify .../pcs/settings.py after installation (for # debian) and regenerate .pyc file aftermath. # 2) It is needed remove pcs directory from PYTHON_SITELIB after installation ifndef PYTHON_SITELIB # USE_PYTHON_PLATLIB is a flag that instructs installation process to use # platlib (e.g. /usr/local/lib64/python3.7/site-packages) instead of pureleb # (e.g. /usr/local/lib/python3.7/site-packages) as default value for # PYTHON_SITELIB. .../lib is preferred over .../lib64 because of hardcoded # path in pcs/settings.py (more in rhel specfile). ifeq ($(USE_PYTHON_PLATLIB), true) PYTHON_SITELIB=$(shell $(PYTHON) setup.py platlib | tail --lines=1) else PYTHON_SITELIB=$(shell $(PYTHON) setup.py purelib | tail --lines=1) endif endif # Check for an override for building gems ifndef BUILD_GEMS BUILD_GEMS=true endif ifndef PREFIX PREFIX=$(shell prefix=`$(PYTHON) -c "import sys; print(sys.prefix)"` || prefix="/usr"; echo $$prefix) endif ifndef SYSTEMD_DIR SYSTEMD_DIR=/usr/lib/systemd endif ifndef SYSTEMD_UNIT_DIR SYSTEMD_UNIT_DIR=${SYSTEMD_DIR}/system endif ifndef INIT_DIR INIT_DIR=/etc/init.d endif ifndef BASH_COMPLETION_DIR BASH_COMPLETION_DIR=/etc/bash_completion.d endif ifndef CONF_DIR ifeq ($(IS_DEBIAN),true) CONF_DIR = /etc/default else CONF_DIR = /etc/sysconfig endif endif ifndef SYSTEMD_SERVICE_FILE ifeq ($(IS_DEBIAN),true) SYSTEMD_SERVICE_FILE = pcsd/pcsd.service.debian else SYSTEMD_SERVICE_FILE = pcsd/pcsd.service endif endif ifndef LIB_DIR ifeq ($(IS_DEBIAN),true) LIB_DIR = /usr/share else LIB_DIR = ${PREFIX}/lib endif endif ifndef BUNDLE_LOCAL_DIR BUNDLE_LOCAL_DIR=./pcs/bundled/ endif ifndef SNMP_MIB_DIR SNMP_MIB_DIR=/share/snmp/mibs/ endif # INSTALLATION FINE DETAIL CONTROLL # ================================= # `BUNDLE_INSTALL_PYAGENTX=false` # to disable the default automatic pyagentx instalation # `BUNDLE_PYAGENTX_SRC_DIR=/path/to/pyagentx/sources` # to install pyagentx from the given location instead of using default # location for downloading sources and an installation # `BUNDLE_TORNADO_SRC_DIR=/path/to/tornado/sources` # to install tornado from specified location (tornado is not installed by # default) # `BUNDLE_DATACLASSES_SRC_DIR=/path/to/dataclasses/sources` # to install dataclasses from specified location (dataclasses are not # installed by default) # `BUNDLE_DACITE_SRC_DIR=/path/to/dacite/sources` # to install dacite from specified location (dacite are not installed by # default) BUNDLE_PYAGENTX_VERSION="0.4.pcs.2" BUNDLE_PYAGENTX_URI="https://github.com/ondrejmular/pyagentx/archive/v${BUNDLE_PYAGENTX_VERSION}.tar.gz" ifndef BUNDLE_INSTALL_PYAGENTX BUNDLE_INSTALL_PYAGENTX=true endif BUNDLE_PYAGENTX_SRC_DOWNLOAD=false ifndef BUNDLE_PYAGENTX_SRC_DIR BUNDLE_PYAGENTX_SRC_DOWNLOAD=true endif ifneq ($(BUNDLE_INSTALL_PYAGENTX),true) BUNDLE_PYAGENTX_SRC_DOWNLOAD=false endif # There is BUNDLE_TO_INSTALL when BUNDLE_INSTALL_PYAGENTX is true or any of # BUNDLE_TORNADO_SRC_DIR, BUNDLE_DATACLASSES_SRC_DIR or BUNDLE_DACITE_SRC_DIR # is specified BUNDLE_TO_INSTALL=false ifeq ($(BUNDLE_INSTALL_PYAGENTX), true) BUNDLE_TO_INSTALL=true endif ifneq ($(and $(BUNDLE_TORNADO_SRC_DIR),$(BUNDLE_DATACLASSES_SRC_DIR),$(BUNDLE_DACITE_SRC_DIR)),) BUNDLE_TO_INSTALL=true endif # DESTINATION DIRS # ================ DEST_PYTHON_SITELIB = ${DESTDIR}${PYTHON_SITELIB} DEST_PYTHON_SCRIPT_DIR=${DESTDIR}$(shell $(PYTHON) setup.py scriptdir | tail --lines=1) DEST_MAN=${DESTDIR}/usr/share/man/man8 DEST_SYSTEMD_SYSTEM = ${DESTDIR}${SYSTEMD_UNIT_DIR} DEST_INIT = ${DESTDIR}${INIT_DIR} DEST_BASH_COMPLETION = ${DESTDIR}${BASH_COMPLETION_DIR} DEST_CONF = ${DESTDIR}${CONF_DIR} DEST_LIB = ${DESTDIR}${LIB_DIR} DEST_PREFIX = ${DESTDIR}${PREFIX} DEST_BUNDLE_LIB=${DEST_LIB}/pcs/bundled DEST_BUNDLE_LOCAL=$(shell readlink -f ${BUNDLE_LOCAL_DIR}) DEST_SNMP_MIB=${DEST_PREFIX}${SNMP_MIB_DIR} # OTHER # ===== pcsd_fonts = \ LiberationSans-Regular.ttf;LiberationSans:style=Regular \ LiberationSans-Bold.ttf;LiberationSans:style=Bold \ LiberationSans-BoldItalic.ttf;LiberationSans:style=BoldItalic \ LiberationSans-Italic.ttf;LiberationSans:style=Italic \ Overpass-Regular.ttf;Overpass:style=Regular \ Overpass-Bold.ttf;Overpass:style=Bold # 1 - an alternative file # 2 - a file which will be replaced by the alternative file define use-alternative-file rm -f $(2) install -m644 $(1) $(2) endef # 1 - sources directory - with python package sources # 2 - destination directory - python package will be installed into the # `packages` subdirectory of this destination directory define build_python_bundle cd $(1) && \ PYTHONPATH=$(2)/packages/ \ LC_ALL=C.utf8 \ $(PYTHON) setup.py install --install-lib /packages/ --root $(2) endef # TARGETS # ======= bundle_pyagentx: ifeq ($(BUNDLE_PYAGENTX_SRC_DOWNLOAD),true) mkdir -p ${DEST_BUNDLE_LOCAL}/src $(eval BUNDLE_PYAGENTX_SRC_DIR=${DEST_BUNDLE_LOCAL}/src/pyagentx-${BUNDLE_PYAGENTX_VERSION}) rm -rf ${BUNDLE_PYAGENTX_SRC_DIR} wget -qO- ${BUNDLE_PYAGENTX_URI} | tar xvz -C ${DEST_BUNDLE_LOCAL}/src endif ifeq ($(BUNDLE_INSTALL_PYAGENTX),true) $(call build_python_bundle,${BUNDLE_PYAGENTX_SRC_DIR},$(PYAGENTX_LIB_DIR)) endif ifeq ($(BUNDLE_PYAGENTX_SRC_DOWNLOAD),true) rm -rf ${BUNDLE_PYAGENTX_SRC_DIR} endif install_bundled_libs: ifeq ($(BUNDLE_TO_INSTALL),true) install -d ${DEST_BUNDLE_LIB} endif ifdef BUNDLE_TORNADO_SRC_DIR $(call build_python_bundle,${BUNDLE_TORNADO_SRC_DIR},${DEST_BUNDLE_LIB}) endif ifdef BUNDLE_DATACLASSES_SRC_DIR $(call build_python_bundle,${BUNDLE_DATACLASSES_SRC_DIR},${DEST_BUNDLE_LIB}) endif ifdef BUNDLE_DACITE_SRC_DIR $(call build_python_bundle,${BUNDLE_DACITE_SRC_DIR},${DEST_BUNDLE_LIB}) endif $(MAKE) PYAGENTX_LIB_DIR=$(DEST_BUNDLE_LIB) bundle_pyagentx install_python_part: install_bundled_libs # make Python interpreter execution sane (via -Es flags) printf "[build]\nexecutable = $(PYTHON) -Es\n" > setup.cfg $(PYTHON) setup.py install --root=$(or ${DESTDIR}, /) ${EXTRA_SETUP_OPTS} # fix excessive script interpreting "executable" quoting with old setuptools: # https://github.com/pypa/setuptools/issues/188 # https://bugzilla.redhat.com/1353934 sed -i '1s|^\(#!\)"\(.*\)"$$|\1\2|' ${DEST_PYTHON_SCRIPT_DIR}/pcs sed -i '1s|^\(#!\)"\(.*\)"$$|\1\2|' ${DEST_PYTHON_SCRIPT_DIR}/pcs_snmp_agent sed -i '1s|^\(#!\)"\(.*\)"$$|\1\2|' ${DEST_PYTHON_SCRIPT_DIR}/pcs_internal rm setup.cfg mkdir -p ${DEST_PREFIX}/sbin/ mv ${DEST_PYTHON_SCRIPT_DIR}/pcs ${DEST_PREFIX}/sbin/pcs mv ${DEST_PYTHON_SCRIPT_DIR}/pcsd ${DEST_PREFIX}/sbin/pcsd install -D -m644 pcs/bash_completion ${DEST_BASH_COMPLETION}/pcs install -m644 -D pcs/pcs.8 ${DEST_MAN}/pcs.8 # pcs_internal mkdir -p ${DEST_LIB}/pcs/ mv ${DEST_PYTHON_SCRIPT_DIR}/pcs_internal ${DEST_LIB}/pcs/pcs_internal # pcs SNMP install mv ${DEST_PYTHON_SCRIPT_DIR}/pcs_snmp_agent ${DEST_LIB}/pcs/pcs_snmp_agent install -d ${DEST_SNMP_MIB} install -m 644 pcs/snmp/mibs/PCMK-PCS*-MIB.txt ${DEST_SNMP_MIB} install -m 644 -D pcs/snmp/pcs_snmp_agent.conf ${DEST_CONF}/pcs_snmp_agent install -m 644 -D pcs/snmp/pcs_snmp_agent.8 ${DEST_MAN}/pcs_snmp_agent.8 ifeq ($(IS_DEBIAN),true) $(call use-alternative-file,pcs/settings.py.debian,${DEST_PYTHON_SITELIB}/pcs/settings.py) endif $(PYTHON) -m compileall -fl ${DEST_PYTHON_SITELIB}/pcs/settings.py ifeq ($(IS_SYSTEMCTL),true) install -d ${DEST_SYSTEMD_SYSTEM} install -m 644 pcs/snmp/pcs_snmp_agent.service ${DEST_SYSTEMD_SYSTEM} endif install: install_python_part ifeq ($(BUILD_GEMS),true) make -C pcsd build_gems endif install -d -m 700 ${DESTDIR}/var/log/pcsd mkdir -p ${DEST_LIB}/pcsd/ cp -r pcsd ${DEST_LIB} install -m 644 -D pcsd/pcsd.conf ${DEST_CONF}/pcsd install -d ${DESTDIR}/etc/pam.d install -m 644 pcsd/pcsd.pam ${DESTDIR}/etc/pam.d/pcsd ifeq ($(IS_DEBIAN),true) $(call use-alternative-file,pcsd/settings.rb.debian,${DEST_LIB}/pcsd/settings.rb) endif ifeq ($(IS_DEBIAN)$(IS_SYSTEMCTL),truefalse) install -m 755 -D pcsd/pcsd.debian ${DEST_INIT}/pcsd else install -d ${DEST_SYSTEMD_SYSTEM} install -m 644 ${SYSTEMD_SERVICE_FILE} ${DEST_SYSTEMD_SYSTEM}/pcsd.service install -m 644 pcsd/pcsd-ruby.service ${DEST_SYSTEMD_SYSTEM}/pcsd-ruby.service endif # ${DEST_LIB}/pcsd/pcsd holds the selinux context install -m 755 pcsd/pcsd.service-runner ${DEST_LIB}/pcsd/pcsd rm ${DEST_LIB}/pcsd/pcsd.service-runner install -m 700 -d ${DESTDIR}/var/lib/pcsd install -m 644 -D pcsd/pcsd.logrotate ${DESTDIR}/etc/logrotate.d/pcsd install -m644 -D pcsd/pcsd.8 ${DEST_MAN}/pcsd.8 $(foreach font,$(pcsd_fonts),\ $(eval font_file = $(word 1,$(subst ;, ,$(font)))) \ $(eval font_def = $(word 2,$(subst ;, ,$(font)))) \ $(eval font_path = $(shell fc-match '--format=%{file}' '$(font_def)')) \ $(if $(font_path),ln -s -f $(font_path) ${DEST_LIB}/pcsd/public/css/$(font_file);,$(error Font $(font_def) not found)) \ ) # For running pcs_snmp_agent from a local (git clone) directory (without full # pcs installation) it is necessary to have pyagentx installed in expected # location inside the local directory. bundle_pyagentx_local: $(MAKE) PYAGENTX_LIB_DIR=$(DEST_BUNDLE_LOCAL) bundle_pyagentx uninstall: rm -f ${DEST_PREFIX}/sbin/pcs rm -rf ${DEST_PYTHON_SITELIB}/pcs rm -rf ${DEST_LIB}/pcsd rm -rf ${DEST_LIB}/pcs ifeq ($(IS_DEBIAN)$(IS_SYSTEMCTL),truefalse) rm -f ${DEST_INIT}/pcsd else rm -f ${DEST_SYSTEMD_SYSTEM}/pcsd.service rm -f ${DEST_SYSTEMD_SYSTEM}/pcsd-ruby.service rm -f ${DEST_SYSTEMD_SYSTEM}/pcs_snmp_agent.service endif rm -f ${DESTDIR}/etc/pam.d/pcsd rm -rf ${DESTDIR}/var/lib/pcsd rm -f ${DEST_SNMP_MIB}/PCMK-PCS*-MIB.txt newversion: $(PYTHON) newversion.py # CODE QUALITY # =========== install_pip: requirements.txt $(PYTHON) -m pip install --upgrade -r $< pylint_requirements: install_pip pylint: time $(PYTHON) -m pylint --rcfile pylintrc --persistent=n --reports=n --score=n --disable similarities pcs pcs_test get_lxml_stubs: mkdir -p $(BUNDLE_LOCAL_DIR)/stubs git clone https://github.com/JelleZijlstra/lxml-stubs.git $(BUNDLE_LOCAL_DIR)/stubs mypy_requirements: install_pip bundle_pyagentx_local get_lxml_stubs mypy: time $(PYTHON) -m mypy -p pcs python_static_code_analysis_reqirements: pylint_requirements mypy_requirements python_static_code_analysis: mypy pylint BLACK_CMD = black --config pyproject.toml black_check: $(BLACK_CMD) --check . black: $(BLACK_CMD) . python_tests: python_tests_tier0 python_tests_tier1 python_tests_tier0: $(PYTHON) pcs_test/suite.py --tier0 python_tests_tier1: $(PYTHON) pcs_test/suite.py --tier1 tests: python_tests check: black_check python_static_code_analysis tests # RPM BUILD # ========= RPM_BUILD_DIR = rpm_build SPEC = pcs.spec GIT_COMMIT_HASH := $(shell git rev-parse HEAD) ifndef GIT_TAG ifeq ($(shell git describe --tag --exact-match 2>&1 > /dev/null; echo $$?),0) GIT_TAG := $(shell git describe --tag --exact-match) endif endif GIT_LAST_TAG := $(strip $(shell git describe --abbrev=0 --tags)) ifndef GIT_TAG DIST_VERSION_NAME := $(GIT_COMMIT_HASH) else DIST_VERSION_NAME := $(GIT_TAG) GIT_LAST_TAG := $(GIT_TAG) endif DIST_NAME := pcs-$(DIST_VERSION_NAME) DIST_ARCHIVE_NAME := $(DIST_NAME).tar.gz RPMBUILDOPTS = --define "_sourcedir $(PWD)/$(RPM_BUILD_DIR)" \ --define "_specdir $(PWD)/$(RPM_BUILD_DIR)" \ --define "_builddir $(PWD)/$(RPM_BUILD_DIR)" \ --define "_srcrpmdir $(PWD)/$(RPM_BUILD_DIR)" \ --define "_rpmdir $(PWD)/$(RPM_BUILD_DIR)" BUNDLE_CONGIG_FILE := $(RPM_BUILD_DIR)/pcsd-bundle-config $(RPM_BUILD_DIR): mkdir -p $@ clean: $(PYTHON) setup.py clean rm -rf $(RPM_BUILD_DIR) rm -f $(SPEC) rm -rf $(BUNDLE_LOCAL_DIR) rm -f pcs-*.tar.gz dist: clean rm -rf /tmp/$(DIST_NAME) mkdir -p /tmp/$(DIST_NAME) cp -r . /tmp/$(DIST_NAME) tar -zcf $(DIST_ARCHIVE_NAME) -C /tmp $(DIST_NAME) rm -rf /tmp/$(DIST_NAME) $(BUNDLE_CONGIG_FILE): $(RPM_BUILD_DIR) rm -f $@ echo '---' >> $@ echo 'BUNDLE_FROZEN: "true"' >> $@ echo 'BUNDLE_PATH: "vendor/bundle"' >> $@ echo 'BUNDLE_DISABLE_SHARED_GEMS: "true"' >> $@ echo "BUNDLE_BUILD: \"--with-ldflags='-Wl,-z,now -Wl,-z,relro'\"" >> $@ $(SPEC): $(SPEC).in rm -f $@-t $@ date="$(shell LC_ALL=C date "+%a %b %d %Y")" && \ gitversion="$(GIT_LAST_TAG)" && \ numcommit=`git rev-list $$gitversion..HEAD | wc -l` && \ gitcommit="$(GIT_COMMIT_HASH)" && \ sed \ -e "s#@VERSION@#$$gitversion#g" \ -e "s#@NUMCOMMIT@#$$numcommit#g" \ -e "s#@COMMIT@#$$gitcommit#g" \ -e "s#@DATE@#$$date#g" \ $< > $@-t; \ chmod a-w $@-t mv $@-t $@ sources: dist $(SPEC) $(RPM_BUILD_DIR) cd $(RPM_BUILD_DIR) && \ cp ../$(DIST_ARCHIVE_NAME) $(DIST_ARCHIVE_NAME) && \ cp ../$(SPEC) $(SPEC) && \ spectool -S $(SPEC) | sed -En "s/^[^ ]+ (.*)$$/\1/p" | grep "^http.*" | xargs -n 1 curl -OL $(MAKE) $(BUNDLE_CONGIG_FILE) srpm: sources cd $(RPM_BUILD_DIR) && \ rpmbuild $(RPMBUILDOPTS) --nodeps -bs $(SPEC) rpm: sources cd $(RPM_BUILD_DIR) && \ rpmbuild $(RPMBUILDOPTS) -ba $(SPEC)