| # | |
| # Copyright (C) 2008 Andrew Beekhof | |
| # | |
| # This program is free software; you can redistribute it and/or | |
| # modify it under the terms of the GNU General Public License | |
| # as published by the Free Software Foundation; either version 2 | |
| # of the License, or (at your option) any later version. | |
| # | |
| # This program is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU General Public License | |
| # along with this program; if not, write to the Free Software | |
| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
| # | |
| default: $(shell test ! -e configure && echo init) $(shell test -e configure && echo core) | |
| # The toplevel "clean" targets are generated from Makefile.am, not this file. | |
| # We can't use autotools' CLEANFILES, clean-local, etc. here. Instead, we | |
| # define this target, which Makefile.am can use as a dependency of clean-local. | |
| EXTRA_CLEAN_TARGETS = ancillary-clean | |
| -include Makefile | |
| # The main purpose of this GNUmakefile is that its targets can be invoked | |
| # without having to call autogen.sh and configure first. That means automake | |
| # variables may or may not be defined. Here, we use the current working | |
| # directory if a relevant variable hasn't been defined. | |
| # | |
| # The idea is to keep generated artifacts in the build tree, in case a VPATH | |
| # build is in use, but in practice it would be difficult to make the targets | |
| # here usable from a different location than the source tree. | |
| abs_srcdir ?= $(shell pwd) | |
| abs_builddir ?= $(shell pwd) | |
| PACKAGE ?= pacemaker | |
| # Definitions that specify what various targets will apply to | |
| COMMIT ?= HEAD | |
| # TAG defaults to DIST when not in a git checkout (e.g. from a distribution), | |
| # the tag name if COMMIT is tagged, and the full commit ID otherwise. | |
| TAG ?= $(shell T=$$(git describe --tags --exact-match '$(COMMIT)' 2>/dev/null); \ | |
| test -n "$${T}" && echo "$${T}" \ | |
| || git log --pretty=format:%H -n 1 '$(COMMIT)' 2>/dev/null || echo DIST) | |
| lparen = ( | |
| rparen = ) | |
| # SPEC_COMMIT is identical to TAG for DIST and tagged releases, otherwise it is | |
| # the short commit ID (which must be used in order for "make export" to use the | |
| # same archive name as "make dist") | |
| SPEC_COMMIT ?= $(shell \ | |
| case $(TAG) in \ | |
| Pacemaker-*|DIST$(rparen) \ | |
| echo '$(TAG)' ;; \ | |
| *$(rparen) \ | |
| git log --pretty=format:%h -n 1 '$(TAG)';; \ | |
| esac) | |
| SPEC_ABBREV = $(shell printf %s '$(SPEC_COMMIT)' | wc -c) | |
| LAST_RC ?= $(shell test -e /Volumes || git tag -l | grep Pacemaker | sort -Vr | grep rc | head -n 1) | |
| ifneq ($(origin VERSION), undefined) | |
| LAST_RELEASE ?= Pacemaker-$(VERSION) | |
| else | |
| LAST_RELEASE ?= $(shell git tag -l | grep Pacemaker | sort -Vr | grep -v rc | head -n 1) | |
| endif | |
| NEXT_RELEASE ?= $(shell echo $(LAST_RELEASE) | awk -F. '/[0-9]+\./{$$3+=1;OFS=".";print $$1,$$2,$$3}') | |
| # This Makefile can create 2 types of distributions: | |
| # | |
| # - "make dist" is automake's native functionality, based on the various | |
| # dist/nodist make variables; it always uses the current sources | |
| # | |
| # - "make export" is a custom target based on git archive and relevant entries | |
| # from .gitattributes; it defaults to current sources but can use any git tag | |
| # | |
| # Both types use the TARFILE name for the result, though they generate | |
| # different contents. | |
| # | |
| # The directory is named pacemaker-DIST when not in a git checkout (e.g. | |
| # from a distribution itself), pacemaker-<version_part_of_tag> for tagged | |
| # commits, and pacemaker-<short_commit> otherwise. | |
| distdir = $(PACKAGE)-$(shell \ | |
| case $(TAG) in \ | |
| DIST$(rparen) \ | |
| echo DIST;; \ | |
| Pacemaker-*$(rparen) \ | |
| echo '$(TAG)' | cut -c11-;; \ | |
| *$(rparen) \ | |
| git log --pretty=format:%h -n 1 '$(TAG)';; \ | |
| esac) | |
| TARFILE = $(abs_builddir)/$(distdir).tar.gz | |
| init: | |
| ./autogen.sh init | |
| # @TODO This should probably be what init does | |
| .PHONY: init-if-needed | |
| init-if-needed: | |
| test -e configure || ./autogen.sh init | |
| test -e Makefile || ./configure | |
| export: | |
| if [ ! -f "$(TARFILE)" ]; then \ | |
| if [ $(TAG) = dirty ]; then \ | |
| git commit -m "DO-NOT-PUSH" -a; \ | |
| git archive --prefix=$(distdir)/ -o "$(TARFILE)" HEAD^{tree}; \ | |
| git reset --mixed HEAD^; \ | |
| else \ | |
| git archive --prefix=$(distdir)/ -o "$(TARFILE)" $(TAG)^{tree}; \ | |
| fi; \ | |
| echo "`date`: Rebuilt $(TARFILE)"; \ | |
| else \ | |
| echo "`date`: Using existing tarball: $(TARFILE)"; \ | |
| fi | |
| ## RPM-related targets | |
| # Where to put RPM artifacts; possible values: | |
| # | |
| # - toplevel (default): RPM sources, spec, and source rpm in top-level build | |
| # directory (everything else uses the usual defaults) | |
| # | |
| # - subtree: RPM sources (i.e. TARFILE) in top-level build directory, | |
| # everything else in dedicated "rpm" subdirectory of build tree | |
| RPMDEST ?= toplevel | |
| RPM_SPEC_DIR_toplevel = $(abs_builddir) | |
| RPM_SRCRPM_DIR_toplevel = $(abs_builddir) | |
| RPM_OPTS_toplevel = --define "_sourcedir $(abs_builddir)" \ | |
| --define "_specdir $(RPM_SPEC_DIR_toplevel)" \ | |
| --define "_srcrpmdir $(RPM_SRCRPM_DIR_toplevel)" | |
| RPM_SPEC_DIR_subtree = $(abs_builddir)/rpm/SPECS | |
| RPM_SRCRPM_DIR_subtree = $(abs_builddir)/rpm/SRPMS | |
| RPM_OPTS_subtree = --define "_sourcedir $(abs_builddir)" \ | |
| --define "_topdir $(abs_builddir)/rpm" | |
| RPM_SPEC_DIR = $(RPM_SPEC_DIR_$(RPMDEST)) | |
| RPM_SRCRPM_DIR = $(RPM_SRCRPM_DIR_$(RPMDEST)) | |
| RPM_OPTS = $(RPM_OPTS_$(RPMDEST)) | |
| WITH ?= --without doc | |
| BUILD_COUNTER ?= build.counter | |
| LAST_COUNT = $(shell test ! -e $(BUILD_COUNTER) && echo 0; test -e $(BUILD_COUNTER) && cat $(BUILD_COUNTER)) | |
| COUNT = $(shell expr 1 + $(LAST_COUNT)) | |
| SPECVERSION ?= $(COUNT) | |
| MOCK_DIR = $(abs_builddir)/mock | |
| MOCK_OPTIONS ?= --resultdir=$(MOCK_DIR) --no-cleanup-after | |
| F ?= $(shell test ! -e /etc/fedora-release && echo 0; test -e /etc/fedora-release && rpm --eval %{fedora}) | |
| ARCH ?= $(shell test ! -e /etc/fedora-release && uname -m; test -e /etc/fedora-release && rpm --eval %{_arch}) | |
| MOCK_CFG ?= $(shell test -e /etc/fedora-release && echo fedora-$(F)-$(ARCH)) | |
| # toplevel rsync destination for www targets (without trailing slash) | |
| RSYNC_DEST ?= root@www.clusterlabs.org:/var/www/html | |
| # recursive, preserve symlinks/permissions/times, verbose, compress, | |
| # don't cross filesystems, sparse, show progress | |
| RSYNC_OPTS = -rlptvzxS --progress | |
| # rpmbuild wrapper that translates "--with[out] FEATURE" into RPM macros | |
| # | |
| # Unfortunately, at least recent versions of rpm do not support mentioned | |
| # switch. To work this around, we can emulate mechanism that rpm uses | |
| # internally: unfold the flags into respective macro definitions: | |
| # | |
| # --with[out] FOO -> --define "_with[out]_FOO --with[out]-FOO" | |
| # | |
| # $(1) ... WITH string (e.g., --with pre_release --without cman) | |
| # $(2) ... options following the initial "rpmbuild" in the command | |
| # $(3) ... final arguments determined with $2 (e.g., pacemaker.spec) | |
| # | |
| # Note that if $(3) is a specfile, extra case is taken so as to reflect | |
| # pcmkversion correctly (using in-place modification). | |
| # | |
| # Also note that both ways to specify long option with an argument | |
| # (i.e., what getopt and, importantly, rpm itself support) can be used: | |
| # | |
| # --with FOO | |
| # --with=FOO | |
| rpmbuild-with = \ | |
| WITH=$$(getopt -o "" -l with:,without: -- $(1)) || exit 1; \ | |
| CMD='rpmbuild $(2)'; PREREL=0; \ | |
| eval set -- "$${WITH}"; \ | |
| while true; do \ | |
| case "$$1" in \ | |
| --with) CMD="$${CMD} --define \"_with_$$2 --with-$$2\""; \ | |
| [ "$$2" != pre_release ] || PREREL=1; shift 2;; \ | |
| --without) CMD="$${CMD} --define \"_without_$$2 --without-$$2\""; \ | |
| [ "$$2" != pre_release ] || PREREL=0; shift 2;; \ | |
| --) shift ; break ;; \ | |
| *) echo "cannot parse WITH: $$1"; exit 1;; \ | |
| esac; \ | |
| done; \ | |
| case "$(3)" in \ | |
| *.spec) { [ $${PREREL} -eq 0 ] || [ $(LAST_RELEASE) = $(TAG) ]; } \ | |
| && sed -i "s/^\(%global pcmkversion \).*/\1$$(echo $(LAST_RELEASE) | sed -e s:Pacemaker-:: -e s:-.*::)/" $(3) \ | |
| || sed -i "s/^\(%global pcmkversion \).*/\1$$(echo $(NEXT_RELEASE) | sed -e s:Pacemaker-:: -e s:-.*::)/" $(3);; \ | |
| esac; \ | |
| CMD="$${CMD} $(3)"; \ | |
| eval "$${CMD}" | |
| # Depend on spec-clean so it gets rebuilt every time | |
| $(RPM_SPEC_DIR)/$(PACKAGE).spec: spec-clean rpm/pacemaker.spec.in | |
| $(AM_V_at)$(MKDIR_P) $(RPM_SPEC_DIR) # might not exist in VPATH build | |
| $(AM_V_GEN)if [ x != x"`git ls-files -m rpm/pacemaker.spec.in 2>/dev/null`" ]; then \ | |
| cat $(abs_srcdir)/rpm/pacemaker.spec.in; \ | |
| elif git cat-file -e $(TAG):rpm/pacemaker.spec.in 2>/dev/null; then \ | |
| git show $(TAG):rpm/pacemaker.spec.in; \ | |
| elif git cat-file -e $(TAG):pacemaker.spec.in 2>/dev/null; then \ | |
| git show $(TAG):pacemaker.spec.in; \ | |
| else \ | |
| cat $(abs_srcdir)/rpm/pacemaker.spec.in; \ | |
| fi | sed \ | |
| -e 's/global\ specversion\ .*/global\ specversion\ $(SPECVERSION)/' \ | |
| -e 's/global\ commit\ .*/global\ commit\ $(SPEC_COMMIT)/' \ | |
| -e 's/global\ commit_abbrev\ .*/global\ commit_abbrev\ $(SPEC_ABBREV)/' \ | |
| -e "s/PACKAGE_DATE/$$(date +'%a %b %d %Y')/" \ | |
| -e "s/PACKAGE_VERSION/$$(git describe --tags $(TAG) | sed -e s:Pacemaker-:: -e s:-.*::)/" \ | |
| > "$@" | |
| .PHONY: $(PACKAGE).spec | |
| $(PACKAGE).spec: $(RPM_SPEC_DIR)/$(PACKAGE).spec | |
| .PHONY: spec-clean | |
| spec-clean: | |
| -rm -f $(RPM_SPEC_DIR)/$(PACKAGE).spec | |
| .PHONY: srpm | |
| srpm: export srpm-clean $(RPM_SPEC_DIR)/$(PACKAGE).spec | |
| if [ -e $(BUILD_COUNTER) ]; then \ | |
| echo $(COUNT) > $(BUILD_COUNTER); \ | |
| fi | |
| $(call rpmbuild-with,$(WITH),-bs $(RPM_OPTS),$(RPM_SPEC_DIR)/$(PACKAGE).spec) | |
| .PHONY: srpm-clean | |
| srpm-clean: | |
| -rm -f $(RPM_SRCRPM_DIR)/*.src.rpm | |
| .PHONY: chroot | |
| chroot: mock-$(MOCK_CFG) mock-install-$(MOCK_CFG) mock-sh-$(MOCK_CFG) | |
| @echo "Done" | |
| .PHONY: mock-next | |
| mock-next: | |
| $(MAKE) $(AM_MAKEFLAGS) F=$(shell expr 1 + $(F)) mock | |
| .PHONY: mock-rawhide | |
| mock-rawhide: | |
| $(MAKE) $(AM_MAKEFLAGS) F=rawhide mock | |
| mock-install-%: | |
| @echo "Installing packages" | |
| mock --root=$* $(MOCK_OPTIONS) --install $(MOCK_DIR)/*.rpm \ | |
| vi sudo valgrind lcov gdb fence-agents psmisc | |
| .PHONY: mock-install | |
| mock-install: mock-install-$(MOCK_CFG) | |
| @echo "Done" | |
| .PHONY: mock-sh | |
| mock-sh: mock-sh-$(MOCK_CFG) | |
| @echo "Done" | |
| mock-sh-%: | |
| @echo "Connecting" | |
| mock --root=$* $(MOCK_OPTIONS) --shell | |
| @echo "Done" | |
| mock-%: srpm mock-clean | |
| mock $(MOCK_OPTIONS) --root=$* --no-cleanup-after --rebuild \ | |
| $(WITH) $(RPM_SRCRPM_DIR)/*.src.rpm | |
| .PHONY: mock | |
| mock: mock-$(MOCK_CFG) | |
| @echo "Done" | |
| .PHONY: dirty | |
| dirty: | |
| $(MAKE) $(AM_MAKEFLAGS) TAG=dirty mock | |
| .PHONY: mock-clean | |
| mock-clean: | |
| -rm -rf $(MOCK_DIR) | |
| .PHONY: rpm-dep | |
| rpm-dep: $(RPM_SPEC_DIR)/$(PACKAGE).spec | |
| sudo yum-builddep $(PACKAGE).spec | |
| # e.g. make WITH="--with pre_release" rpm | |
| .PHONY: rpm | |
| rpm: srpm | |
| @echo To create custom builds, edit the flags and options in $(PACKAGE).spec first | |
| $(call rpmbuild-with,$(WITH),$(RPM_OPTS),--rebuild $(RPM_SRCRPM_DIR)/*.src.rpm) | |
| .PHONY: rpmlint | |
| rpmlint: $(RPM_SPEC_DIR)/$(PACKAGE).spec | |
| rpmlint -f rpm/rpmlintrc "$<" | |
| .PHONY: release | |
| release: | |
| $(MAKE) $(AM_MAKEFLAGS) TAG=$(LAST_RELEASE) rpm | |
| .PHONY: rc | |
| rc: | |
| $(MAKE) $(AM_MAKEFLAGS) TAG=$(LAST_RC) rpm | |
| ## Static analysis via coverity | |
| # Aggressiveness (low, medium, or high) | |
| COVLEVEL ?= low | |
| # Generated outputs | |
| COVERITY_DIR = $(abs_builddir)/coverity-$(TAG) | |
| COVTAR = $(abs_builddir)/$(PACKAGE)-coverity-$(TAG).tgz | |
| COVEMACS = $(abs_builddir)/$(TAG).coverity | |
| COVHTML = $(COVERITY_DIR)/output/errors | |
| # Coverity outputs are phony so they get rebuilt every invocation | |
| .PHONY: $(COVERITY_DIR) | |
| $(COVERITY_DIR): init-if-needed core-clean coverity-clean | |
| $(AM_V_GEN)cov-build --dir "$@" $(MAKE) $(AM_MAKEFLAGS) core | |
| # Public coverity instance | |
| .PHONY: $(COVTAR) | |
| $(COVTAR): $(COVERITY_DIR) | |
| $(AM_V_GEN)tar czf "$@" --transform="s@.*$(TAG)@cov-int@" "$<" | |
| .PHONY: coverity | |
| coverity: $(COVTAR) | |
| @echo "Now go to https://scan.coverity.com/users/sign_in and upload:" | |
| @echo " $(COVTAR)" | |
| @echo "then make core-clean coverity-clean" | |
| # Licensed coverity instance | |
| # | |
| # The prerequisites are a little hacky; rather than actually required, some | |
| # of them are designed so that things execute in the proper order (which is | |
| # not the same as GNU make's order-only prerequisites). | |
| .PHONY: coverity-analyze | |
| coverity-analyze: $(COVERITY_DIR) | |
| @echo "" | |
| @echo "Analyzing (waiting for coverity license if necessary) ..." | |
| cov-analyze --dir "$<" --wait-for-license --security \ | |
| --aggressiveness-level "$(COVLEVEL)" | |
| .PHONY: $(COVEMACS) | |
| $(COVEMACS): coverity-analyze | |
| $(AM_V_GEN)cov-format-errors --dir "$(COVERITY_DIR)" --emacs-style > "$@" | |
| .PHONY: $(COVHTML) | |
| $(COVHTML): $(COVEMACS) | |
| $(AM_V_GEN)cov-format-errors --dir "$(COVERITY_DIR)" --html-output "$@" | |
| .PHONY: coverity-corp | |
| coverity-corp: $(COVHTML) | |
| $(MAKE) $(AM_MAKEFLAGS) core-clean | |
| @echo "Done. See:" | |
| @echo " file://$(abs_builddir)/$(COVHTML)/index.html" | |
| @echo "When no longer needed, make coverity-clean" | |
| # Remove all outputs regardless of tag | |
| .PHONY: coverity-clean | |
| coverity-clean: | |
| -rm -rf "$(abs_builddir)"/coverity-* \ | |
| "$(abs_builddir)"/$(PACKAGE)-coverity-*.tgz \ | |
| "$(abs_builddir)"/*.coverity | |
| global: clean-generic | |
| gtags -q | |
| global-upload: global | |
| htags -sanhIT | |
| rsync $(RSYNC_OPTS) HTML/ "$(RSYNC_DEST)/$(PACKAGE)/global/$(TAG)/" | |
| %.8.html: %.8 | |
| echo groff -mandoc `man -w ./$<` -T html > $@ | |
| groff -mandoc `man -w ./$<` -T html > $@ | |
| rsync $(RSYNC_OPTS) "$@" "$(RSYNC_DEST)/$(PACKAGE)/man/" | |
| %.7.html: %.7 | |
| echo groff -mandoc `man -w ./$<` -T html > $@ | |
| groff -mandoc `man -w ./$<` -T html > $@ | |
| rsync $(RSYNC_OPTS) "$@" "$(RSYNC_DEST)/$(PACKAGE)/man/" | |
| manhtml-upload: all | |
| find . -name "[a-z]*.[78]" -exec make \{\}.html \; | |
| doxygen: Doxyfile | |
| doxygen Doxyfile | |
| doxygen-upload: doxygen | |
| rsync $(RSYNC_OPTS) doc/api/html/ "$(RSYNC_DEST)/$(PACKAGE)/doxygen/$(TAG)/" | |
| abi: | |
| ./abi-check pacemaker $(LAST_RELEASE) $(TAG) | |
| abi-www: | |
| export RSYNC_DEST=$(RSYNC_DEST); ./abi-check -u pacemaker $(LAST_RELEASE) $(TAG) | |
| www: manhtml-upload global-upload doxygen-upload | |
| make RSYNC_DEST=$(RSYNC_DEST) -C doc www | |
| ## Change log generation | |
| summary: | |
| @printf "\n* `date +"%a %b %d %Y"` `git config user.name` <`git config user.email`> $(NEXT_RELEASE)" | |
| @printf "\n- Changesets: `git log --pretty=oneline $(LAST_RELEASE)..HEAD | wc -l`" | |
| @printf "\n- Diff: " | |
| @git diff -r $(LAST_RELEASE)..HEAD --stat include lib mcp pengine/*.c pengine/*.h cib crmd fencing lrmd tools xml | tail -n 1 | |
| rc-changes: | |
| @$(MAKE) $(AM_MAKEFLAGS) NEXT_RELEASE=$(shell echo $(LAST_RC) | sed s:-rc.*::) LAST_RELEASE=$(LAST_RC) changes | |
| changes: summary | |
| @printf "\n- Features added since $(LAST_RELEASE)\n" | |
| @git log --pretty=format:' +%s' --abbrev-commit $(LAST_RELEASE)..HEAD | grep -e Feature: | sed -e 's@Feature:@@' | sort -uf | |
| @printf "\n- Changes since $(LAST_RELEASE)\n" | |
| @git log --pretty=format:' +%s' --abbrev-commit $(LAST_RELEASE)..HEAD | grep -e High: -e Fix: -e Bug | sed -e 's@Fix:@@' -e s@High:@@ -e s@Fencing:@fencing:@ -e 's@Bug@ Bug@' -e s@PE:@pengine:@ | sort -uf | |
| changelog: | |
| @$(MAKE) $(AM_MAKEFLAGS) changes > ChangeLog | |
| @printf "\n">> ChangeLog | |
| git show $(LAST_RELEASE):ChangeLog >> ChangeLog | |
| @echo -e "\033[1;35m -- Don't forget to run the bumplibs.sh script! --\033[0m" | |
| indent: | |
| find . -name "*.h" -exec ./p-indent \{\} \; | |
| find . -name "*.c" -exec ./p-indent \{\} \; | |
| git co HEAD crmd/fsa_proto.h lib/gnu | |
| rel-tags: tags | |
| find . -name TAGS -exec sed -i 's:\(.*\)/\(.*\)/TAGS:\2/TAGS:g' \{\} \; | |
| CLANG_analyzer = $(shell which scan-build) | |
| CLANG_checkers = | |
| # Extra cppcheck options: --enable=all --inconclusive --std=posix | |
| cppcheck: | |
| for d in replace lib mcp attrd pengine cib crmd fencing lrmd tools; do cppcheck -q $$d; done | |
| clang: | |
| test -e $(CLANG_analyzer) | |
| scan-build $(CLANG_checkers:%=-enable-checker %) $(MAKE) $(AM_MAKEFLAGS) clean all | |
| # V3 = scandir unsetenv alphasort xalloc | |
| # V2 = setenv strerror strchrnul strndup | |
| # http://www.gnu.org/software/gnulib/manual/html_node/Initial-import.html#Initial-import | |
| GNU_MODS = crypto/md5 | |
| gnulib-update: | |
| -test ! -e gnulib && git clone git://git.savannah.gnu.org/gnulib.git | |
| cd gnulib && git pull | |
| gnulib/gnulib-tool --source-base=lib/gnu --lgpl=2 --no-vc-files --import $(GNU_MODS) | |
| ancillary-clean: spec-clean srpm-clean mock-clean coverity-clean | |
| -rm -f $(TARFILE) |