All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Makefile, README: Break installation into a target for each mandir
@ 2021-06-04 20:53 Alejandro Colomar
  2021-06-07 16:49 ` [PATCH v19] " Alejandro Colomar
                   ` (8 more replies)
  0 siblings, 9 replies; 11+ messages in thread
From: Alejandro Colomar @ 2021-06-04 20:53 UTC (permalink / raw)
  To: mtk.manpages; +Cc: Alejandro Colomar, linux-man, Dr . Tobias Quathamer

Instead of having a monolithic 'make install', break it into
multiple targets such as 'make install-man3'.  This simplifies
packaging, for example in Debian, where they break this project
into several packages: 'manpages' and 'manpages-dev', each
containinng different mandirs.

The above allows for multithread installation: 'make -j8'

Also, don't overwrite files that don't need to be overwritten, by
having a target for files, which makes use of make's timestamp
comparison.

This allows much faster installation times (although, it's a bit
slower to do a full clean install).

Now that the installation overwrites target files when needed,
'make [all]' is aliased to 'make install', as an uninstall is
almost never needed (unless you specifically want to test a clean
install, in which case you can run 'make uninstall; make install').

Also,
I replaced = by ?=, because eventhough user input overrides any of
those, I don't trust make to not start a subshell for every
submake, in the cases where I invoke a shell.  I want the shell to
be invoked only in the main make process in variable assignments.

Signed-off-by: Alejandro Colomar <alx.manpages@gmail.com>
---
 Makefile | 217 +++++++++++++++++++++++++++++++++++++++++++------------
 README   |   8 +-
 2 files changed, 176 insertions(+), 49 deletions(-)

diff --git a/Makefile b/Makefile
index 78d953f3c..159b2fe78 100644
--- a/Makefile
+++ b/Makefile
@@ -1,28 +1,178 @@
 SHELL = /bin/bash -Eeuo pipefail
 
-# Do not print "Entering directory ..."
 MAKEFLAGS += --no-print-directory
+MAKEFLAGS += --silent
+
+htmlbuilddir ?= $(CURDIR)/.html
+HTOPTS ?=
+
+DESTDIR ?=
+prefix ?= /usr/local
+datarootdir ?= $(prefix)/share
+docdir ?= $(datarootdir)/doc
+MANDIR ?= $(CURDIR)
+mandir ?= $(datarootdir)/man
+MAN1DIR ?= $(CURDIR)/man1
+MAN2DIR ?= $(CURDIR)/man2
+MAN3DIR ?= $(CURDIR)/man3
+MAN4DIR ?= $(CURDIR)/man4
+MAN5DIR ?= $(CURDIR)/man5
+MAN6DIR ?= $(CURDIR)/man6
+MAN7DIR ?= $(CURDIR)/man7
+MAN8DIR ?= $(CURDIR)/man8
+man1dir ?= $(mandir)/man1
+man2dir ?= $(mandir)/man2
+man3dir ?= $(mandir)/man3
+man4dir ?= $(mandir)/man4
+man5dir ?= $(mandir)/man5
+man6dir ?= $(mandir)/man6
+man7dir ?= $(mandir)/man7
+man8dir ?= $(mandir)/man8
+htmldir ?= $(docdir)
+htmldir_ ?= $(htmldir)/man
+htmlext ?= .html
+
+INSTALL ?= install
+INSTALL_DATA ?= $(INSTALL) -m 644
+INSTALL_DIR ?= $(INSTALL) -m 755 -d
+RMDIR ?= rmdir --ignore-fail-on-non-empty
 
-htmlbuilddir = $(CURDIR)/.html
-HTOPTS =
 
-DESTDIR =
-prefix = /usr/local
-datarootdir = $(prefix)/share
-docdir = $(datarootdir)/doc
-mandir = $(datarootdir)/man
-htmldir = $(docdir)
-htmldir_ = $(htmldir)/man
-htmlext = .html
+.PHONY: all
+all: install
 
-INSTALL = install
-INSTALL_DATA = $(INSTALL) -m 644
-INSTALL_DIR = $(INSTALL) -m 755 -d
 
-.PHONY: all
-all:
-	$(MAKE) uninstall;
-	$(MAKE) install;
+%/:
+	@echo '	INSTALL	$@';
+	$(INSTALL_DIR) $@;
+
+
+.PHONY: install
+install: install-man
+
+.PHONY: uninstall remove
+uninstall remove: uninstall-man
+
+.PHONY: installdirs
+installdirs: installdirs-man
+installdirs: installdirs-man1
+installdirs: installdirs-man2
+installdirs: installdirs-man3
+installdirs: installdirs-man4
+installdirs: installdirs-man5
+installdirs: installdirs-man6
+installdirs: installdirs-man7
+installdirs: installdirs-man8
+
+.PHONY: clean
+clean:
+	find man?/ -type f \
+	|while read f; do \
+		rm -f "$(htmlbuilddir)/$$f".*; \
+	done;
+
+################################################################################
+# man
+
+MAN1PAGES ?= $(shell find $(MAN1DIR) -type f | grep '$(man1ext)$$')
+MAN2PAGES ?= $(shell find $(MAN2DIR) -type f | grep '$(man2ext)$$')
+MAN3PAGES ?= $(shell find $(MAN3DIR) -type f | grep '$(man3ext)$$')
+MAN4PAGES ?= $(shell find $(MAN4DIR) -type f | grep '$(man4ext)$$')
+MAN5PAGES ?= $(shell find $(MAN5DIR) -type f | grep '$(man5ext)$$')
+MAN6PAGES ?= $(shell find $(MAN6DIR) -type f | grep '$(man6ext)$$')
+MAN7PAGES ?= $(shell find $(MAN7DIR) -type f | grep '$(man7ext)$$')
+MAN8PAGES ?= $(shell find $(MAN8DIR) -type f | grep '$(man8ext)$$')
+MANPAGES  ?= $(MAN1PAGES) \
+            $(MAN2PAGES) \
+            $(MAN3PAGES) \
+            $(MAN4PAGES) \
+            $(MAN5PAGES) \
+            $(MAN6PAGES) \
+            $(MAN7PAGES) \
+            $(MAN8PAGES)
+man1pages ?= $(addprefix $(DESTDIR)$(man1dir)/,$(notdir $(MAN1PAGES)))
+man2pages ?= $(addprefix $(DESTDIR)$(man2dir)/,$(notdir $(MAN2PAGES)))
+man3pages ?= $(addprefix $(DESTDIR)$(man3dir)/,$(notdir $(MAN3PAGES)))
+man4pages ?= $(addprefix $(DESTDIR)$(man4dir)/,$(notdir $(MAN4PAGES)))
+man5pages ?= $(addprefix $(DESTDIR)$(man5dir)/,$(notdir $(MAN5PAGES)))
+man6pages ?= $(addprefix $(DESTDIR)$(man6dir)/,$(notdir $(MAN6PAGES)))
+man7pages ?= $(addprefix $(DESTDIR)$(man7dir)/,$(notdir $(MAN7PAGES)))
+man8pages ?= $(addprefix $(DESTDIR)$(man8dir)/,$(notdir $(MAN8PAGES)))
+manpages  ?= $(man1pages) \
+            $(man2pages) \
+            $(man3pages) \
+            $(man4pages) \
+            $(man5pages) \
+            $(man6pages) \
+            $(man7pages) \
+            $(man8pages)
+
+
+$(manpages): $(DESTDIR)$(mandir)/%: $(MANDIR)/%
+	@echo '	INSTALL	$@';
+	$(INSTALL_DATA) -T "$(MANDIR)/$*" $@;
+
+
+INSTALLDIRS_MANN ?= installdirs-man1 \
+                    installdirs-man2 \
+                    installdirs-man3 \
+                    installdirs-man4 \
+                    installdirs-man5 \
+                    installdirs-man6 \
+                    installdirs-man7 \
+                    installdirs-man8
+.PHONY: $(INSTALLDIRS_MANN)
+$(INSTALLDIRS_MANN): installdirs-%: $(DESTDIR)$(mandir)/%/ | installdirs-man
+	@:;
+.PHONY: installdirs-man
+installdirs-man:  $(DESTDIR)$(mandir)/
+	@:;
+
+INSTALL_MANN ?= install-man1 \
+                install-man2 \
+                install-man3 \
+                install-man4 \
+                install-man5 \
+                install-man6 \
+                install-man7 \
+                install-man8
+.PHONY: install-man
+install-man: $(INSTALL_MANN) | $(INSTALLDIRS_MANN)
+	@:;
+
+.PHONY: $(INSTALL_MANN)
+$(INSTALL_MANN): install-%: | installdirs-%
+	@echo '	INSTALL    $(DESTDIR)$(mandir)/$*/';
+	find $(MANDIR)/$*/ -type f \
+	| grep '.[0-9]$$' \
+	| sed 's,$(MANDIR)/$*/,$(DESTDIR)$(mandir)/$*/,' \
+	| xargs $(MAKE);
+
+UNINSTALL_MANN ?= uninstall-man1 \
+                  uninstall-man2 \
+                  uninstall-man3 \
+                  uninstall-man4 \
+                  uninstall-man5 \
+                  uninstall-man6 \
+                  uninstall-man7 \
+                  uninstall-man8 
+
+.PHONY: $(UNINSTALL_MANN)
+$(UNINSTALL_MANN): uninstall-%:
+	@echo '	UNINSTALL  $(DESTDIR)$(mandir)/$*/';
+	find $(MANDIR)/$*/ -type f \
+	| grep '.[0-9]$$' \
+	| sed 's,$(MANDIR)/$*/,$(DESTDIR)$(mandir)/$*/,' \
+	| xargs rm -f;
+	$(RMDIR) $(DESTDIR)$(mandir)/$*/ 2>/dev/null ||:;
+.PHONY: uninstall-man
+uninstall-man: $(UNINSTALL_MANN)
+	@echo '	UNINSTALL	$(DESTDIR)$(mandir)/';
+	$(RMDIR) $(DESTDIR)$(mandir)/ ||:;
+
+
+################################################################################
+# html
 
 # Use with
 #  make HTOPTS=whatever html
@@ -58,28 +208,6 @@ installdirs-html:
 		$(INSTALL_DIR) "$(DESTDIR)$(htmldir_)/$$d"; \
 	done;
 
-.PHONY: install
-install: | installdirs
-	find man?/ -type f \
-	|while read f; do \
-		$(INSTALL_DATA) -T "$$f" "$(DESTDIR)$(mandir)/$$f"; \
-	done;
-
-.PHONY: installdirs
-installdirs:
-	find man?/ -type d \
-	|while read d; do \
-		$(INSTALL_DIR) "$(DESTDIR)$(mandir)/$$d"; \
-	done;
-
-.PHONY: uninstall remove
-uninstall remove:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(DESTDIR)$(mandir)/$$f"; \
-		rm -f "$(DESTDIR)$(mandir)/$$f".*; \
-	done;
-
 .PHONY: uninstall-html
 uninstall-html:
 	find man?/ -type f \
@@ -87,12 +215,9 @@ uninstall-html:
 		rm -f "$(DESTDIR)$(htmldir_)/$$f".*; \
 	done;
 
-.PHONY: clean
-clean:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(htmlbuilddir)/$$f".*; \
-	done;
+
+################################################################################
+# tests
 
 # Check if groff reports warnings (may be words of sentences not displayed)
 # from https://lintian.debian.org/tags/groff-message.html
diff --git a/README b/README
index 6598170c0..b8c7d6edc 100644
--- a/README
+++ b/README
@@ -17,7 +17,8 @@ For further information on contributing, see the CONTRIBUTING file.
 
 Installing and uninstalling
 ===========================
-"make install" will copy these man pages to /usr/local/share/man/man[1-8].
+"make", "make all", or "make install" will copy these man pages to
+/usr/local/share/man/man[1-8].
 
 To install to a path different from /usr/local, use
 "make install prefix=/install/path".
@@ -26,8 +27,9 @@ To install to a path different from /usr/local, use
 distribution from its destination.  Use with caution, and remember to
 use "prefix" if desired, as with the "install" target.
 
-"make" or "make all" will perform "make uninstall" followed by "make
-install".
+To install only a specific man section (mandir) such as man3, use
+"make install-man3".  Similar syntax can be used to uninstall a
+specific man section, such as man7: "make uninstall-man7".
 
 Copyrights
 ==========
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v19] Makefile, README: Break installation into a target for each mandir
  2021-06-04 20:53 [PATCH] Makefile, README: Break installation into a target for each mandir Alejandro Colomar
@ 2021-06-07 16:49 ` Alejandro Colomar
  2021-06-07 18:12 ` [PATCH v20] " Alejandro Colomar
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Alejandro Colomar @ 2021-06-07 16:49 UTC (permalink / raw)
  To: mtk.manpages; +Cc: Alejandro Colomar, linux-man, Dr . Tobias Quathamer

Instead of having a monolithic 'make install', break it into
multiple targets such as 'make install-man3'.  This simplifies
packaging, for example in Debian, where they break this project
into several packages: 'manpages' and 'manpages-dev', each
containinng different mandirs.

The above allows for multithread installation: 'make -j8'

Also, don't overwrite files that don't need to be overwritten, by
having a target for files, which makes use of make's timestamp
comparison.

This allows for much faster installation times.

For comparison, on my laptop (i7-8850H; 6C/12T):

Old Makefile:
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.509s
	user	0m5.269s
	sys	0m2.614s

	The times with the old makefile, varied a lot, between
	5 and 10 seconds.  The times after applying this patch
	are much more consistent.  BTW, I compared these times to
	the very old Makefile of man-pages-5-09, and those were
	around 3.5 s, so it was a bit of my fault to have such a
	slow Makefile, when I changed the Makefile some weeks ago.

New Makefile (full clean install):
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m5.072s
	user	0m4.356s
	sys	0m0.980s
	~/src/linux/man-pages$ time sudo make -j2 >/dev/null

	real	0m1.688s
	user	0m2.616s
	sys	0m0.283s
	~/src/linux/man-pages$ time sudo make -j >/dev/null

	real	0m1.468s
	user	0m2.579s
	sys	0m0.289s

	Here we can see that 'make -j' drops times drastically,
	compared to the old monolithic Makefile.  Not only that,
	but since when we are working with the man pages there
	aren't many pages involved, times will be even better.

	Here are some times with a single page changed:

New Makefile (one page touched):
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.976s
	user	0m0.952s
	sys	0m0.027s
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make -j install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.967s
	user	0m0.964s
	sys	0m0.007s

Also, modify the output of the make install and uninstall commands
so that a line is output for each file or directory that is
installed, similarly to the kernel's Makefile.  This doesn't apply
to html targets, which haven't been changed in this commit.

Also, use ':=' instead of '=' to improve performance, by
evaluating each assignment only once.

Ensure than the shell is not called when not needed, by removing
all ";" and quotes in the commands.

See also: <https://stackoverflow.com/q/67862417/6872717>

Signed-off-by: Alejandro Colomar <alx.manpages@gmail.com>
---


v2:  - Revert changes to 'make [all]', as it might break existing
       scripts.

v3:  - Remove unnecessary target installdirs (its functionality has been
       superseeded by installdirs-man).

v4:  - srcfix

v5:  - wsfix
     - Remove duplicate echo

v6:  - Add paragraph to commit msg.

v7:  - Output which files are removed (one RM line for each file)
     - Add manext variable, to avoid repeating the \.[0-9] regex.
     - Don't use ?=.  It's ugly, and makes no tangible effects
       (I tested that the times are the same).

v8:  - wsfix
     - Replace small shell scripts by proper Makefile dependencies
       and helper targets.

v9:  - wsfix
     - Sort files, so that the installation is more predictable, and
       thus, one can guess the progress of the install by the filenames
       in the log.

v10: - srcfix: use patsubst instead of addprefix + notdir, to be more
       clear.

v11: - Reorder things a bit to make it easier to read.
     - Remove an unnecessary dependency.

v12: - tfix

v13: - Remove unused variable: manext
     - Define undefined variables: man[1-8]ext
     - A few minor srcfix
     - Add times comparison and output examples to commit msg.

v14: - Replace '=' with ':=' to improve performance.
       Times in commit msg updated for this change.
     - Add a note in the README that installation should use
       multiple threads for performance reasons.

v15: - Move some code into variables (times didn't change considerably).

v16: - Add missing .PHONY: $(uninstall_manpages)

v17: - Replace *_MANN by *_MANn, which is more readable.

v18: - Don't use bash(1).  We don't need pipefail now that I simplified
       the targets to have simple separate commands.  make(1) will
       now break installation if anything fails.  This cut times by
       around 40%.
     - Don't call echo(1).  Invoking a shell just to call echo(1) for
       every file is too expensive.  Use make(1)'s $(info ...) function.
       This cut times by another 40%.
     - Update the commit msg to reflect the times after these changes.
       For a single thread, it's using around a third of the time that
       v17 needed.
     - v18 applies on top of mtk's tree, unlike previous revisions,
       which applied on top of another patch of mine, which I dropped.

v19: - Remove `;` and `'` to avoid calling unnecessary shells.
     - The above improved performance by another 50% or so; update the
       commit msg with the new times.
     - Fix some dependencies.
     - Add installdirs again.
     - srcfix: reduce LOC


 Makefile | 235 ++++++++++++++++++++++++++++++++++++++++++++-----------
 README   |   8 ++
 2 files changed, 199 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile
index 609009715..f491a0197 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,198 @@
-# Do not print "Entering directory ..."
 MAKEFLAGS += --no-print-directory
+MAKEFLAGS += --silent
 
-htmlbuilddir = $(CURDIR)/.html
-HTOPTS =
+ROOTDIR := $(CURDIR)
+htmlbuilddir := $(ROOTDIR)/.html
+HTOPTS :=
 
-DESTDIR =
-prefix = /usr/local
-datarootdir = $(prefix)/share
-docdir = $(datarootdir)/doc
-mandir = $(datarootdir)/man
-htmldir = $(docdir)
-htmldir_ = $(htmldir)/man
-htmlext = .html
+DESTDIR :=
+prefix := /usr/local
+datarootdir := $(prefix)/share
+docdir := $(datarootdir)/doc
+MANDIR := $(ROOTDIR)
+mandir := $(datarootdir)/man
+MAN1DIR := $(MANDIR)/man1
+MAN2DIR := $(MANDIR)/man2
+MAN3DIR := $(MANDIR)/man3
+MAN4DIR := $(MANDIR)/man4
+MAN5DIR := $(MANDIR)/man5
+MAN6DIR := $(MANDIR)/man6
+MAN7DIR := $(MANDIR)/man7
+MAN8DIR := $(MANDIR)/man8
+man1dir := $(mandir)/man1
+man2dir := $(mandir)/man2
+man3dir := $(mandir)/man3
+man4dir := $(mandir)/man4
+man5dir := $(mandir)/man5
+man6dir := $(mandir)/man6
+man7dir := $(mandir)/man7
+man8dir := $(mandir)/man8
+manext := \.[0-9]
+man1ext := .1
+man2ext := .2
+man3ext := .3
+man4ext := .4
+man5ext := .5
+man6ext := .6
+man7ext := .7
+man8ext := .8
+htmldir := $(docdir)
+htmldir_ := $(htmldir)/man
+htmlext := .html
+
+INSTALL := install
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -m 755 -d
+RM := rm -f
+RMDIR := rmdir --ignore-fail-on-non-empty
 
-INSTALL = install
-INSTALL_DATA = $(INSTALL) -m 644
-INSTALL_DIR = $(INSTALL) -m 755 -d
 
 .PHONY: all
 all:
-	$(MAKE) uninstall;
-	$(MAKE) install;
+	$(MAKE) uninstall
+	$(MAKE) install
+
+
+%/:
+	$(info -	INSTALL	$@)
+	$(INSTALL_DIR) $@
+
+
+.PHONY: install
+install: install-man | installdirs
+
+.PHONY: installdirs
+installdirs: | installdirs-man
+
+.PHONY: uninstall remove
+uninstall remove: uninstall-man
+
+.PHONY: clean
+clean:
+	find man?/ -type f \
+	|while read f; do \
+		rm -f "$(htmlbuilddir)/$$f".*; \
+	done;
+
+################################################################################
+# man
+
+MANPAGES  := $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)$$' | sort)
+MAN1PAGES := $(filter %$(man1ext),$(MANPAGES))
+MAN2PAGES := $(filter %$(man2ext),$(MANPAGES))
+MAN3PAGES := $(filter %$(man3ext),$(MANPAGES))
+MAN4PAGES := $(filter %$(man4ext),$(MANPAGES))
+MAN5PAGES := $(filter %$(man5ext),$(MANPAGES))
+MAN6PAGES := $(filter %$(man6ext),$(MANPAGES))
+MAN7PAGES := $(filter %$(man7ext),$(MANPAGES))
+MAN8PAGES := $(filter %$(man8ext),$(MANPAGES))
+
+manpages  := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%,$(MANPAGES))
+man1pages := $(filter %$(man1ext),$(manpages))
+man2pages := $(filter %$(man2ext),$(manpages))
+man3pages := $(filter %$(man3ext),$(manpages))
+man4pages := $(filter %$(man4ext),$(manpages))
+man5pages := $(filter %$(man5ext),$(manpages))
+man6pages := $(filter %$(man6ext),$(manpages))
+man7pages := $(filter %$(man7ext),$(manpages))
+man8pages := $(filter %$(man8ext),$(manpages))
+
+.SECONDEXPANSION:
+$(manpages): $(DESTDIR)$(mandir)/%: $(MANDIR)/% | $$(@D)/
+	$(info -	INSTALL	$@)
+	$(INSTALL_DATA) -T $(MANDIR)/$* $@
+
+
+INSTALL_MANn := install-man1 \
+                install-man2 \
+                install-man3 \
+                install-man4 \
+                install-man5 \
+                install-man6 \
+                install-man7 \
+                install-man8
+
+.PHONY: $(INSTALL_MANn)
+install-man1: $(man1pages)
+install-man2: $(man2pages)
+install-man3: $(man3pages)
+install-man4: $(man4pages)
+install-man5: $(man5pages)
+install-man6: $(man6pages)
+install-man7: $(man7pages)
+install-man8: $(man8pages)
+$(INSTALL_MANn): install-%: | installdirs-%
+	@:
+
+.PHONY: install-man
+install-man: $(INSTALL_MANn)
+	@:
+
+
+INSTALLDIRS_MANn := installdirs-man1 \
+                    installdirs-man2 \
+                    installdirs-man3 \
+                    installdirs-man4 \
+                    installdirs-man5 \
+                    installdirs-man6 \
+                    installdirs-man7 \
+                    installdirs-man8
+
+.PHONY: $(INSTALLDIRS_MANn)
+$(INSTALLDIRS_MANn): installdirs-%: $(DESTDIR)$(mandir)/%/ | $(DESTDIR)$(mandir)/
+	@:
+
+.PHONY: installdirs-man
+installdirs-man: $(INSTALL_MANn)
+	@:
+
+
+uninstall_manpages  := $(addprefix uninstall-,$(wildcard $(manpages)))
+uninstall_man1pages := $(filter %$(man1ext),$(uninstall_manpages))
+uninstall_man2pages := $(filter %$(man2ext),$(uninstall_manpages))
+uninstall_man3pages := $(filter %$(man3ext),$(uninstall_manpages))
+uninstall_man4pages := $(filter %$(man4ext),$(uninstall_manpages))
+uninstall_man5pages := $(filter %$(man5ext),$(uninstall_manpages))
+uninstall_man6pages := $(filter %$(man6ext),$(uninstall_manpages))
+uninstall_man7pages := $(filter %$(man7ext),$(uninstall_manpages))
+uninstall_man8pages := $(filter %$(man8ext),$(uninstall_manpages))
+
+.PHONY: $(uninstall_manpages)
+$(uninstall_manpages): uninstall-%:
+	$(info -	RM	$*)
+	$(RM) $*
+
+
+UNINSTALL_MANn := uninstall-man1 \
+                  uninstall-man2 \
+                  uninstall-man3 \
+                  uninstall-man4 \
+                  uninstall-man5 \
+                  uninstall-man6 \
+                  uninstall-man7 \
+                  uninstall-man8
+
+.PHONY: $(UNINSTALL_MANn)
+uninstall-man1: $(uninstall_man1pages)
+uninstall-man2: $(uninstall_man2pages)
+uninstall-man3: $(uninstall_man3pages)
+uninstall-man4: $(uninstall_man4pages)
+uninstall-man5: $(uninstall_man5pages)
+uninstall-man6: $(uninstall_man6pages)
+uninstall-man7: $(uninstall_man7pages)
+uninstall-man8: $(uninstall_man8pages)
+$(UNINSTALL_MANn): uninstall-%:
+	$(info -	RMDIR	$(DESTDIR)$(mandir)/$*/)
+	$(RMDIR) $(DESTDIR)$(mandir)/$*/ 2>/dev/null ||:
+
+.PHONY: uninstall-man
+uninstall-man: $(UNINSTALL_MANn)
+	$(info -	RMDIR	$(DESTDIR)$(mandir)/)
+	$(RMDIR) $(DESTDIR)$(mandir)/ ||:
+
+
+################################################################################
+# html
 
 # Use with
 #  make HTOPTS=whatever html
@@ -57,28 +229,6 @@ installdirs-html:
 		$(INSTALL_DIR) "$(DESTDIR)$(htmldir_)/$$d" || exit $$?; \
 	done;
 
-.PHONY: install
-install: | installdirs
-	find man?/ -type f \
-	|while read f; do \
-		$(INSTALL_DATA) -T "$$f" "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-	done;
-
-.PHONY: installdirs
-installdirs:
-	find man?/ -type d \
-	|while read d; do \
-		$(INSTALL_DIR) "$(DESTDIR)$(mandir)/$$d" || exit $$?; \
-	done;
-
-.PHONY: uninstall remove
-uninstall remove:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-		rm -f "$(DESTDIR)$(mandir)/$$f".* || exit $$?; \
-	done;
-
 .PHONY: uninstall-html
 uninstall-html:
 	find man?/ -type f \
@@ -86,12 +236,9 @@ uninstall-html:
 		rm -f "$(DESTDIR)$(htmldir_)/$$f".* || exit $$?; \
 	done;
 
-.PHONY: clean
-clean:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(htmlbuilddir)/$$f".* || exit $$?; \
-	done;
+
+################################################################################
+# tests
 
 # Check if groff reports warnings (may be words of sentences not displayed)
 # from https://lintian.debian.org/tags/groff-message.html
diff --git a/README b/README
index 6598170c0..484151773 100644
--- a/README
+++ b/README
@@ -26,9 +26,17 @@ To install to a path different from /usr/local, use
 distribution from its destination.  Use with caution, and remember to
 use "prefix" if desired, as with the "install" target.
 
+To install only a specific man section (mandir) such as man3, use
+"make install-man3".  Similar syntax can be used to uninstall a
+specific man section, such as man7: "make uninstall-man7".
+
 "make" or "make all" will perform "make uninstall" followed by "make
 install".
 
+Consider using multiple threads (at least 2) when installing
+these man pages, as the Makefile is optimized for multiple threads:
+"make -j install".
+
 Copyrights
 ==========
 See the 'man-pages-x.y.Announce' file.
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v20] Makefile, README: Break installation into a target for each mandir
  2021-06-04 20:53 [PATCH] Makefile, README: Break installation into a target for each mandir Alejandro Colomar
  2021-06-07 16:49 ` [PATCH v19] " Alejandro Colomar
@ 2021-06-07 18:12 ` Alejandro Colomar
  2021-06-07 18:18 ` [PATCH v21] " Alejandro Colomar
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Alejandro Colomar @ 2021-06-07 18:12 UTC (permalink / raw)
  To: mtk.manpages; +Cc: Alejandro Colomar, linux-man, Dr . Tobias Quathamer

Instead of having a monolithic 'make install', break it into
multiple targets such as 'make install-man3'.  This simplifies
packaging, for example in Debian, where they break this project
into several packages: 'manpages' and 'manpages-dev', each
containinng different mandirs.

The above allows for multithread installation: 'make -j8'

Also, don't overwrite files that don't need to be overwritten, by
having a target for files, which makes use of make's timestamp
comparison.

This allows for much faster installation times.

For comparison, on my laptop (i7-8850H; 6C/12T):

Old Makefile:
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.509s
	user	0m5.269s
	sys	0m2.614s

	The times with the old makefile, varied a lot, between
	5 and 10 seconds.  The times after applying this patch
	are much more consistent.  BTW, I compared these times to
	the very old Makefile of man-pages-5-09, and those were
	around 3.5 s, so it was a bit of my fault to have such a
	slow Makefile, when I changed the Makefile some weeks ago.

New Makefile (full clean install):
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m5.072s
	user	0m4.356s
	sys	0m0.980s
	~/src/linux/man-pages$ time sudo make -j2 >/dev/null

	real	0m1.688s
	user	0m2.616s
	sys	0m0.283s
	~/src/linux/man-pages$ time sudo make -j >/dev/null

	real	0m1.468s
	user	0m2.579s
	sys	0m0.289s

	Here we can see that 'make -j' drops times drastically,
	compared to the old monolithic Makefile.  Not only that,
	but since when we are working with the man pages there
	aren't many pages involved, times will be even better.

	Here are some times with a single page changed (touched):

New Makefile (one page touched):
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.976s
	user	0m0.952s
	sys	0m0.027s
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make -j install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.967s
	user	0m0.964s
	sys	0m0.007s

Also, modify the output of the make install and uninstall commands
so that a line is output for each file or directory that is
installed, similarly to the kernel's Makefile.  This doesn't apply
to html targets, which haven't been changed in this commit.

Also, use ':=' instead of '=' to improve performance, by
evaluating each assignment only once.

Ensure than the shell is not called when not needed, by removing
all ";" and quotes in the commands.

See also: <https://stackoverflow.com/q/67862417/6872717>

Signed-off-by: Alejandro Colomar <alx.manpages@gmail.com>
---


v2:  - Revert changes to 'make [all]', as it might break existing
       scripts.

v3:  - Remove unnecessary target installdirs (its functionality has been
       superseeded by installdirs-man).

v4:  - srcfix

v5:  - wsfix
     - Remove duplicate echo

v6:  - Add paragraph to commit msg.

v7:  - Output which files are removed (one RM line for each file)
     - Add manext variable, to avoid repeating the \.[0-9] regex.
     - Don't use ?=.  It's ugly, and makes no tangible effects
       (I tested that the times are the same).

v8:  - wsfix
     - Replace small shell scripts by proper Makefile dependencies
       and helper targets.

v9:  - wsfix
     - Sort files, so that the installation is more predictable, and
       thus, one can guess the progress of the install by the filenames
       in the log.

v10: - srcfix: use patsubst instead of addprefix + notdir, to be more
       clear.

v11: - Reorder things a bit to make it easier to read.
     - Remove an unnecessary dependency.

v12: - tfix

v13: - Remove unused variable: manext
     - Define undefined variables: man[1-8]ext
     - A few minor srcfix
     - Add times comparison and output examples to commit msg.

v14: - Replace '=' with ':=' to improve performance.
       Times in commit msg updated for this change.
     - Add a note in the README that installation should use
       multiple threads for performance reasons.

v15: - Move some code into variables (times didn't change considerably).

v16: - Add missing .PHONY: $(uninstall_manpages)

v17: - Replace *_MANN by *_MANn, which is more readable.

v18: - Don't use bash(1).  We don't need pipefail now that I simplified
       the targets to have simple separate commands.  make(1) will
       now break installation if anything fails.  This cut times by
       around 40%.
     - Don't call echo(1).  Invoking a shell just to call echo(1) for
       every file is too expensive.  Use make(1)'s $(info ...) function.
       This cut times by another 40%.
     - Update the commit msg to reflect the times after these changes.
       For a single thread, it's using around a third of the time that
       v17 needed.
     - v18 applies on top of mtk's tree, unlike previous revisions,
       which applied on top of another patch of mine, which I dropped.

v19: - Remove `;` and `'` to avoid calling unnecessary shells.
     - The above improved performance by another 50% or so; update the
       commit msg with the new times.
     - Fix some dependencies.
     - Add installdirs again.
     - srcfix: reduce LOC

v20: - Workaround a bug in make 3.81 where a trailing slash is ignored.
       See <https://ismail.badawi.io/blog/automatic-directory-creation-in-make/>
       That helps workaround some ambiguity in the rules, wher a rule
       to uninstall a dir also matched the pattern rule to install that
       dir.


 Makefile | 235 ++++++++++++++++++++++++++++++++++++++++++++-----------
 README   |   8 ++
 2 files changed, 199 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile
index 609009715..5919d0fc0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,198 @@
-# Do not print "Entering directory ..."
 MAKEFLAGS += --no-print-directory
+MAKEFLAGS += --silent
 
-htmlbuilddir = $(CURDIR)/.html
-HTOPTS =
+ROOTDIR := $(CURDIR)
+htmlbuilddir := $(ROOTDIR)/.html
+HTOPTS :=
 
-DESTDIR =
-prefix = /usr/local
-datarootdir = $(prefix)/share
-docdir = $(datarootdir)/doc
-mandir = $(datarootdir)/man
-htmldir = $(docdir)
-htmldir_ = $(htmldir)/man
-htmlext = .html
+DESTDIR :=
+prefix := /usr/local
+datarootdir := $(prefix)/share
+docdir := $(datarootdir)/doc
+MANDIR := $(ROOTDIR)
+mandir := $(datarootdir)/man
+MAN1DIR := $(MANDIR)/man1
+MAN2DIR := $(MANDIR)/man2
+MAN3DIR := $(MANDIR)/man3
+MAN4DIR := $(MANDIR)/man4
+MAN5DIR := $(MANDIR)/man5
+MAN6DIR := $(MANDIR)/man6
+MAN7DIR := $(MANDIR)/man7
+MAN8DIR := $(MANDIR)/man8
+man1dir := $(mandir)/man1
+man2dir := $(mandir)/man2
+man3dir := $(mandir)/man3
+man4dir := $(mandir)/man4
+man5dir := $(mandir)/man5
+man6dir := $(mandir)/man6
+man7dir := $(mandir)/man7
+man8dir := $(mandir)/man8
+manext := \.[0-9]
+man1ext := .1
+man2ext := .2
+man3ext := .3
+man4ext := .4
+man5ext := .5
+man6ext := .6
+man7ext := .7
+man8ext := .8
+htmldir := $(docdir)
+htmldir_ := $(htmldir)/man
+htmlext := .html
+
+INSTALL := install
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -m 755 -d
+RM := rm -f
+RMDIR := rmdir --ignore-fail-on-non-empty
 
-INSTALL = install
-INSTALL_DATA = $(INSTALL) -m 644
-INSTALL_DIR = $(INSTALL) -m 755 -d
 
 .PHONY: all
 all:
-	$(MAKE) uninstall;
-	$(MAKE) install;
+	$(MAKE) uninstall
+	$(MAKE) install
+
+
+%/.:
+	$(info -	INSTALL	$@)
+	$(INSTALL_DIR) $@
+
+
+.PHONY: install
+install: install-man | installdirs
+
+.PHONY: installdirs
+installdirs: | installdirs-man
+
+.PHONY: uninstall remove
+uninstall remove: uninstall-man
+
+.PHONY: clean
+clean:
+	find man?/ -type f \
+	|while read f; do \
+		rm -f "$(htmlbuilddir)/$$f".*; \
+	done;
+
+################################################################################
+# man
+
+MANPAGES  := $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)$$' | sort)
+MAN1PAGES := $(filter %$(man1ext),$(MANPAGES))
+MAN2PAGES := $(filter %$(man2ext),$(MANPAGES))
+MAN3PAGES := $(filter %$(man3ext),$(MANPAGES))
+MAN4PAGES := $(filter %$(man4ext),$(MANPAGES))
+MAN5PAGES := $(filter %$(man5ext),$(MANPAGES))
+MAN6PAGES := $(filter %$(man6ext),$(MANPAGES))
+MAN7PAGES := $(filter %$(man7ext),$(MANPAGES))
+MAN8PAGES := $(filter %$(man8ext),$(MANPAGES))
+
+manpages  := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%,$(MANPAGES))
+man1pages := $(filter %$(man1ext),$(manpages))
+man2pages := $(filter %$(man2ext),$(manpages))
+man3pages := $(filter %$(man3ext),$(manpages))
+man4pages := $(filter %$(man4ext),$(manpages))
+man5pages := $(filter %$(man5ext),$(manpages))
+man6pages := $(filter %$(man6ext),$(manpages))
+man7pages := $(filter %$(man7ext),$(manpages))
+man8pages := $(filter %$(man8ext),$(manpages))
+
+.SECONDEXPANSION:
+$(manpages): $(DESTDIR)$(mandir)/%: $(MANDIR)/% | $$(@D)/.
+	$(info -	INSTALL	$@)
+	$(INSTALL_DATA) -T $(MANDIR)/$* $@
+
+
+INSTALL_MANn := install-man1 \
+                install-man2 \
+                install-man3 \
+                install-man4 \
+                install-man5 \
+                install-man6 \
+                install-man7 \
+                install-man8
+
+.PHONY: $(INSTALL_MANn)
+install-man1: $(man1pages)
+install-man2: $(man2pages)
+install-man3: $(man3pages)
+install-man4: $(man4pages)
+install-man5: $(man5pages)
+install-man6: $(man6pages)
+install-man7: $(man7pages)
+install-man8: $(man8pages)
+$(INSTALL_MANn): install-%: | installdirs-%
+	@:
+
+.PHONY: install-man
+install-man: $(INSTALL_MANn)
+	@:
+
+
+INSTALLDIRS_MANn := installdirs-man1 \
+                    installdirs-man2 \
+                    installdirs-man3 \
+                    installdirs-man4 \
+                    installdirs-man5 \
+                    installdirs-man6 \
+                    installdirs-man7 \
+                    installdirs-man8
+
+.PHONY: $(INSTALLDIRS_MANn)
+$(INSTALLDIRS_MANn): installdirs-%: $(DESTDIR)$(mandir)/%/. | $(DESTDIR)$(mandir)/.
+	@:
+
+.PHONY: installdirs-man
+installdirs-man: $(INSTALL_MANn)
+	@:
+
+
+uninstall_manpages  := $(addprefix uninstall-,$(wildcard $(manpages)))
+uninstall_man1pages := $(filter %$(man1ext),$(uninstall_manpages))
+uninstall_man2pages := $(filter %$(man2ext),$(uninstall_manpages))
+uninstall_man3pages := $(filter %$(man3ext),$(uninstall_manpages))
+uninstall_man4pages := $(filter %$(man4ext),$(uninstall_manpages))
+uninstall_man5pages := $(filter %$(man5ext),$(uninstall_manpages))
+uninstall_man6pages := $(filter %$(man6ext),$(uninstall_manpages))
+uninstall_man7pages := $(filter %$(man7ext),$(uninstall_manpages))
+uninstall_man8pages := $(filter %$(man8ext),$(uninstall_manpages))
+
+.PHONY: $(uninstall_manpages)
+$(uninstall_manpages): uninstall-%:
+	$(info -	RM	$*)
+	$(RM) $*
+
+
+UNINSTALL_MANn := uninstall-man1 \
+                  uninstall-man2 \
+                  uninstall-man3 \
+                  uninstall-man4 \
+                  uninstall-man5 \
+                  uninstall-man6 \
+                  uninstall-man7 \
+                  uninstall-man8
+
+.PHONY: $(UNINSTALL_MANn)
+uninstall-man1: $(uninstall_man1pages)
+uninstall-man2: $(uninstall_man2pages)
+uninstall-man3: $(uninstall_man3pages)
+uninstall-man4: $(uninstall_man4pages)
+uninstall-man5: $(uninstall_man5pages)
+uninstall-man6: $(uninstall_man6pages)
+uninstall-man7: $(uninstall_man7pages)
+uninstall-man8: $(uninstall_man8pages)
+$(UNINSTALL_MANn): uninstall-%:
+	$(info -	RMDIR	$(DESTDIR)$(mandir)/$*/)
+	$(RMDIR) $(DESTDIR)$(mandir)/$*/ 2>/dev/null ||:
+
+.PHONY: uninstall-man
+uninstall-man: $(UNINSTALL_MANn)
+	$(info -	RMDIR	$(DESTDIR)$(mandir)/)
+	$(RMDIR) $(DESTDIR)$(mandir)/ ||:
+
+
+################################################################################
+# html
 
 # Use with
 #  make HTOPTS=whatever html
@@ -57,28 +229,6 @@ installdirs-html:
 		$(INSTALL_DIR) "$(DESTDIR)$(htmldir_)/$$d" || exit $$?; \
 	done;
 
-.PHONY: install
-install: | installdirs
-	find man?/ -type f \
-	|while read f; do \
-		$(INSTALL_DATA) -T "$$f" "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-	done;
-
-.PHONY: installdirs
-installdirs:
-	find man?/ -type d \
-	|while read d; do \
-		$(INSTALL_DIR) "$(DESTDIR)$(mandir)/$$d" || exit $$?; \
-	done;
-
-.PHONY: uninstall remove
-uninstall remove:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-		rm -f "$(DESTDIR)$(mandir)/$$f".* || exit $$?; \
-	done;
-
 .PHONY: uninstall-html
 uninstall-html:
 	find man?/ -type f \
@@ -86,12 +236,9 @@ uninstall-html:
 		rm -f "$(DESTDIR)$(htmldir_)/$$f".* || exit $$?; \
 	done;
 
-.PHONY: clean
-clean:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(htmlbuilddir)/$$f".* || exit $$?; \
-	done;
+
+################################################################################
+# tests
 
 # Check if groff reports warnings (may be words of sentences not displayed)
 # from https://lintian.debian.org/tags/groff-message.html
diff --git a/README b/README
index 6598170c0..484151773 100644
--- a/README
+++ b/README
@@ -26,9 +26,17 @@ To install to a path different from /usr/local, use
 distribution from its destination.  Use with caution, and remember to
 use "prefix" if desired, as with the "install" target.
 
+To install only a specific man section (mandir) such as man3, use
+"make install-man3".  Similar syntax can be used to uninstall a
+specific man section, such as man7: "make uninstall-man7".
+
 "make" or "make all" will perform "make uninstall" followed by "make
 install".
 
+Consider using multiple threads (at least 2) when installing
+these man pages, as the Makefile is optimized for multiple threads:
+"make -j install".
+
 Copyrights
 ==========
 See the 'man-pages-x.y.Announce' file.
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v21] Makefile, README: Break installation into a target for each mandir
  2021-06-04 20:53 [PATCH] Makefile, README: Break installation into a target for each mandir Alejandro Colomar
  2021-06-07 16:49 ` [PATCH v19] " Alejandro Colomar
  2021-06-07 18:12 ` [PATCH v20] " Alejandro Colomar
@ 2021-06-07 18:18 ` Alejandro Colomar
  2021-06-07 21:36 ` [PATCH v22] " Alejandro Colomar
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Alejandro Colomar @ 2021-06-07 18:18 UTC (permalink / raw)
  To: mtk.manpages; +Cc: Alejandro Colomar, linux-man, Dr . Tobias Quathamer

Instead of having a monolithic 'make install', break it into
multiple targets such as 'make install-man3'.  This simplifies
packaging, for example in Debian, where they break this project
into several packages: 'manpages' and 'manpages-dev', each
containinng different mandirs.

The above allows for multithread installation: 'make -j8'

Also, don't overwrite files that don't need to be overwritten, by
having a target for files, which makes use of make's timestamp
comparison.

This allows for much faster installation times.

For comparison, on my laptop (i7-8850H; 6C/12T):

Old Makefile:
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.509s
	user	0m5.269s
	sys	0m2.614s

	The times with the old makefile, varied a lot, between
	5 and 10 seconds.  The times after applying this patch
	are much more consistent.  BTW, I compared these times to
	the very old Makefile of man-pages-5-09, and those were
	around 3.5 s, so it was a bit of my fault to have such a
	slow Makefile, when I changed the Makefile some weeks ago.

New Makefile (full clean install):
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m5.072s
	user	0m4.356s
	sys	0m0.980s
	~/src/linux/man-pages$ time sudo make -j2 >/dev/null

	real	0m1.688s
	user	0m2.616s
	sys	0m0.283s
	~/src/linux/man-pages$ time sudo make -j >/dev/null

	real	0m1.468s
	user	0m2.579s
	sys	0m0.289s

	Here we can see that 'make -j' drops times drastically,
	compared to the old monolithic Makefile.  Not only that,
	but since when we are working with the man pages there
	aren't many pages involved, times will be even better.

	Here are some times with a single page changed (touched):

New Makefile (one page touched):
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.976s
	user	0m0.952s
	sys	0m0.027s
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make -j install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.967s
	user	0m0.964s
	sys	0m0.007s

Also, modify the output of the make install and uninstall commands
so that a line is output for each file or directory that is
installed, similarly to the kernel's Makefile.  This doesn't apply
to html targets, which haven't been changed in this commit.

Also, use ':=' instead of '=' to improve performance, by
evaluating each assignment only once.

Ensure than the shell is not called when not needed, by removing
all ";" and quotes in the commands.

See also: <https://stackoverflow.com/q/67862417/6872717>

Signed-off-by: Alejandro Colomar <alx.manpages@gmail.com>
---


v2:  - Revert changes to 'make [all]', as it might break existing
       scripts.

v3:  - Remove unnecessary target installdirs (its functionality has been
       superseeded by installdirs-man).

v4:  - srcfix

v5:  - wsfix
     - Remove duplicate echo

v6:  - Add paragraph to commit msg.

v7:  - Output which files are removed (one RM line for each file)
     - Add manext variable, to avoid repeating the \.[0-9] regex.
     - Don't use ?=.  It's ugly, and makes no tangible effects
       (I tested that the times are the same).

v8:  - wsfix
     - Replace small shell scripts by proper Makefile dependencies
       and helper targets.

v9:  - wsfix
     - Sort files, so that the installation is more predictable, and
       thus, one can guess the progress of the install by the filenames
       in the log.

v10: - srcfix: use patsubst instead of addprefix + notdir, to be more
       clear.

v11: - Reorder things a bit to make it easier to read.
     - Remove an unnecessary dependency.

v12: - tfix

v13: - Remove unused variable: manext
     - Define undefined variables: man[1-8]ext
     - A few minor srcfix
     - Add times comparison and output examples to commit msg.

v14: - Replace '=' with ':=' to improve performance.
       Times in commit msg updated for this change.
     - Add a note in the README that installation should use
       multiple threads for performance reasons.

v15: - Move some code into variables (times didn't change considerably).

v16: - Add missing .PHONY: $(uninstall_manpages)

v17: - Replace *_MANN by *_MANn, which is more readable.

v18: - Don't use bash(1).  We don't need pipefail now that I simplified
       the targets to have simple separate commands.  make(1) will
       now break installation if anything fails.  This cut times by
       around 40%.
     - Don't call echo(1).  Invoking a shell just to call echo(1) for
       every file is too expensive.  Use make(1)'s $(info ...) function.
       This cut times by another 40%.
     - Update the commit msg to reflect the times after these changes.
       For a single thread, it's using around a third of the time that
       v17 needed.
     - v18 applies on top of mtk's tree, unlike previous revisions,
       which applied on top of another patch of mine, which I dropped.

v19: - Remove `;` and `'` to avoid calling unnecessary shells.
     - The above improved performance by another 50% or so; update the
       commit msg with the new times.
     - Fix some dependencies.
     - Add installdirs again.
     - srcfix: reduce LOC

v20: - Workaround a bug in make 3.81 where a trailing slash is ignored.
       See <https://ismail.badawi.io/blog/automatic-directory-creation-in-make/>
       That helps workaround some ambiguity in the rules, wher a rule
       to uninstall a dir also matched the pattern rule to install that
       dir.

v21: - ffix


 Makefile | 235 ++++++++++++++++++++++++++++++++++++++++++++-----------
 README   |   8 ++
 2 files changed, 199 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile
index 609009715..16ddd4f01 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,198 @@
-# Do not print "Entering directory ..."
 MAKEFLAGS += --no-print-directory
+MAKEFLAGS += --silent
 
-htmlbuilddir = $(CURDIR)/.html
-HTOPTS =
+ROOTDIR := $(CURDIR)
+htmlbuilddir := $(ROOTDIR)/.html
+HTOPTS :=
 
-DESTDIR =
-prefix = /usr/local
-datarootdir = $(prefix)/share
-docdir = $(datarootdir)/doc
-mandir = $(datarootdir)/man
-htmldir = $(docdir)
-htmldir_ = $(htmldir)/man
-htmlext = .html
+DESTDIR :=
+prefix := /usr/local
+datarootdir := $(prefix)/share
+docdir := $(datarootdir)/doc
+MANDIR := $(ROOTDIR)
+mandir := $(datarootdir)/man
+MAN1DIR := $(MANDIR)/man1
+MAN2DIR := $(MANDIR)/man2
+MAN3DIR := $(MANDIR)/man3
+MAN4DIR := $(MANDIR)/man4
+MAN5DIR := $(MANDIR)/man5
+MAN6DIR := $(MANDIR)/man6
+MAN7DIR := $(MANDIR)/man7
+MAN8DIR := $(MANDIR)/man8
+man1dir := $(mandir)/man1
+man2dir := $(mandir)/man2
+man3dir := $(mandir)/man3
+man4dir := $(mandir)/man4
+man5dir := $(mandir)/man5
+man6dir := $(mandir)/man6
+man7dir := $(mandir)/man7
+man8dir := $(mandir)/man8
+manext := \.[0-9]
+man1ext := .1
+man2ext := .2
+man3ext := .3
+man4ext := .4
+man5ext := .5
+man6ext := .6
+man7ext := .7
+man8ext := .8
+htmldir := $(docdir)
+htmldir_ := $(htmldir)/man
+htmlext := .html
+
+INSTALL := install
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -m 755 -d
+RM := rm -f
+RMDIR := rmdir --ignore-fail-on-non-empty
 
-INSTALL = install
-INSTALL_DATA = $(INSTALL) -m 644
-INSTALL_DIR = $(INSTALL) -m 755 -d
 
 .PHONY: all
 all:
-	$(MAKE) uninstall;
-	$(MAKE) install;
+	$(MAKE) uninstall
+	$(MAKE) install
+
+
+%/.:
+	$(info -	INSTALL	$*/)
+	$(INSTALL_DIR) $*/
+
+
+.PHONY: install
+install: install-man | installdirs
+
+.PHONY: installdirs
+installdirs: | installdirs-man
+
+.PHONY: uninstall remove
+uninstall remove: uninstall-man
+
+.PHONY: clean
+clean:
+	find man?/ -type f \
+	|while read f; do \
+		rm -f "$(htmlbuilddir)/$$f".*; \
+	done;
+
+################################################################################
+# man
+
+MANPAGES  := $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)$$' | sort)
+MAN1PAGES := $(filter %$(man1ext),$(MANPAGES))
+MAN2PAGES := $(filter %$(man2ext),$(MANPAGES))
+MAN3PAGES := $(filter %$(man3ext),$(MANPAGES))
+MAN4PAGES := $(filter %$(man4ext),$(MANPAGES))
+MAN5PAGES := $(filter %$(man5ext),$(MANPAGES))
+MAN6PAGES := $(filter %$(man6ext),$(MANPAGES))
+MAN7PAGES := $(filter %$(man7ext),$(MANPAGES))
+MAN8PAGES := $(filter %$(man8ext),$(MANPAGES))
+
+manpages  := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%,$(MANPAGES))
+man1pages := $(filter %$(man1ext),$(manpages))
+man2pages := $(filter %$(man2ext),$(manpages))
+man3pages := $(filter %$(man3ext),$(manpages))
+man4pages := $(filter %$(man4ext),$(manpages))
+man5pages := $(filter %$(man5ext),$(manpages))
+man6pages := $(filter %$(man6ext),$(manpages))
+man7pages := $(filter %$(man7ext),$(manpages))
+man8pages := $(filter %$(man8ext),$(manpages))
+
+.SECONDEXPANSION:
+$(manpages): $(DESTDIR)$(mandir)/%: $(MANDIR)/% | $$(@D)/.
+	$(info -	INSTALL	$@)
+	$(INSTALL_DATA) -T $(MANDIR)/$* $@
+
+
+INSTALL_MANn := install-man1 \
+                install-man2 \
+                install-man3 \
+                install-man4 \
+                install-man5 \
+                install-man6 \
+                install-man7 \
+                install-man8
+
+.PHONY: $(INSTALL_MANn)
+install-man1: $(man1pages)
+install-man2: $(man2pages)
+install-man3: $(man3pages)
+install-man4: $(man4pages)
+install-man5: $(man5pages)
+install-man6: $(man6pages)
+install-man7: $(man7pages)
+install-man8: $(man8pages)
+$(INSTALL_MANn): install-%: | installdirs-%
+	@:
+
+.PHONY: install-man
+install-man: $(INSTALL_MANn)
+	@:
+
+
+INSTALLDIRS_MANn := installdirs-man1 \
+                    installdirs-man2 \
+                    installdirs-man3 \
+                    installdirs-man4 \
+                    installdirs-man5 \
+                    installdirs-man6 \
+                    installdirs-man7 \
+                    installdirs-man8
+
+.PHONY: $(INSTALLDIRS_MANn)
+$(INSTALLDIRS_MANn): installdirs-%: $(DESTDIR)$(mandir)/%/. | $(DESTDIR)$(mandir)/.
+	@:
+
+.PHONY: installdirs-man
+installdirs-man: $(INSTALL_MANn)
+	@:
+
+
+uninstall_manpages  := $(addprefix uninstall-,$(wildcard $(manpages)))
+uninstall_man1pages := $(filter %$(man1ext),$(uninstall_manpages))
+uninstall_man2pages := $(filter %$(man2ext),$(uninstall_manpages))
+uninstall_man3pages := $(filter %$(man3ext),$(uninstall_manpages))
+uninstall_man4pages := $(filter %$(man4ext),$(uninstall_manpages))
+uninstall_man5pages := $(filter %$(man5ext),$(uninstall_manpages))
+uninstall_man6pages := $(filter %$(man6ext),$(uninstall_manpages))
+uninstall_man7pages := $(filter %$(man7ext),$(uninstall_manpages))
+uninstall_man8pages := $(filter %$(man8ext),$(uninstall_manpages))
+
+.PHONY: $(uninstall_manpages)
+$(uninstall_manpages): uninstall-%:
+	$(info -	RM	$*)
+	$(RM) $*
+
+
+UNINSTALL_MANn := uninstall-man1 \
+                  uninstall-man2 \
+                  uninstall-man3 \
+                  uninstall-man4 \
+                  uninstall-man5 \
+                  uninstall-man6 \
+                  uninstall-man7 \
+                  uninstall-man8
+
+.PHONY: $(UNINSTALL_MANn)
+uninstall-man1: $(uninstall_man1pages)
+uninstall-man2: $(uninstall_man2pages)
+uninstall-man3: $(uninstall_man3pages)
+uninstall-man4: $(uninstall_man4pages)
+uninstall-man5: $(uninstall_man5pages)
+uninstall-man6: $(uninstall_man6pages)
+uninstall-man7: $(uninstall_man7pages)
+uninstall-man8: $(uninstall_man8pages)
+$(UNINSTALL_MANn): uninstall-%:
+	$(info -	RMDIR	$(DESTDIR)$(mandir)/$*/)
+	$(RMDIR) $(DESTDIR)$(mandir)/$*/ 2>/dev/null ||:
+
+.PHONY: uninstall-man
+uninstall-man: $(UNINSTALL_MANn)
+	$(info -	RMDIR	$(DESTDIR)$(mandir)/)
+	$(RMDIR) $(DESTDIR)$(mandir)/ ||:
+
+
+################################################################################
+# html
 
 # Use with
 #  make HTOPTS=whatever html
@@ -57,28 +229,6 @@ installdirs-html:
 		$(INSTALL_DIR) "$(DESTDIR)$(htmldir_)/$$d" || exit $$?; \
 	done;
 
-.PHONY: install
-install: | installdirs
-	find man?/ -type f \
-	|while read f; do \
-		$(INSTALL_DATA) -T "$$f" "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-	done;
-
-.PHONY: installdirs
-installdirs:
-	find man?/ -type d \
-	|while read d; do \
-		$(INSTALL_DIR) "$(DESTDIR)$(mandir)/$$d" || exit $$?; \
-	done;
-
-.PHONY: uninstall remove
-uninstall remove:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-		rm -f "$(DESTDIR)$(mandir)/$$f".* || exit $$?; \
-	done;
-
 .PHONY: uninstall-html
 uninstall-html:
 	find man?/ -type f \
@@ -86,12 +236,9 @@ uninstall-html:
 		rm -f "$(DESTDIR)$(htmldir_)/$$f".* || exit $$?; \
 	done;
 
-.PHONY: clean
-clean:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(htmlbuilddir)/$$f".* || exit $$?; \
-	done;
+
+################################################################################
+# tests
 
 # Check if groff reports warnings (may be words of sentences not displayed)
 # from https://lintian.debian.org/tags/groff-message.html
diff --git a/README b/README
index 6598170c0..484151773 100644
--- a/README
+++ b/README
@@ -26,9 +26,17 @@ To install to a path different from /usr/local, use
 distribution from its destination.  Use with caution, and remember to
 use "prefix" if desired, as with the "install" target.
 
+To install only a specific man section (mandir) such as man3, use
+"make install-man3".  Similar syntax can be used to uninstall a
+specific man section, such as man7: "make uninstall-man7".
+
 "make" or "make all" will perform "make uninstall" followed by "make
 install".
 
+Consider using multiple threads (at least 2) when installing
+these man pages, as the Makefile is optimized for multiple threads:
+"make -j install".
+
 Copyrights
 ==========
 See the 'man-pages-x.y.Announce' file.
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v22] Makefile, README: Break installation into a target for each mandir
  2021-06-04 20:53 [PATCH] Makefile, README: Break installation into a target for each mandir Alejandro Colomar
                   ` (2 preceding siblings ...)
  2021-06-07 18:18 ` [PATCH v21] " Alejandro Colomar
@ 2021-06-07 21:36 ` Alejandro Colomar
  2021-06-07 21:49 ` [PATCH v23] " Alejandro Colomar
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Alejandro Colomar @ 2021-06-07 21:36 UTC (permalink / raw)
  To: mtk.manpages; +Cc: Alejandro Colomar, linux-man, Dr . Tobias Quathamer

Instead of having a monolithic 'make install', break it into
multiple targets such as 'make install-man3'.  This simplifies
packaging, for example in Debian, where they break this project
into several packages: 'manpages' and 'manpages-dev', each
containinng different mandirs.

The above allows for multithread installation: 'make -j8'

Also, don't overwrite files that don't need to be overwritten, by
having a target for files, which makes use of make's timestamp
comparison.

This allows for much faster installation times.

For comparison, on my laptop (i7-8850H; 6C/12T):

Old Makefile:
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.509s
	user	0m5.269s
	sys	0m2.614s

	The times with the old makefile, varied a lot, between
	5 and 10 seconds.  The times after applying this patch
	are much more consistent.  BTW, I compared these times to
	the very old Makefile of man-pages-5-09, and those were
	around 3.5 s, so it was a bit of my fault to have such a
	slow Makefile, when I changed the Makefile some weeks ago.

New Makefile (full clean install):
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m5.072s
	user	0m4.356s
	sys	0m0.980s
	~/src/linux/man-pages$ time sudo make -j2 >/dev/null

	real	0m1.688s
	user	0m2.616s
	sys	0m0.283s
	~/src/linux/man-pages$ time sudo make -j >/dev/null

	real	0m1.468s
	user	0m2.579s
	sys	0m0.289s

	Here we can see that 'make -j' drops times drastically,
	compared to the old monolithic Makefile.  Not only that,
	but since when we are working with the man pages there
	aren't many pages involved, times will be even better.

	Here are some times with a single page changed (touched):

New Makefile (one page touched):
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.976s
	user	0m0.952s
	sys	0m0.027s
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make -j install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.967s
	user	0m0.964s
	sys	0m0.007s

Also, modify the output of the make install and uninstall commands
so that a line is output for each file or directory that is
installed, similarly to the kernel's Makefile.  This doesn't apply
to html targets, which haven't been changed in this commit.

Also, make sure that for each invokation of $(INSTALL_DIR), no
parents are created, (i.e., avoid `mkdir -p` behavior).  The GNU
make manual states that it can create race conditions.  Instead,
declare as a prerequisite for each directory its parent directory,
and let make resolve the order of creation.

Also, use ':=' instead of '=' to improve performance, by
evaluating each assignment only once.

Ensure than the shell is not called when not needed, by removing
all ";" and quotes in the commands.

See also: <https://stackoverflow.com/q/67862417/6872717>

Signed-off-by: Alejandro Colomar <alx.manpages@gmail.com>
---


v2:  - Revert changes to 'make [all]', as it might break existing
       scripts.

v3:  - Remove unnecessary target installdirs (its functionality has been
       superseeded by installdirs-man).

v4:  - srcfix

v5:  - wsfix
     - Remove duplicate echo

v6:  - Add paragraph to commit msg.

v7:  - Output which files are removed (one RM line for each file)
     - Add manext variable, to avoid repeating the \.[0-9] regex.
     - Don't use ?=.  It's ugly, and makes no tangible effects
       (I tested that the times are the same).

v8:  - wsfix
     - Replace small shell scripts by proper Makefile dependencies
       and helper targets.

v9:  - wsfix
     - Sort files, so that the installation is more predictable, and
       thus, one can guess the progress of the install by the filenames
       in the log.

v10: - srcfix: use patsubst instead of addprefix + notdir, to be more
       clear.

v11: - Reorder things a bit to make it easier to read.
     - Remove an unnecessary dependency.

v12: - tfix

v13: - Remove unused variable: manext
     - Define undefined variables: man[1-8]ext
     - A few minor srcfix
     - Add times comparison and output examples to commit msg.

v14: - Replace '=' with ':=' to improve performance.
       Times in commit msg updated for this change.
     - Add a note in the README that installation should use
       multiple threads for performance reasons.

v15: - Move some code into variables (times didn't change considerably).

v16: - Add missing .PHONY: $(uninstall_manpages)

v17: - Replace *_MANN by *_MANn, which is more readable.

v18: - Don't use bash(1).  We don't need pipefail now that I simplified
       the targets to have simple separate commands.  make(1) will
       now break installation if anything fails.  This cut times by
       around 40%.
     - Don't call echo(1).  Invoking a shell just to call echo(1) for
       every file is too expensive.  Use make(1)'s $(info ...) function.
       This cut times by another 40%.
     - Update the commit msg to reflect the times after these changes.
       For a single thread, it's using around a third of the time that
       v17 needed.
     - v18 applies on top of mtk's tree, unlike previous revisions,
       which applied on top of another patch of mine, which I dropped.

v19: - Remove `;` and `'` to avoid calling unnecessary shells.
     - The above improved performance by another 50% or so; update the
       commit msg with the new times.
     - Fix some dependencies.
     - Add installdirs again.
     - srcfix: reduce LOC

v20: - Workaround a bug in make 3.81 where a trailing slash is ignored.
       See <https://ismail.badawi.io/blog/automatic-directory-creation-in-make/>
       That helps workaround some ambiguity in the rules, wher a rule
       to uninstall a dir also matched the pattern rule to install that
       dir.

v21: - ffix

v22: - Avoid race conditions, by avoiding `mkdir -p` behavior.
     - Reduce LOCs by using make's `$(foreach ...)` and `.SECONDEXPANSION:`.
     - Add an empty recipe for each target that does nothing but declare
       dependencies, to avoid implicit rules.


 Makefile | 203 +++++++++++++++++++++++++++++++++++++++++++------------
 README   |   8 +++
 2 files changed, 167 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile
index 609009715..b5ff7e237 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,166 @@
-# Do not print "Entering directory ..."
 MAKEFLAGS += --no-print-directory
+MAKEFLAGS += --silent
 
-htmlbuilddir = $(CURDIR)/.html
-HTOPTS =
+ROOTDIR := $(CURDIR)
+htmlbuilddir := $(ROOTDIR)/.html
+HTOPTS :=
 
-DESTDIR =
-prefix = /usr/local
-datarootdir = $(prefix)/share
-docdir = $(datarootdir)/doc
-mandir = $(datarootdir)/man
-htmldir = $(docdir)
-htmldir_ = $(htmldir)/man
-htmlext = .html
+DESTDIR :=
+prefix := /usr/local
+datarootdir := $(prefix)/share
+docdir := $(datarootdir)/doc
+MANDIR := $(ROOTDIR)
+mandir := $(datarootdir)/man
+MAN1DIR := $(MANDIR)/man1
+MAN2DIR := $(MANDIR)/man2
+MAN3DIR := $(MANDIR)/man3
+MAN4DIR := $(MANDIR)/man4
+MAN5DIR := $(MANDIR)/man5
+MAN6DIR := $(MANDIR)/man6
+MAN7DIR := $(MANDIR)/man7
+MAN8DIR := $(MANDIR)/man8
+man1dir := $(mandir)/man1
+man2dir := $(mandir)/man2
+man3dir := $(mandir)/man3
+man4dir := $(mandir)/man4
+man5dir := $(mandir)/man5
+man6dir := $(mandir)/man6
+man7dir := $(mandir)/man7
+man8dir := $(mandir)/man8
+manext := \.[0-9]
+man1ext := .1
+man2ext := .2
+man3ext := .3
+man4ext := .4
+man5ext := .5
+man6ext := .6
+man7ext := .7
+man8ext := .8
+htmldir := $(docdir)
+htmldir_ := $(htmldir)/man
+htmlext := .html
+
+INSTALL := install
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -m 755 -d
+RM := rm -f
+RMDIR := rmdir --ignore-fail-on-non-empty
+
+MAN_SECTIONS := 1 2 3 4 5 6 7 8
 
-INSTALL = install
-INSTALL_DATA = $(INSTALL) -m 644
-INSTALL_DIR = $(INSTALL) -m 755 -d
 
 .PHONY: all
 all:
-	$(MAKE) uninstall;
-	$(MAKE) install;
+	$(MAKE) uninstall
+	$(MAKE) install
+
+
+%/.: $(dir %).
+	$(info -	INSTALL	$*/)
+	$(INSTALL_DIR) $*/
+
+
+.PHONY: install
+install: install-man | installdirs
+	@:
+
+.PHONY: installdirs
+installdirs: | installdirs-man
+	@:
+
+.PHONY: uninstall remove
+uninstall remove: uninstall-man
+	@:
+
+.PHONY: clean
+clean:
+	find man?/ -type f \
+	|while read f; do \
+		rm -f "$(htmlbuilddir)/$$f".*; \
+	done;
+
+################################################################################
+# man
+
+MANPAGES  := $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)$$' | sort)
+MAN1PAGES := $(filter %$(man1ext),$(MANPAGES))
+MAN2PAGES := $(filter %$(man2ext),$(MANPAGES))
+MAN3PAGES := $(filter %$(man3ext),$(MANPAGES))
+MAN4PAGES := $(filter %$(man4ext),$(MANPAGES))
+MAN5PAGES := $(filter %$(man5ext),$(MANPAGES))
+MAN6PAGES := $(filter %$(man6ext),$(MANPAGES))
+MAN7PAGES := $(filter %$(man7ext),$(MANPAGES))
+MAN8PAGES := $(filter %$(man8ext),$(MANPAGES))
+
+manpages  := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%,$(MANPAGES))
+man1pages := $(filter %$(man1ext),$(manpages))
+man2pages := $(filter %$(man2ext),$(manpages))
+man3pages := $(filter %$(man3ext),$(manpages))
+man4pages := $(filter %$(man4ext),$(manpages))
+man5pages := $(filter %$(man5ext),$(manpages))
+man6pages := $(filter %$(man6ext),$(manpages))
+man7pages := $(filter %$(man7ext),$(manpages))
+man8pages := $(filter %$(man8ext),$(manpages))
+
+.SECONDEXPANSION:
+$(manpages): $(DESTDIR)$(mandir)/%: $(MANDIR)/% | $$(@D)/.
+	$(info -	INSTALL	$@)
+	$(INSTALL_DATA) -T $(MANDIR)/$* $@
+
+
+INSTALL_MANn := $(foreach i,$(MAN_SECTIONS),install-man$(i))
+
+.PHONY: $(INSTALL_MANn)
+$(INSTALL_MANn): install-%: $$(%pages) | installdirs-%
+	@:
+
+.PHONY: install-man
+install-man: $(INSTALL_MANn)
+	@:
+
+
+INSTALLDIRS_MANn := $(foreach i,$(MAN_SECTIONS),installdirs-man$(i))
+
+.PHONY: $(INSTALLDIRS_MANn)
+$(INSTALLDIRS_MANn): installdirs-%: $(DESTDIR)$(mandir)/%/. | $(DESTDIR)$(mandir)/.
+	@:
+
+.PHONY: installdirs-man
+installdirs-man: $(INSTALL_MANn)
+	@:
+
+
+uninstall_manpages  := $(addprefix uninstall-,$(wildcard $(manpages)))
+uninstall_man1pages := $(filter %$(man1ext),$(uninstall_manpages))
+uninstall_man2pages := $(filter %$(man2ext),$(uninstall_manpages))
+uninstall_man3pages := $(filter %$(man3ext),$(uninstall_manpages))
+uninstall_man4pages := $(filter %$(man4ext),$(uninstall_manpages))
+uninstall_man5pages := $(filter %$(man5ext),$(uninstall_manpages))
+uninstall_man6pages := $(filter %$(man6ext),$(uninstall_manpages))
+uninstall_man7pages := $(filter %$(man7ext),$(uninstall_manpages))
+uninstall_man8pages := $(filter %$(man8ext),$(uninstall_manpages))
+
+.PHONY: $(uninstall_manpages)
+$(uninstall_manpages): uninstall-%:
+	$(info -	RM	$*)
+	$(RM) $*
+
+
+UNINSTALL_MANn := $(foreach i,$(MAN_SECTIONS),uninstall-man$(i))
+
+.PHONY: $(UNINSTALL_MANn)
+$(UNINSTALL_MANn): uninstall-%: $$(uninstall_%pages)
+	$(info -	RMDIR	$(DESTDIR)$(mandir)/$*/)
+	$(RMDIR) $(DESTDIR)$(mandir)/$*/ 2>/dev/null ||:
+
+.PHONY: uninstall-man
+uninstall-man: $(UNINSTALL_MANn)
+	$(info -	RMDIR	$(DESTDIR)$(mandir)/)
+	$(RMDIR) $(DESTDIR)$(mandir)/ ||:
+
+
+################################################################################
+# html
 
 # Use with
 #  make HTOPTS=whatever html
@@ -57,28 +197,6 @@ installdirs-html:
 		$(INSTALL_DIR) "$(DESTDIR)$(htmldir_)/$$d" || exit $$?; \
 	done;
 
-.PHONY: install
-install: | installdirs
-	find man?/ -type f \
-	|while read f; do \
-		$(INSTALL_DATA) -T "$$f" "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-	done;
-
-.PHONY: installdirs
-installdirs:
-	find man?/ -type d \
-	|while read d; do \
-		$(INSTALL_DIR) "$(DESTDIR)$(mandir)/$$d" || exit $$?; \
-	done;
-
-.PHONY: uninstall remove
-uninstall remove:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-		rm -f "$(DESTDIR)$(mandir)/$$f".* || exit $$?; \
-	done;
-
 .PHONY: uninstall-html
 uninstall-html:
 	find man?/ -type f \
@@ -86,12 +204,9 @@ uninstall-html:
 		rm -f "$(DESTDIR)$(htmldir_)/$$f".* || exit $$?; \
 	done;
 
-.PHONY: clean
-clean:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(htmlbuilddir)/$$f".* || exit $$?; \
-	done;
+
+################################################################################
+# tests
 
 # Check if groff reports warnings (may be words of sentences not displayed)
 # from https://lintian.debian.org/tags/groff-message.html
diff --git a/README b/README
index 6598170c0..484151773 100644
--- a/README
+++ b/README
@@ -26,9 +26,17 @@ To install to a path different from /usr/local, use
 distribution from its destination.  Use with caution, and remember to
 use "prefix" if desired, as with the "install" target.
 
+To install only a specific man section (mandir) such as man3, use
+"make install-man3".  Similar syntax can be used to uninstall a
+specific man section, such as man7: "make uninstall-man7".
+
 "make" or "make all" will perform "make uninstall" followed by "make
 install".
 
+Consider using multiple threads (at least 2) when installing
+these man pages, as the Makefile is optimized for multiple threads:
+"make -j install".
+
 Copyrights
 ==========
 See the 'man-pages-x.y.Announce' file.
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v23] Makefile, README: Break installation into a target for each mandir
  2021-06-04 20:53 [PATCH] Makefile, README: Break installation into a target for each mandir Alejandro Colomar
                   ` (3 preceding siblings ...)
  2021-06-07 21:36 ` [PATCH v22] " Alejandro Colomar
@ 2021-06-07 21:49 ` Alejandro Colomar
  2021-06-08  9:34 ` [PATCH v24] " Alejandro Colomar
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Alejandro Colomar @ 2021-06-07 21:49 UTC (permalink / raw)
  To: mtk.manpages; +Cc: Alejandro Colomar, linux-man, Dr . Tobias Quathamer

Instead of having a monolithic 'make install', break it into
multiple targets such as 'make install-man3'.  This simplifies
packaging, for example in Debian, where they break this project
into several packages: 'manpages' and 'manpages-dev', each
containinng different mandirs.

The above allows for multithread installation: 'make -j8'

Also, don't overwrite files that don't need to be overwritten, by
having a target for files, which makes use of make's timestamp
comparison.

This allows for much faster installation times.

For comparison, on my laptop (i7-8850H; 6C/12T):

Old Makefile:
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.509s
	user	0m5.269s
	sys	0m2.614s

	The times with the old makefile, varied a lot, between
	5 and 10 seconds.  The times after applying this patch
	are much more consistent.  BTW, I compared these times to
	the very old Makefile of man-pages-5-09, and those were
	around 3.5 s, so it was a bit of my fault to have such a
	slow Makefile, when I changed the Makefile some weeks ago.

New Makefile (full clean install):
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m5.072s
	user	0m4.356s
	sys	0m0.980s
	~/src/linux/man-pages$ time sudo make -j2 >/dev/null

	real	0m1.688s
	user	0m2.616s
	sys	0m0.283s
	~/src/linux/man-pages$ time sudo make -j >/dev/null

	real	0m1.468s
	user	0m2.579s
	sys	0m0.289s

	Here we can see that 'make -j' drops times drastically,
	compared to the old monolithic Makefile.  Not only that,
	but since when we are working with the man pages there
	aren't many pages involved, times will be even better.

	Here are some times with a single page changed (touched):

New Makefile (one page touched):
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.976s
	user	0m0.952s
	sys	0m0.027s
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make -j install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.967s
	user	0m0.964s
	sys	0m0.007s

Also, modify the output of the make install and uninstall commands
so that a line is output for each file or directory that is
installed, similarly to the kernel's Makefile.  This doesn't apply
to html targets, which haven't been changed in this commit.

Also, make sure that for each invokation of $(INSTALL_DIR), no
parents are created, (i.e., avoid `mkdir -p` behavior).  The GNU
make manual states that it can create race conditions.  Instead,
declare as a prerequisite for each directory its parent directory,
and let make resolve the order of creation.

Also, use ':=' instead of '=' to improve performance, by
evaluating each assignment only once.

Ensure than the shell is not called when not needed, by removing
all ";" and quotes in the commands.

See also: <https://stackoverflow.com/q/67862417/6872717>

Signed-off-by: Alejandro Colomar <alx.manpages@gmail.com>
---


v2:  - Revert changes to 'make [all]', as it might break existing
       scripts.

v3:  - Remove unnecessary target installdirs (its functionality has been
       superseeded by installdirs-man).

v4:  - srcfix

v5:  - wsfix
     - Remove duplicate echo

v6:  - Add paragraph to commit msg.

v7:  - Output which files are removed (one RM line for each file)
     - Add manext variable, to avoid repeating the \.[0-9] regex.
     - Don't use ?=.  It's ugly, and makes no tangible effects
       (I tested that the times are the same).

v8:  - wsfix
     - Replace small shell scripts by proper Makefile dependencies
       and helper targets.

v9:  - wsfix
     - Sort files, so that the installation is more predictable, and
       thus, one can guess the progress of the install by the filenames
       in the log.

v10: - srcfix: use patsubst instead of addprefix + notdir, to be more
       clear.

v11: - Reorder things a bit to make it easier to read.
     - Remove an unnecessary dependency.

v12: - tfix

v13: - Remove unused variable: manext
     - Define undefined variables: man[1-8]ext
     - A few minor srcfix
     - Add times comparison and output examples to commit msg.

v14: - Replace '=' with ':=' to improve performance.
       Times in commit msg updated for this change.
     - Add a note in the README that installation should use
       multiple threads for performance reasons.

v15: - Move some code into variables (times didn't change considerably).

v16: - Add missing .PHONY: $(uninstall_manpages)

v17: - Replace *_MANN by *_MANn, which is more readable.

v18: - Don't use bash(1).  We don't need pipefail now that I simplified
       the targets to have simple separate commands.  make(1) will
       now break installation if anything fails.  This cut times by
       around 40%.
     - Don't call echo(1).  Invoking a shell just to call echo(1) for
       every file is too expensive.  Use make(1)'s $(info ...) function.
       This cut times by another 40%.
     - Update the commit msg to reflect the times after these changes.
       For a single thread, it's using around a third of the time that
       v17 needed.
     - v18 applies on top of mtk's tree, unlike previous revisions,
       which applied on top of another patch of mine, which I dropped.

v19: - Remove `;` and `'` to avoid calling unnecessary shells.
     - The above improved performance by another 50% or so; update the
       commit msg with the new times.
     - Fix some dependencies.
     - Add installdirs again.
     - srcfix: reduce LOC

v20: - Workaround a bug in make 3.81 where a trailing slash is ignored.
       See <https://ismail.badawi.io/blog/automatic-directory-creation-in-make/>
       That helps workaround some ambiguity in the rules, wher a rule
       to uninstall a dir also matched the pattern rule to install that
       dir.

v21: - ffix

v22: - Avoid race conditions, by avoiding `mkdir -p` behavior.
     - Reduce LOCs by using make's `$(foreach ...)` and `.SECONDEXPANSION:`.
     - Add an empty recipe for each target that does nothing but declare
       dependencies, to avoid implicit rules.

v23: - Remove unused variables


 Makefile | 194 ++++++++++++++++++++++++++++++++++++++++++-------------
 README   |   8 +++
 2 files changed, 158 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile
index 609009715..4632d904c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,157 @@
-# Do not print "Entering directory ..."
 MAKEFLAGS += --no-print-directory
+MAKEFLAGS += --silent
 
-htmlbuilddir = $(CURDIR)/.html
-HTOPTS =
+ROOTDIR := $(CURDIR)
+htmlbuilddir := $(ROOTDIR)/.html
+HTOPTS :=
 
-DESTDIR =
-prefix = /usr/local
-datarootdir = $(prefix)/share
-docdir = $(datarootdir)/doc
-mandir = $(datarootdir)/man
-htmldir = $(docdir)
-htmldir_ = $(htmldir)/man
-htmlext = .html
+DESTDIR :=
+prefix := /usr/local
+datarootdir := $(prefix)/share
+docdir := $(datarootdir)/doc
+MANDIR := $(ROOTDIR)
+mandir := $(datarootdir)/man
+MAN1DIR := $(MANDIR)/man1
+MAN2DIR := $(MANDIR)/man2
+MAN3DIR := $(MANDIR)/man3
+MAN4DIR := $(MANDIR)/man4
+MAN5DIR := $(MANDIR)/man5
+MAN6DIR := $(MANDIR)/man6
+MAN7DIR := $(MANDIR)/man7
+MAN8DIR := $(MANDIR)/man8
+man1dir := $(mandir)/man1
+man2dir := $(mandir)/man2
+man3dir := $(mandir)/man3
+man4dir := $(mandir)/man4
+man5dir := $(mandir)/man5
+man6dir := $(mandir)/man6
+man7dir := $(mandir)/man7
+man8dir := $(mandir)/man8
+manext := \.[0-9]
+man1ext := .1
+man2ext := .2
+man3ext := .3
+man4ext := .4
+man5ext := .5
+man6ext := .6
+man7ext := .7
+man8ext := .8
+htmldir := $(docdir)
+htmldir_ := $(htmldir)/man
+htmlext := .html
+
+INSTALL := install
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -m 755 -d
+RM := rm -f
+RMDIR := rmdir --ignore-fail-on-non-empty
+
+MAN_SECTIONS := 1 2 3 4 5 6 7 8
 
-INSTALL = install
-INSTALL_DATA = $(INSTALL) -m 644
-INSTALL_DIR = $(INSTALL) -m 755 -d
 
 .PHONY: all
 all:
-	$(MAKE) uninstall;
-	$(MAKE) install;
+	$(MAKE) uninstall
+	$(MAKE) install
+
+
+%/.: $(dir %).
+	$(info -	INSTALL	$*/)
+	$(INSTALL_DIR) $*/
+
+
+.PHONY: install
+install: install-man | installdirs
+	@:
+
+.PHONY: installdirs
+installdirs: | installdirs-man
+	@:
+
+.PHONY: uninstall remove
+uninstall remove: uninstall-man
+	@:
+
+.PHONY: clean
+clean:
+	find man?/ -type f \
+	|while read f; do \
+		rm -f "$(htmlbuilddir)/$$f".*; \
+	done;
+
+################################################################################
+# man
+
+MANPAGES  := $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)$$' | sort)
+manpages  := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%,$(MANPAGES))
+man1pages := $(filter %$(man1ext),$(manpages))
+man2pages := $(filter %$(man2ext),$(manpages))
+man3pages := $(filter %$(man3ext),$(manpages))
+man4pages := $(filter %$(man4ext),$(manpages))
+man5pages := $(filter %$(man5ext),$(manpages))
+man6pages := $(filter %$(man6ext),$(manpages))
+man7pages := $(filter %$(man7ext),$(manpages))
+man8pages := $(filter %$(man8ext),$(manpages))
+
+.SECONDEXPANSION:
+$(manpages): $(DESTDIR)$(mandir)/%: $(MANDIR)/% | $$(@D)/.
+	$(info -	INSTALL	$@)
+	$(INSTALL_DATA) -T $(MANDIR)/$* $@
+
+
+INSTALL_MANn := $(foreach i,$(MAN_SECTIONS),install-man$(i))
+
+.PHONY: $(INSTALL_MANn)
+$(INSTALL_MANn): install-%: $$(%pages) | installdirs-%
+	@:
+
+.PHONY: install-man
+install-man: $(INSTALL_MANn)
+	@:
+
+
+INSTALLDIRS_MANn := $(foreach i,$(MAN_SECTIONS),installdirs-man$(i))
+
+.PHONY: $(INSTALLDIRS_MANn)
+$(INSTALLDIRS_MANn): installdirs-%: $(DESTDIR)$(mandir)/%/. | $(DESTDIR)$(mandir)/.
+	@:
+
+.PHONY: installdirs-man
+installdirs-man: $(INSTALL_MANn)
+	@:
+
+
+uninstall_manpages  := $(addprefix uninstall-,$(wildcard $(manpages)))
+uninstall_man1pages := $(filter %$(man1ext),$(uninstall_manpages))
+uninstall_man2pages := $(filter %$(man2ext),$(uninstall_manpages))
+uninstall_man3pages := $(filter %$(man3ext),$(uninstall_manpages))
+uninstall_man4pages := $(filter %$(man4ext),$(uninstall_manpages))
+uninstall_man5pages := $(filter %$(man5ext),$(uninstall_manpages))
+uninstall_man6pages := $(filter %$(man6ext),$(uninstall_manpages))
+uninstall_man7pages := $(filter %$(man7ext),$(uninstall_manpages))
+uninstall_man8pages := $(filter %$(man8ext),$(uninstall_manpages))
+
+.PHONY: $(uninstall_manpages)
+$(uninstall_manpages): uninstall-%:
+	$(info -	RM	$*)
+	$(RM) $*
+
+
+UNINSTALL_MANn := $(foreach i,$(MAN_SECTIONS),uninstall-man$(i))
+
+.PHONY: $(UNINSTALL_MANn)
+$(UNINSTALL_MANn): uninstall-%: $$(uninstall_%pages)
+	$(info -	RMDIR	$(DESTDIR)$(mandir)/$*/)
+	$(RMDIR) $(DESTDIR)$(mandir)/$*/ 2>/dev/null ||:
+
+.PHONY: uninstall-man
+uninstall-man: $(UNINSTALL_MANn)
+	$(info -	RMDIR	$(DESTDIR)$(mandir)/)
+	$(RMDIR) $(DESTDIR)$(mandir)/ ||:
+
+
+################################################################################
+# html
 
 # Use with
 #  make HTOPTS=whatever html
@@ -57,28 +188,6 @@ installdirs-html:
 		$(INSTALL_DIR) "$(DESTDIR)$(htmldir_)/$$d" || exit $$?; \
 	done;
 
-.PHONY: install
-install: | installdirs
-	find man?/ -type f \
-	|while read f; do \
-		$(INSTALL_DATA) -T "$$f" "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-	done;
-
-.PHONY: installdirs
-installdirs:
-	find man?/ -type d \
-	|while read d; do \
-		$(INSTALL_DIR) "$(DESTDIR)$(mandir)/$$d" || exit $$?; \
-	done;
-
-.PHONY: uninstall remove
-uninstall remove:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-		rm -f "$(DESTDIR)$(mandir)/$$f".* || exit $$?; \
-	done;
-
 .PHONY: uninstall-html
 uninstall-html:
 	find man?/ -type f \
@@ -86,12 +195,9 @@ uninstall-html:
 		rm -f "$(DESTDIR)$(htmldir_)/$$f".* || exit $$?; \
 	done;
 
-.PHONY: clean
-clean:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(htmlbuilddir)/$$f".* || exit $$?; \
-	done;
+
+################################################################################
+# tests
 
 # Check if groff reports warnings (may be words of sentences not displayed)
 # from https://lintian.debian.org/tags/groff-message.html
diff --git a/README b/README
index 6598170c0..484151773 100644
--- a/README
+++ b/README
@@ -26,9 +26,17 @@ To install to a path different from /usr/local, use
 distribution from its destination.  Use with caution, and remember to
 use "prefix" if desired, as with the "install" target.
 
+To install only a specific man section (mandir) such as man3, use
+"make install-man3".  Similar syntax can be used to uninstall a
+specific man section, such as man7: "make uninstall-man7".
+
 "make" or "make all" will perform "make uninstall" followed by "make
 install".
 
+Consider using multiple threads (at least 2) when installing
+these man pages, as the Makefile is optimized for multiple threads:
+"make -j install".
+
 Copyrights
 ==========
 See the 'man-pages-x.y.Announce' file.
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v24] Makefile, README: Break installation into a target for each mandir
  2021-06-04 20:53 [PATCH] Makefile, README: Break installation into a target for each mandir Alejandro Colomar
                   ` (4 preceding siblings ...)
  2021-06-07 21:49 ` [PATCH v23] " Alejandro Colomar
@ 2021-06-08  9:34 ` Alejandro Colomar
  2021-06-08 16:51 ` [PATCH v25] " Alejandro Colomar
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: Alejandro Colomar @ 2021-06-08  9:34 UTC (permalink / raw)
  To: mtk.manpages; +Cc: Alejandro Colomar, linux-man, Dr . Tobias Quathamer

Instead of having a monolithic 'make install', break it into
multiple targets such as 'make install-man3'.  This simplifies
packaging, for example in Debian, where they break this project
into several packages: 'manpages' and 'manpages-dev', each
containinng different mandirs.

The above allows for multithread installation: 'make -j8'

Also, don't overwrite files that don't need to be overwritten, by
having a target for files, which makes use of make's timestamp
comparison.

This allows for much faster installation times.

For comparison, on my laptop (i7-8850H; 6C/12T):

Old Makefile:
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.509s
	user	0m5.269s
	sys	0m2.614s

	The times with the old makefile, varied a lot, between
	5 and 10 seconds.  The times after applying this patch
	are much more consistent.  BTW, I compared these times to
	the very old Makefile of man-pages-5-09, and those were
	around 3.5 s, so it was a bit of my fault to have such a
	slow Makefile, when I changed the Makefile some weeks ago.

New Makefile (full clean install):
	user@sqli:~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.818s
	user	0m6.250s
	sys	0m2.045s
	user@sqli:~/src/linux/man-pages$ time sudo make -j2 >/dev/null

	real	0m1.275s
	user	0m2.222s
	sys	0m0.331s
	user@sqli:~/src/linux/man-pages$ time sudo make -j >/dev/null

	real	0m0.938s
	user	0m2.328s
	sys	0m0.334s

	Here we can see that 'make -j' drops times drastically,
	compared to the old monolithic Makefile.  Not only that,
	but since when we are working with the man pages there
	aren't many pages involved, times will be even better.

	Here are some times with a single page changed (touched):

New Makefile (one page touched):
	user@sqli:~/src/linux/man-pages$ touch man2/membarrier.2
	user@sqli:~/src/linux/man-pages$ time sudo make install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.119s
	user	0m0.098s
	sys	0m0.025s
	user@sqli:~/src/linux/man-pages$ touch man2/membarrier.2
	user@sqli:~/src/linux/man-pages$ time sudo make -j install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.117s
	user	0m0.106s
	sys	0m0.015s

Also, modify the output of the make install and uninstall commands
so that a line is output for each file or directory that is
installed, similarly to the kernel's Makefile.  This doesn't apply
to html targets, which haven't been changed in this commit.

Also, make sure that for each invokation of $(INSTALL_DIR), no
parents are created, (i.e., avoid `mkdir -p` behavior).  The GNU
make manual states that it can create race conditions.  Instead,
declare as a prerequisite for each directory its parent directory,
and let make resolve the order of creation.

Also, use ':=' instead of '=' to improve performance, by
evaluating each assignment only once.

Ensure than the shell is not called when not needed, by removing
all ";" and quotes in the commands.

See also: <https://stackoverflow.com/q/67862417/6872717>

Signed-off-by: Alejandro Colomar <alx.manpages@gmail.com>
---


v2:  - Revert changes to 'make [all]', as it might break existing
       scripts.

v3:  - Remove unnecessary target installdirs (its functionality has been
       superseeded by installdirs-man).

v4:  - srcfix

v5:  - wsfix
     - Remove duplicate echo

v6:  - Add paragraph to commit msg.

v7:  - Output which files are removed (one RM line for each file)
     - Add manext variable, to avoid repeating the \.[0-9] regex.
     - Don't use ?=.  It's ugly, and makes no tangible effects
       (I tested that the times are the same).

v8:  - wsfix
     - Replace small shell scripts by proper Makefile dependencies
       and helper targets.

v9:  - wsfix
     - Sort files, so that the installation is more predictable, and
       thus, one can guess the progress of the install by the filenames
       in the log.

v10: - srcfix: use patsubst instead of addprefix + notdir, to be more
       clear.

v11: - Reorder things a bit to make it easier to read.
     - Remove an unnecessary dependency.

v12: - tfix

v13: - Remove unused variable: manext
     - Define undefined variables: man[1-8]ext
     - A few minor srcfix
     - Add times comparison and output examples to commit msg.

v14: - Replace '=' with ':=' to improve performance.
       Times in commit msg updated for this change.
     - Add a note in the README that installation should use
       multiple threads for performance reasons.

v15: - Move some code into variables (times didn't change considerably).

v16: - Add missing .PHONY: $(uninstall_manpages)

v17: - Replace *_MANN by *_MANn, which is more readable.

v18: - Don't use bash(1).  We don't need pipefail now that I simplified
       the targets to have simple separate commands.  make(1) will
       now break installation if anything fails.  This cut times by
       around 40%.
     - Don't call echo(1).  Invoking a shell just to call echo(1) for
       every file is too expensive.  Use make(1)'s $(info ...) function.
       This cut times by another 40%.
     - Update the commit msg to reflect the times after these changes.
       For a single thread, it's using around a third of the time that
       v17 needed.
     - v18 applies on top of mtk's tree, unlike previous revisions,
       which applied on top of another patch of mine, which I dropped.

v19: - Remove `;` and `'` to avoid calling unnecessary shells.
     - The above improved performance by another 50% or so; update the
       commit msg with the new times.
     - Fix some dependencies.
     - Add installdirs again.
     - srcfix: reduce LOC

v20: - Workaround a bug in make 3.81 where a trailing slash is ignored.
       See <https://ismail.badawi.io/blog/automatic-directory-creation-in-make/>
       That helps workaround some ambiguity in the rules, wher a rule
       to uninstall a dir also matched the pattern rule to install that
       dir.

v21: - ffix

v22: - Avoid race conditions, by avoiding `mkdir -p` behavior.
     - Reduce LOCs by using make's `$(foreach ...)` and `.SECONDEXPANSION:`.
     - Add an empty recipe for each target that does nothing but declare
       dependencies, to avoid implicit rules.

v23: - Remove unused variables

v24: - Improve readability
     - Fix dependencies
     - Time increased a bit for single-process full clean install,
       but they dropped to 10% for a single page, having times of
       around 0.1 s!  Update commit msg.


 Makefile | 210 +++++++++++++++++++++++++++++++++++++++++++------------
 README   |   8 +++
 2 files changed, 174 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile
index 609009715..58b01b7a4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,173 @@
-# Do not print "Entering directory ..."
 MAKEFLAGS += --no-print-directory
+MAKEFLAGS += --silent
 
-htmlbuilddir = $(CURDIR)/.html
-HTOPTS =
+ROOTDIR := $(CURDIR)
+htmlbuilddir := $(ROOTDIR)/.html
+HTOPTS :=
 
-DESTDIR =
-prefix = /usr/local
-datarootdir = $(prefix)/share
-docdir = $(datarootdir)/doc
-mandir = $(datarootdir)/man
-htmldir = $(docdir)
-htmldir_ = $(htmldir)/man
-htmlext = .html
+DESTDIR :=
+prefix := /usr/local
+datarootdir := $(prefix)/share
+docdir := $(datarootdir)/doc
+MANDIR := $(ROOTDIR)
+mandir := $(datarootdir)/man
+MAN1DIR := $(MANDIR)/man1
+MAN2DIR := $(MANDIR)/man2
+MAN3DIR := $(MANDIR)/man3
+MAN4DIR := $(MANDIR)/man4
+MAN5DIR := $(MANDIR)/man5
+MAN6DIR := $(MANDIR)/man6
+MAN7DIR := $(MANDIR)/man7
+MAN8DIR := $(MANDIR)/man8
+man1dir := $(mandir)/man1
+man2dir := $(mandir)/man2
+man3dir := $(mandir)/man3
+man4dir := $(mandir)/man4
+man5dir := $(mandir)/man5
+man6dir := $(mandir)/man6
+man7dir := $(mandir)/man7
+man8dir := $(mandir)/man8
+manext := \.[0-9]
+man1ext := .1
+man2ext := .2
+man3ext := .3
+man4ext := .4
+man5ext := .5
+man6ext := .6
+man7ext := .7
+man8ext := .8
+htmldir := $(docdir)
+htmldir_ := $(htmldir)/man
+htmlext := .html
+
+INSTALL := install
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -m 755 -d
+RM := rm -f
+RMDIR := rmdir --ignore-fail-on-non-empty
+
+MAN_SECTIONS := 1 2 3 4 5 6 7 8
 
-INSTALL = install
-INSTALL_DATA = $(INSTALL) -m 644
-INSTALL_DIR = $(INSTALL) -m 755 -d
 
 .PHONY: all
 all:
-	$(MAKE) uninstall;
-	$(MAKE) install;
+	$(MAKE) uninstall
+	$(MAKE) install
+
+
+%/.:
+	$(info -	INSTALL	$*/)
+	$(INSTALL_DIR) $*/
+
+.SECONDEXPANSION:
+%$(man1ext) \
+%$(man2ext) \
+%$(man3ext) \
+%$(man4ext) \
+%$(man5ext) \
+%$(man6ext) \
+%$(man7ext) \
+%$(man8ext): | $$(@D)/.
+	$(info -	INSTALL	$@)
+	$(INSTALL_DATA) -T $^ $@
+
+rm-/%:
+	$(info -	RM	/$*)
+	$(RM) /$*
+
+rmdir-/%:
+	$(info -	RMDIR	/$*)
+	$(RMDIR) /$* 2>/dev/null ||:
+
+
+.PHONY: install
+install: install-man | installdirs
+	@:
+
+.PHONY: installdirs
+installdirs: | installdirs-man
+	@:
+
+.PHONY: uninstall remove
+uninstall remove: uninstall-man
+	@:
+
+.PHONY: clean
+clean:
+	find man?/ -type f \
+	|while read f; do \
+		rm -f "$(htmlbuilddir)/$$f".*; \
+	done;
+
+################################################################################
+# man
+
+MANPAGES  := $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)$$' | sort)
+manpages  := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%,$(MANPAGES))
+man1pages := $(filter %$(man1ext),$(manpages))
+man2pages := $(filter %$(man2ext),$(manpages))
+man3pages := $(filter %$(man3ext),$(manpages))
+man4pages := $(filter %$(man4ext),$(manpages))
+man5pages := $(filter %$(man5ext),$(manpages))
+man6pages := $(filter %$(man6ext),$(manpages))
+man7pages := $(filter %$(man7ext),$(manpages))
+man8pages := $(filter %$(man8ext),$(manpages))
+
+$(manpages): $(DESTDIR)$(mandir)/%: $(MANDIR)/%
+
+
+install_manX := $(foreach x,$(MAN_SECTIONS),install-man$(x))
+
+.PHONY: $(install_manX)
+$(install_manX): install-%: $$(%pages) | installdirs-%
+	@:
+
+.PHONY: install-man
+install-man: $(install_manX)
+	@:
+
+
+MANDIRS := $(shell find $(MANDIR)/man? -type d)
+mandirs := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%/,$(MANDIRS))
+
+$(mandirs:=.): %/.: | $$(dir %). $(mandir)
+
+
+installdirs_manX := $(foreach x,$(MAN_SECTIONS),installdirs-man$(x))
+
+.PHONY: $(installdirs_manX)
+$(installdirs_manX): installdirs-%: $(DESTDIR)$(mandir)/%/. | $(DESTDIR)$(mandir)/.
+	@:
+
+.PHONY: installdirs-man
+installdirs-man: $(installdirs_manX)
+	@:
+
+
+rm_manpages  := $(addprefix rm-,$(wildcard $(manpages)))
+rm_man1pages := $(filter %$(man1ext),$(rm_manpages))
+rm_man2pages := $(filter %$(man2ext),$(rm_manpages))
+rm_man3pages := $(filter %$(man3ext),$(rm_manpages))
+rm_man4pages := $(filter %$(man4ext),$(rm_manpages))
+rm_man5pages := $(filter %$(man5ext),$(rm_manpages))
+rm_man6pages := $(filter %$(man6ext),$(rm_manpages))
+rm_man7pages := $(filter %$(man7ext),$(rm_manpages))
+rm_man8pages := $(filter %$(man8ext),$(rm_manpages))
+
+
+uninstall_manX := $(foreach x,$(MAN_SECTIONS),uninstall-man$(x))
+
+.PHONY: $(uninstall_manX)
+$(uninstall_manX): uninstall-%: $$(rm_%pages)
+	$(MAKE) rmdir-$(DESTDIR)$(mandir)/$*/
+
+.PHONY: uninstall-man
+uninstall-man: $(uninstall_manX)
+	$(MAKE) rmdir-$(DESTDIR)$(mandir)/
+
+
+################################################################################
+# html
 
 # Use with
 #  make HTOPTS=whatever html
@@ -57,28 +204,6 @@ installdirs-html:
 		$(INSTALL_DIR) "$(DESTDIR)$(htmldir_)/$$d" || exit $$?; \
 	done;
 
-.PHONY: install
-install: | installdirs
-	find man?/ -type f \
-	|while read f; do \
-		$(INSTALL_DATA) -T "$$f" "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-	done;
-
-.PHONY: installdirs
-installdirs:
-	find man?/ -type d \
-	|while read d; do \
-		$(INSTALL_DIR) "$(DESTDIR)$(mandir)/$$d" || exit $$?; \
-	done;
-
-.PHONY: uninstall remove
-uninstall remove:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-		rm -f "$(DESTDIR)$(mandir)/$$f".* || exit $$?; \
-	done;
-
 .PHONY: uninstall-html
 uninstall-html:
 	find man?/ -type f \
@@ -86,12 +211,9 @@ uninstall-html:
 		rm -f "$(DESTDIR)$(htmldir_)/$$f".* || exit $$?; \
 	done;
 
-.PHONY: clean
-clean:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(htmlbuilddir)/$$f".* || exit $$?; \
-	done;
+
+################################################################################
+# tests
 
 # Check if groff reports warnings (may be words of sentences not displayed)
 # from https://lintian.debian.org/tags/groff-message.html
diff --git a/README b/README
index 6598170c0..484151773 100644
--- a/README
+++ b/README
@@ -26,9 +26,17 @@ To install to a path different from /usr/local, use
 distribution from its destination.  Use with caution, and remember to
 use "prefix" if desired, as with the "install" target.
 
+To install only a specific man section (mandir) such as man3, use
+"make install-man3".  Similar syntax can be used to uninstall a
+specific man section, such as man7: "make uninstall-man7".
+
 "make" or "make all" will perform "make uninstall" followed by "make
 install".
 
+Consider using multiple threads (at least 2) when installing
+these man pages, as the Makefile is optimized for multiple threads:
+"make -j install".
+
 Copyrights
 ==========
 See the 'man-pages-x.y.Announce' file.
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v25] Makefile, README: Break installation into a target for each mandir
  2021-06-04 20:53 [PATCH] Makefile, README: Break installation into a target for each mandir Alejandro Colomar
                   ` (5 preceding siblings ...)
  2021-06-08  9:34 ` [PATCH v24] " Alejandro Colomar
@ 2021-06-08 16:51 ` Alejandro Colomar
  2021-06-08 21:55 ` [PATCH v26] " Alejandro Colomar
  2021-06-09 17:01 ` [PATCH v27] " Alejandro Colomar
  8 siblings, 0 replies; 11+ messages in thread
From: Alejandro Colomar @ 2021-06-08 16:51 UTC (permalink / raw)
  To: mtk.manpages; +Cc: Alejandro Colomar, linux-man, Dr . Tobias Quathamer

Instead of having a monolithic 'make install', break it into
multiple targets such as 'make install-man3'.  This simplifies
packaging, for example in Debian, where they break this project
into several packages: 'manpages' and 'manpages-dev', each
containinng different mandirs.

The above allows for multithread installation: 'make -j8'

Also, don't overwrite files that don't need to be overwritten, by
having a target for files, which makes use of make's timestamp
comparison.

This allows for much faster installation times.

For comparison, on my laptop (i7-8850H; 6C/12T):

Old Makefile:
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.509s
	user	0m5.269s
	sys	0m2.614s

	The times with the old makefile, varied a lot, between
	5 and 10 seconds.  The times after applying this patch
	are much more consistent.  BTW, I compared these times to
	the very old Makefile of man-pages-5-09, and those were
	around 3.5 s, so it was a bit of my fault to have such a
	slow Makefile, when I changed the Makefile some weeks ago.

New Makefile (full clean install):
	user@sqli:~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.818s
	user	0m6.250s
	sys	0m2.045s
	user@sqli:~/src/linux/man-pages$ time sudo make -j2 >/dev/null

	real	0m1.275s
	user	0m2.222s
	sys	0m0.331s
	user@sqli:~/src/linux/man-pages$ time sudo make -j >/dev/null

	real	0m0.938s
	user	0m2.328s
	sys	0m0.334s

	Here we can see that 'make -j' drops times drastically,
	compared to the old monolithic Makefile.  Not only that,
	but since when we are working with the man pages there
	aren't many pages involved, times will be even better.

	Here are some times with a single page changed (touched):

New Makefile (one page touched):
	user@sqli:~/src/linux/man-pages$ touch man2/membarrier.2
	user@sqli:~/src/linux/man-pages$ time sudo make install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.119s
	user	0m0.098s
	sys	0m0.025s
	user@sqli:~/src/linux/man-pages$ touch man2/membarrier.2
	user@sqli:~/src/linux/man-pages$ time sudo make -j install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.117s
	user	0m0.106s
	sys	0m0.015s

Also, modify the output of the make install and uninstall commands
so that a line is output for each file or directory that is
installed, similarly to the kernel's Makefile.  This doesn't apply
to html targets, which haven't been changed in this commit.

Also, make sure that for each invokation of $(INSTALL_DIR), no
parents are created, (i.e., avoid `mkdir -p` behavior).  The GNU
make manual states that it can create race conditions.  Instead,
declare as a prerequisite for each directory its parent directory,
and let make resolve the order of creation.

Also, use ':=' instead of '=' to improve performance, by
evaluating each assignment only once.

Ensure than the shell is not called when not needed, by removing
all ";" and quotes in the commands.

See also: <https://stackoverflow.com/q/67862417/6872717>

Signed-off-by: Alejandro Colomar <alx.manpages@gmail.com>
---


v2:  - Revert changes to 'make [all]', as it might break existing
       scripts.

v3:  - Remove unnecessary target installdirs (its functionality has been
       superseeded by installdirs-man).

v4:  - srcfix

v5:  - wsfix
     - Remove duplicate echo

v6:  - Add paragraph to commit msg.

v7:  - Output which files are removed (one RM line for each file)
     - Add manext variable, to avoid repeating the \.[0-9] regex.
     - Don't use ?=.  It's ugly, and makes no tangible effects
       (I tested that the times are the same).

v8:  - wsfix
     - Replace small shell scripts by proper Makefile dependencies
       and helper targets.

v9:  - wsfix
     - Sort files, so that the installation is more predictable, and
       thus, one can guess the progress of the install by the filenames
       in the log.

v10: - srcfix: use patsubst instead of addprefix + notdir, to be more
       clear.

v11: - Reorder things a bit to make it easier to read.
     - Remove an unnecessary dependency.

v12: - tfix

v13: - Remove unused variable: manext
     - Define undefined variables: man[1-8]ext
     - A few minor srcfix
     - Add times comparison and output examples to commit msg.

v14: - Replace '=' with ':=' to improve performance.
       Times in commit msg updated for this change.
     - Add a note in the README that installation should use
       multiple threads for performance reasons.

v15: - Move some code into variables (times didn't change considerably).

v16: - Add missing .PHONY: $(uninstall_manpages)

v17: - Replace *_MANN by *_MANn, which is more readable.

v18: - Don't use bash(1).  We don't need pipefail now that I simplified
       the targets to have simple separate commands.  make(1) will
       now break installation if anything fails.  This cut times by
       around 40%.
     - Don't call echo(1).  Invoking a shell just to call echo(1) for
       every file is too expensive.  Use make(1)'s $(info ...) function.
       This cut times by another 40%.
     - Update the commit msg to reflect the times after these changes.
       For a single thread, it's using around a third of the time that
       v17 needed.
     - v18 applies on top of mtk's tree, unlike previous revisions,
       which applied on top of another patch of mine, which I dropped.

v19: - Remove `;` and `'` to avoid calling unnecessary shells.
     - The above improved performance by another 50% or so; update the
       commit msg with the new times.
     - Fix some dependencies.
     - Add installdirs again.
     - srcfix: reduce LOC

v20: - Workaround a bug in make 3.81 where a trailing slash is ignored.
       See <https://ismail.badawi.io/blog/automatic-directory-creation-in-make/>
       That helps workaround some ambiguity in the rules, wher a rule
       to uninstall a dir also matched the pattern rule to install that
       dir.

v21: - ffix

v22: - Avoid race conditions, by avoiding `mkdir -p` behavior.
     - Reduce LOCs by using make's `$(foreach ...)` and `.SECONDEXPANSION:`.
     - Add an empty recipe for each target that does nothing but declare
       dependencies, to avoid implicit rules.

v23: - Remove unused variables

v24: - Improve readability
     - Fix dependencies
     - Time increased a bit for single-process full clean install,
       but they dropped to 10% for a single page, having times of
       around 0.1 s!  Update commit msg.

v25: - Workaround some problems with make:
       - Instead of using a prefix in pattern rules, use a suffix.
       - Instead of using .PHONY for implicit rules, use FORCE.
     - Improve readability
     - Use make's $(sort ...) instead of sort(1).


 Makefile | 215 +++++++++++++++++++++++++++++++++++++++++++------------
 README   |   8 +++
 2 files changed, 179 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile
index 609009715..9eb439abc 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,175 @@
-# Do not print "Entering directory ..."
 MAKEFLAGS += --no-print-directory
+MAKEFLAGS += --silent
 
-htmlbuilddir = $(CURDIR)/.html
-HTOPTS =
+ROOTDIR := $(CURDIR)
+htmlbuilddir := $(ROOTDIR)/.html
+HTOPTS :=
 
-DESTDIR =
-prefix = /usr/local
-datarootdir = $(prefix)/share
-docdir = $(datarootdir)/doc
-mandir = $(datarootdir)/man
-htmldir = $(docdir)
-htmldir_ = $(htmldir)/man
-htmlext = .html
+DESTDIR :=
+prefix := /usr/local
+datarootdir := $(prefix)/share
+docdir := $(datarootdir)/doc
+MANDIR := $(ROOTDIR)
+mandir := $(datarootdir)/man
+MAN1DIR := $(MANDIR)/man1
+MAN2DIR := $(MANDIR)/man2
+MAN3DIR := $(MANDIR)/man3
+MAN4DIR := $(MANDIR)/man4
+MAN5DIR := $(MANDIR)/man5
+MAN6DIR := $(MANDIR)/man6
+MAN7DIR := $(MANDIR)/man7
+MAN8DIR := $(MANDIR)/man8
+man1dir := $(mandir)/man1
+man2dir := $(mandir)/man2
+man3dir := $(mandir)/man3
+man4dir := $(mandir)/man4
+man5dir := $(mandir)/man5
+man6dir := $(mandir)/man6
+man7dir := $(mandir)/man7
+man8dir := $(mandir)/man8
+manext := \.[0-9]
+man1ext := .1
+man2ext := .2
+man3ext := .3
+man4ext := .4
+man5ext := .5
+man6ext := .6
+man7ext := .7
+man8ext := .8
+htmldir := $(docdir)
+htmldir_ := $(htmldir)/man
+htmlext := .html
+
+INSTALL := install
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -m 755 -d
+RM := rm -f
+RMDIR := rmdir --ignore-fail-on-non-empty
+
+MAN_SECTIONS := 1 2 3 4 5 6 7 8
 
-INSTALL = install
-INSTALL_DATA = $(INSTALL) -m 644
-INSTALL_DIR = $(INSTALL) -m 755 -d
 
 .PHONY: all
 all:
-	$(MAKE) uninstall;
-	$(MAKE) install;
+	$(MAKE) uninstall
+	$(MAKE) install
+
+
+%/.:
+	$(info -	INSTALL	$(@D))
+	$(INSTALL_DIR) $(@D)
+
+.SECONDEXPANSION:
+%$(man1ext) \
+%$(man2ext) \
+%$(man3ext) \
+%$(man4ext) \
+%$(man5ext) \
+%$(man6ext) \
+%$(man7ext) \
+%$(man8ext): | $$(@D)/.
+	$(info -	INSTALL	$@)
+	$(INSTALL_DATA) -T $< $@
+
+%-rm:
+	$(info -	RM	$*)
+	$(RM) $*
+
+%-rmdir:
+	$(info -	RMDIR	$(@D))
+	$(RMDIR) $(@D) 2>/dev/null ||:
+
+
+.PHONY: install
+install: install-man | installdirs
+	@:
+
+.PHONY: installdirs
+installdirs: | installdirs-man
+	@:
+
+.PHONY: uninstall remove
+uninstall remove: uninstall-man
+	@:
+
+.PHONY: clean
+clean:
+	find man?/ -type f \
+	|while read f; do \
+		rm -f "$(htmlbuilddir)/$$f".*; \
+	done;
+
+########################################################################
+# man
+
+MANPAGES  := $(sort $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)$$'))
+manpages  := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%,$(MANPAGES))
+man1pages := $(filter %$(man1ext),$(manpages))
+man2pages := $(filter %$(man2ext),$(manpages))
+man3pages := $(filter %$(man3ext),$(manpages))
+man4pages := $(filter %$(man4ext),$(manpages))
+man5pages := $(filter %$(man5ext),$(manpages))
+man6pages := $(filter %$(man6ext),$(manpages))
+man7pages := $(filter %$(man7ext),$(manpages))
+man8pages := $(filter %$(man8ext),$(manpages))
+
+MANDIRS := $(sort $(shell find $(MANDIR)/man? -type d))
+mandirs := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%/.,$(MANDIRS))
+
+rm_manpages  := $(addsuffix -rm,$(wildcard $(manpages)))
+rm_man1pages := $(filter %$(man1ext)-rm,$(rm_manpages))
+rm_man2pages := $(filter %$(man2ext)-rm,$(rm_manpages))
+rm_man3pages := $(filter %$(man3ext)-rm,$(rm_manpages))
+rm_man4pages := $(filter %$(man4ext)-rm,$(rm_manpages))
+rm_man5pages := $(filter %$(man5ext)-rm,$(rm_manpages))
+rm_man6pages := $(filter %$(man6ext)-rm,$(rm_manpages))
+rm_man7pages := $(filter %$(man7ext)-rm,$(rm_manpages))
+rm_man8pages := $(filter %$(man8ext)-rm,$(rm_manpages))
+
+rmdir_mandirs := $(addsuffix -rmdir,$(wildcard $(mandirs)))
+
+install_manX     := $(foreach x,$(MAN_SECTIONS),install-man$(x))
+installdirs_manX := $(foreach x,$(MAN_SECTIONS),installdirs-man$(x))
+uninstall_manX   := $(foreach x,$(MAN_SECTIONS),uninstall-man$(x))
+
+
+$(manpages): $(DESTDIR)$(mandir)/%: $(MANDIR)/% FORCE
+
+$(mandirs): %/.: FORCE | $$(dir %). $(mandir)
+
+$(rmdir_mandirs): $(DESTDIR)$(mandir)/%/.-rmdir: $$(rm_%pages) FORCE
+$(DESTDIR)$(mandir)/.-rmdir: $(uninstall_manX) FORCE
+
+
+.PHONY: $(install_manX)
+$(install_manX): install-%: $$(%pages) | installdirs-%
+	@:
+
+.PHONY: install-man
+install-man: $(install_manX)
+	@:
+
+
+.PHONY: $(installdirs_manX)
+$(installdirs_manX): installdirs-%: $(DESTDIR)$(mandir)/%/. | $(DESTDIR)$(mandir)/.
+	@:
+
+.PHONY: installdirs-man
+installdirs-man: $(installdirs_manX)
+	@:
+
+
+.PHONY: $(uninstall_manX)
+$(uninstall_manX): uninstall-%: $(DESTDIR)$(mandir)/%/.-rmdir $$(rm_%pages)
+	@:
+
+.PHONY: uninstall-man
+uninstall-man: $(DESTDIR)$(mandir)/.-rmdir $(uninstall_manX)
+	@:
+
+
+########################################################################
+# html
 
 # Use with
 #  make HTOPTS=whatever html
@@ -57,28 +206,6 @@ installdirs-html:
 		$(INSTALL_DIR) "$(DESTDIR)$(htmldir_)/$$d" || exit $$?; \
 	done;
 
-.PHONY: install
-install: | installdirs
-	find man?/ -type f \
-	|while read f; do \
-		$(INSTALL_DATA) -T "$$f" "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-	done;
-
-.PHONY: installdirs
-installdirs:
-	find man?/ -type d \
-	|while read d; do \
-		$(INSTALL_DIR) "$(DESTDIR)$(mandir)/$$d" || exit $$?; \
-	done;
-
-.PHONY: uninstall remove
-uninstall remove:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-		rm -f "$(DESTDIR)$(mandir)/$$f".* || exit $$?; \
-	done;
-
 .PHONY: uninstall-html
 uninstall-html:
 	find man?/ -type f \
@@ -86,12 +213,9 @@ uninstall-html:
 		rm -f "$(DESTDIR)$(htmldir_)/$$f".* || exit $$?; \
 	done;
 
-.PHONY: clean
-clean:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(htmlbuilddir)/$$f".* || exit $$?; \
-	done;
+
+########################################################################
+# tests
 
 # Check if groff reports warnings (may be words of sentences not displayed)
 # from https://lintian.debian.org/tags/groff-message.html
@@ -109,3 +233,6 @@ check-groff-warnings:
 
 # someone might also want to look at /var/catman/cat2 or so ...
 # a problem is that the location of cat pages varies a lot
+
+########################################################################
+FORCE:
diff --git a/README b/README
index 6598170c0..484151773 100644
--- a/README
+++ b/README
@@ -26,9 +26,17 @@ To install to a path different from /usr/local, use
 distribution from its destination.  Use with caution, and remember to
 use "prefix" if desired, as with the "install" target.
 
+To install only a specific man section (mandir) such as man3, use
+"make install-man3".  Similar syntax can be used to uninstall a
+specific man section, such as man7: "make uninstall-man7".
+
 "make" or "make all" will perform "make uninstall" followed by "make
 install".
 
+Consider using multiple threads (at least 2) when installing
+these man pages, as the Makefile is optimized for multiple threads:
+"make -j install".
+
 Copyrights
 ==========
 See the 'man-pages-x.y.Announce' file.
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v26] Makefile, README: Break installation into a target for each mandir
  2021-06-04 20:53 [PATCH] Makefile, README: Break installation into a target for each mandir Alejandro Colomar
                   ` (6 preceding siblings ...)
  2021-06-08 16:51 ` [PATCH v25] " Alejandro Colomar
@ 2021-06-08 21:55 ` Alejandro Colomar
  2021-06-09 17:01 ` [PATCH v27] " Alejandro Colomar
  8 siblings, 0 replies; 11+ messages in thread
From: Alejandro Colomar @ 2021-06-08 21:55 UTC (permalink / raw)
  To: mtk.manpages; +Cc: Alejandro Colomar, linux-man, Dr . Tobias Quathamer

Instead of having a monolithic 'make install', break it into
multiple targets such as 'make install-man3'.  This simplifies
packaging, for example in Debian, where they break this project
into several packages: 'manpages' and 'manpages-dev', each
containinng different mandirs.

The above allows for multithread installation: 'make -j8'

Also, don't overwrite files that don't need to be overwritten, by
having a target for files, which makes use of make's timestamp
comparison.

This allows for much faster installation times.

For comparison, on my laptop (i7-8850H; 6C/12T):

Old Makefile:
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.509s
	user	0m5.269s
	sys	0m2.614s

	The times with the old makefile, varied a lot, between
	5 and 10 seconds.  The times after applying this patch
	are much more consistent.  BTW, I compared these times to
	the very old Makefile of man-pages-5-09, and those were
	around 3.5 s, so it was a bit of my fault to have such a
	slow Makefile, when I changed the Makefile some weeks ago.

New Makefile (full clean install):
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.818s
	user	0m6.250s
	sys	0m2.045s
	~/src/linux/man-pages$ time sudo make -j2 >/dev/null

	real	0m1.275s
	user	0m2.222s
	sys	0m0.331s
	~/src/linux/man-pages$ time sudo make -j >/dev/null

	real	0m0.938s
	user	0m2.328s
	sys	0m0.334s

	Here we can see that 'make -j' drops times drastically,
	compared to the old monolithic Makefile.  Not only that,
	but since when we are working with the man pages there
	aren't many pages involved, times will be even better.

	Here are some times with a single page changed (touched):

New Makefile (one page touched):
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.119s
	user	0m0.098s
	sys	0m0.025s
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make -j install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.117s
	user	0m0.106s
	sys	0m0.015s

Also, modify the output of the make install and uninstall commands
so that a line is output for each file or directory that is
installed, similarly to the kernel's Makefile.  This doesn't apply
to html targets, which haven't been changed in this commit.

Also, make sure that for each invokation of $(INSTALL_DIR), no
parents are created, (i.e., avoid `mkdir -p` behavior).  The GNU
make manual states that it can create race conditions.  Instead,
declare as a prerequisite for each directory its parent directory,
and let make resolve the order of creation.

Also, use ':=' instead of '=' to improve performance, by
evaluating each assignment only once.

Ensure than the shell is not called when not needed, by removing
all ";" and quotes in the commands.

See also: <https://stackoverflow.com/q/67862417/6872717>

Specify conventions and rationales used in the Makefile in a comment.

Add copyright.

Signed-off-by: Alejandro Colomar <alx.manpages@gmail.com>
---


v2:  - Revert changes to 'make [all]', as it might break existing
       scripts.

v3:  - Remove unnecessary target installdirs (its functionality has been
       superseeded by installdirs-man).

v4:  - srcfix

v5:  - wsfix
     - Remove duplicate echo

v6:  - Add paragraph to commit msg.

v7:  - Output which files are removed (one RM line for each file)
     - Add manext variable, to avoid repeating the \.[0-9] regex.
     - Don't use ?=.  It's ugly, and makes no tangible effects
       (I tested that the times are the same).

v8:  - wsfix
     - Replace small shell scripts by proper Makefile dependencies
       and helper targets.

v9:  - wsfix
     - Sort files, so that the installation is more predictable, and
       thus, one can guess the progress of the install by the filenames
       in the log.

v10: - srcfix: use patsubst instead of addprefix + notdir, to be more
       clear.

v11: - Reorder things a bit to make it easier to read.
     - Remove an unnecessary dependency.

v12: - tfix

v13: - Remove unused variable: manext
     - Define undefined variables: man[1-8]ext
     - A few minor srcfix
     - Add times comparison and output examples to commit msg.

v14: - Replace '=' with ':=' to improve performance.
       Times in commit msg updated for this change.
     - Add a note in the README that installation should use
       multiple threads for performance reasons.

v15: - Move some code into variables (times didn't change considerably).

v16: - Add missing .PHONY: $(uninstall_manpages)

v17: - Replace *_MANN by *_MANn, which is more readable.

v18: - Don't use bash(1).  We don't need pipefail now that I simplified
       the targets to have simple separate commands.  make(1) will
       now break installation if anything fails.  This cut times by
       around 40%.
     - Don't call echo(1).  Invoking a shell just to call echo(1) for
       every file is too expensive.  Use make(1)'s $(info ...) function.
       This cut times by another 40%.
     - Update the commit msg to reflect the times after these changes.
       For a single thread, it's using around a third of the time that
       v17 needed.
     - v18 applies on top of mtk's tree, unlike previous revisions,
       which applied on top of another patch of mine, which I dropped.

v19: - Remove `;` and `'` to avoid calling unnecessary shells.
     - The above improved performance by another 50% or so; update the
       commit msg with the new times.
     - Fix some dependencies.
     - Add installdirs again.
     - srcfix: reduce LOC

v20: - Workaround a bug in make 3.81 where a trailing slash is ignored.
       See <https://ismail.badawi.io/blog/automatic-directory-creation-in-make/>
       That helps workaround some ambiguity in the rules, wher a rule
       to uninstall a dir also matched the pattern rule to install that
       dir.

v21: - ffix

v22: - Avoid race conditions, by avoiding `mkdir -p` behavior.
     - Reduce LOCs by using make's `$(foreach ...)` and `.SECONDEXPANSION:`.
     - Add an empty recipe for each target that does nothing but declare
       dependencies, to avoid implicit rules.

v23: - Remove unused variables

v24: - Improve readability
     - Fix dependencies
     - Time increased a bit for single-process full clean install,
       but they dropped to 10% for a single page, having times of
       around 0.1 s!  Update commit msg.

v25: - Workaround some problems with make:
       - Instead of using a prefix in pattern rules, use a suffix.
       - Instead of using .PHONY for implicit rules, use FORCE.
     - Improve readability
     - Use make's $(sort ...) instead of sort(1).

v26: - Fix dependencies.
     - Add copyright.
     - Add "Conventions" comment at the top of the Makefile.
     - Rename some variables to add consistency, and provide allow creating
       some simple conventions.
     - Add warning: --warn-undefined-variables

 Makefile | 260 +++++++++++++++++++++++++++++++++++++++++++++----------
 README   |   8 ++
 2 files changed, 224 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile
index 609009715..e93cb9364 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,219 @@
-# Do not print "Entering directory ..."
+########################################################################
+# Copyright (C) 2021        Alejandro Colomar <alx.manpages@gmail.com>
+# SPDX-License-Identifier:  GPL-2.0  OR  LGPL-2.0
+########################################################################
+# Conventions:
+#
+# - Follow "Makefile Conventions" from the "GNU Coding Standards" closely.
+#   However, when something could be improved, don't follow those.
+# - Uppercase variables, when referring files, refer to files in this repo.
+# - Lowercase variables, when referring files, refer to system files.
+# - Variables starting with '_' refer to absolute paths, including $(DESTDIR).
+# - Variables ending with '_' refer to a subdir of their parent dir, which
+#   is in a variable of the same name but without the '_'.  The subdir is
+#   named after this project: <*/man>.
+# - Variables ending in '_rm' refer to files that can be removed (exist).
+# - Variables ending in '_rmdir' refer to dirs that can be removed (exist).
+# - Targets of the form '%-rm' remove their corresponding file '%'.
+# - Targets of the form '%/.-rmdir' remove their corresponding dir '%/'.
+# - Targets of the form '%/.' create their corresponding directory '%/'.
+# - Every file or directory to be created depends on its directory.  This
+#   avoids race conditions caused by `mkdir -p`.  Only the root directories
+#   are created with parents.
+# - The 'FORCE' target is used to make phony some variables that can't be
+#   .PHONY to avoid some optimizations.
+#
+########################################################################
+
 MAKEFLAGS += --no-print-directory
+MAKEFLAGS += --silent
+MAKEFLAGS += --warn-undefined-variables
+
+
+htmlbuilddir := $(CURDIR)/.html
+HTOPTS :=
+
+DESTDIR :=
+prefix := /usr/local
+datarootdir := $(prefix)/share
+docdir := $(datarootdir)/doc
+MANDIR := $(CURDIR)
+mandir := $(datarootdir)/man
+MAN1DIR := $(MANDIR)/man1
+MAN2DIR := $(MANDIR)/man2
+MAN3DIR := $(MANDIR)/man3
+MAN4DIR := $(MANDIR)/man4
+MAN5DIR := $(MANDIR)/man5
+MAN6DIR := $(MANDIR)/man6
+MAN7DIR := $(MANDIR)/man7
+MAN8DIR := $(MANDIR)/man8
+man1dir := $(mandir)/man1
+man2dir := $(mandir)/man2
+man3dir := $(mandir)/man3
+man4dir := $(mandir)/man4
+man5dir := $(mandir)/man5
+man6dir := $(mandir)/man6
+man7dir := $(mandir)/man7
+man8dir := $(mandir)/man8
+manext := \.[0-9]
+man1ext := .1
+man2ext := .2
+man3ext := .3
+man4ext := .4
+man5ext := .5
+man6ext := .6
+man7ext := .7
+man8ext := .8
+htmldir := $(docdir)
+htmldir_ := $(htmldir)/man
+htmlext := .html
 
-htmlbuilddir = $(CURDIR)/.html
-HTOPTS =
+INSTALL := install
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -m 755 -d
+RM := rm
+RMDIR := rmdir --ignore-fail-on-non-empty
 
-DESTDIR =
-prefix = /usr/local
-datarootdir = $(prefix)/share
-docdir = $(datarootdir)/doc
-mandir = $(datarootdir)/man
-htmldir = $(docdir)
-htmldir_ = $(htmldir)/man
-htmlext = .html
+MAN_SECTIONS := 1 2 3 4 5 6 7 8
 
-INSTALL = install
-INSTALL_DATA = $(INSTALL) -m 644
-INSTALL_DIR = $(INSTALL) -m 755 -d
 
 .PHONY: all
 all:
-	$(MAKE) uninstall;
-	$(MAKE) install;
+	$(MAKE) uninstall
+	$(MAKE) install
+
+
+%/.:
+	$(info -	INSTALL	$(@D))
+	$(INSTALL_DIR) $(@D)
+
+.SECONDEXPANSION:
+%$(man1ext) \
+%$(man2ext) \
+%$(man3ext) \
+%$(man4ext) \
+%$(man5ext) \
+%$(man6ext) \
+%$(man7ext) \
+%$(man8ext): | $$(@D)/.
+	$(info -	INSTALL	$@)
+	$(INSTALL_DATA) -T $< $@
+
+%-rm:
+	$(info -	RM	$*)
+	$(RM) $*
+
+%-rmdir:
+	$(info -	RMDIR	$(@D))
+	$(RMDIR) $(@D)
+
+
+.PHONY: install
+install: install-man | installdirs
+	@:
+
+.PHONY: installdirs
+installdirs: | installdirs-man
+	@:
+
+.PHONY: uninstall remove
+uninstall remove: uninstall-man
+	@:
+
+.PHONY: clean
+clean:
+	find man?/ -type f \
+	|while read f; do \
+		rm -f "$(htmlbuilddir)/$$f".*; \
+	done;
+
+########################################################################
+# man
+
+MANPAGES   := $(sort $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)$$'))
+_manpages  := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%,$(MANPAGES))
+_man1pages := $(filter %$(man1ext),$(_manpages))
+_man2pages := $(filter %$(man2ext),$(_manpages))
+_man3pages := $(filter %$(man3ext),$(_manpages))
+_man4pages := $(filter %$(man4ext),$(_manpages))
+_man5pages := $(filter %$(man5ext),$(_manpages))
+_man6pages := $(filter %$(man6ext),$(_manpages))
+_man7pages := $(filter %$(man7ext),$(_manpages))
+_man8pages := $(filter %$(man8ext),$(_manpages))
+
+MANDIRS  := $(sort $(shell find $(MANDIR)/man? -type d))
+_mandirs := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%/.,$(MANDIRS))
+_man1dir := $(filter %man1/.,$(_mandirs))
+_man2dir := $(filter %man2/.,$(_mandirs))
+_man3dir := $(filter %man3/.,$(_mandirs))
+_man4dir := $(filter %man4/.,$(_mandirs))
+_man5dir := $(filter %man5/.,$(_mandirs))
+_man6dir := $(filter %man6/.,$(_mandirs))
+_man7dir := $(filter %man7/.,$(_mandirs))
+_man8dir := $(filter %man8/.,$(_mandirs))
+_mandir  := $(DESTDIR)$(mandir)/.
+
+_manpages_rm  := $(addsuffix -rm,$(wildcard $(_manpages)))
+_man1pages_rm := $(filter %$(man1ext)-rm,$(_manpages_rm))
+_man2pages_rm := $(filter %$(man2ext)-rm,$(_manpages_rm))
+_man3pages_rm := $(filter %$(man3ext)-rm,$(_manpages_rm))
+_man4pages_rm := $(filter %$(man4ext)-rm,$(_manpages_rm))
+_man5pages_rm := $(filter %$(man5ext)-rm,$(_manpages_rm))
+_man6pages_rm := $(filter %$(man6ext)-rm,$(_manpages_rm))
+_man7pages_rm := $(filter %$(man7ext)-rm,$(_manpages_rm))
+_man8pages_rm := $(filter %$(man8ext)-rm,$(_manpages_rm))
+
+_mandirs_rmdir := $(addsuffix -rmdir,$(wildcard $(_mandirs)))
+_man1dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man1dir)))
+_man2dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man2dir)))
+_man3dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man3dir)))
+_man4dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man4dir)))
+_man5dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man5dir)))
+_man6dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man6dir)))
+_man7dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man7dir)))
+_man8dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man8dir)))
+_mandir_rmdir  := $(addsuffix -rmdir,$(wildcard $(_mandir)))
+
+install_manX     := $(foreach x,$(MAN_SECTIONS),install-man$(x))
+installdirs_manX := $(foreach x,$(MAN_SECTIONS),installdirs-man$(x))
+uninstall_manX   := $(foreach x,$(MAN_SECTIONS),uninstall-man$(x))
+
+
+$(_manpages): $(DESTDIR)$(mandir)/%: $(MANDIR)/%
+
+$(_mandirs): %/.: | $$(dir %). $(_mandir)
+
+$(_mandirs_rmdir): $(DESTDIR)$(mandir)/%/.-rmdir: $$(_%pages_rm) FORCE
+$(_mandir_rmdir): $(uninstall_manX) FORCE
+
+
+.PHONY: $(install_manX)
+$(install_manX): install-%: $$(_%pages) | installdirs-%
+	@:
+
+.PHONY: install-man
+install-man: $(install_manX)
+	@:
+
+.PHONY: $(installdirs_manX)
+$(installdirs_manX): installdirs-%: $$(_%dir) $(_mandir)
+	@:
+
+.PHONY: installdirs-man
+installdirs-man: $(installdirs_manX)
+	@:
+
+.PHONY: $(uninstall_manX)
+$(uninstall_manX): uninstall-%: $$(_%pages_rm) $$(_%dir_rmdir)
+	@:
+
+.PHONY: uninstall-man
+uninstall-man: $(_mandir_rmdir) $(uninstall_manX)
+	@:
+
+
+########################################################################
+# html
 
 # Use with
 #  make HTOPTS=whatever html
@@ -57,28 +250,6 @@ installdirs-html:
 		$(INSTALL_DIR) "$(DESTDIR)$(htmldir_)/$$d" || exit $$?; \
 	done;
 
-.PHONY: install
-install: | installdirs
-	find man?/ -type f \
-	|while read f; do \
-		$(INSTALL_DATA) -T "$$f" "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-	done;
-
-.PHONY: installdirs
-installdirs:
-	find man?/ -type d \
-	|while read d; do \
-		$(INSTALL_DIR) "$(DESTDIR)$(mandir)/$$d" || exit $$?; \
-	done;
-
-.PHONY: uninstall remove
-uninstall remove:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-		rm -f "$(DESTDIR)$(mandir)/$$f".* || exit $$?; \
-	done;
-
 .PHONY: uninstall-html
 uninstall-html:
 	find man?/ -type f \
@@ -86,12 +257,9 @@ uninstall-html:
 		rm -f "$(DESTDIR)$(htmldir_)/$$f".* || exit $$?; \
 	done;
 
-.PHONY: clean
-clean:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(htmlbuilddir)/$$f".* || exit $$?; \
-	done;
+
+########################################################################
+# tests
 
 # Check if groff reports warnings (may be words of sentences not displayed)
 # from https://lintian.debian.org/tags/groff-message.html
@@ -109,3 +277,7 @@ check-groff-warnings:
 
 # someone might also want to look at /var/catman/cat2 or so ...
 # a problem is that the location of cat pages varies a lot
+
+########################################################################
+
+FORCE:
diff --git a/README b/README
index 6598170c0..484151773 100644
--- a/README
+++ b/README
@@ -26,9 +26,17 @@ To install to a path different from /usr/local, use
 distribution from its destination.  Use with caution, and remember to
 use "prefix" if desired, as with the "install" target.
 
+To install only a specific man section (mandir) such as man3, use
+"make install-man3".  Similar syntax can be used to uninstall a
+specific man section, such as man7: "make uninstall-man7".
+
 "make" or "make all" will perform "make uninstall" followed by "make
 install".
 
+Consider using multiple threads (at least 2) when installing
+these man pages, as the Makefile is optimized for multiple threads:
+"make -j install".
+
 Copyrights
 ==========
 See the 'man-pages-x.y.Announce' file.
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v27] Makefile, README: Break installation into a target for each mandir
  2021-06-04 20:53 [PATCH] Makefile, README: Break installation into a target for each mandir Alejandro Colomar
                   ` (7 preceding siblings ...)
  2021-06-08 21:55 ` [PATCH v26] " Alejandro Colomar
@ 2021-06-09 17:01 ` Alejandro Colomar
  2021-06-09 21:21   ` Michael Kerrisk (man-pages)
  8 siblings, 1 reply; 11+ messages in thread
From: Alejandro Colomar @ 2021-06-09 17:01 UTC (permalink / raw)
  To: mtk.manpages; +Cc: Alejandro Colomar, linux-man, Dr . Tobias Quathamer

Instead of having a monolithic 'make install', break it into
multiple targets such as 'make install-man3'.  This simplifies
packaging, for example in Debian, where they break this project
into several packages: 'manpages' and 'manpages-dev', each
containinng different mandirs.

The above allows for multithread installation: 'make -j'

Also, don't overwrite files that don't need to be overwritten, by
having a target for files, which makes use of make's timestamp
comparison.

This allows for much faster installation times.

For comparison, on my laptop (i7-8850H; 6C/12T):

Old Makefile:
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m7.509s
	user	0m5.269s
	sys	0m2.614s

	The times with the old makefile, varied a lot, between
	5 and 10 seconds.  The times after applying this patch
	are much more consistent.  BTW, I compared these times to
	the very old Makefile of man-pages-5-09, and those were
	around 3.5 s, so it was a bit of my fault to have such a
	slow Makefile, when I changed the Makefile some weeks ago.

New Makefile (full clean install):
	~/src/linux/man-pages$ time sudo make >/dev/null

	real	0m5.160s
	user	0m4.326s
	sys	0m1.137s
	~/src/linux/man-pages$ time sudo make -j2 >/dev/null

	real	0m1.602s
	user	0m2.529s
	sys	0m0.289s
	~/src/linux/man-pages$ time sudo make -j >/dev/null

	real	0m1.398s
	user	0m2.502s
	sys	0m0.281s

	Here we can see that 'make -j' drops times drastically,
	compared to the old monolithic Makefile.  Not only that,
	but since when we are working with the man pages there
	aren't many pages involved, times will be even better.

	Here are some times with a single page changed (touched):

New Makefile (one page touched):
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make install
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.988s
	user	0m0.966s
	sys	0m0.025s
	~/src/linux/man-pages$ touch man2/membarrier.2
	~/src/linux/man-pages$ time sudo make install -j
	-	INSTALL	/usr/local/share/man/man2/membarrier.2

	real	0m0.989s
	user	0m0.943s
	sys	0m0.049s

Also, modify the output of the make install and uninstall commands
so that a line is output for each file or directory that is
installed, similarly to the kernel's Makefile.  This doesn't apply
to html targets, which haven't been changed in this commit.

Also, make sure that for each invokation of $(INSTALL_DIR), no
parents are created, (i.e., avoid `mkdir -p` behavior).  The GNU
make manual states that it can create race conditions.  Instead,
declare as a prerequisite for each directory its parent directory,
and let make resolve the order of creation.

Also, use ':=' instead of '=' to improve performance, by
evaluating each assignment only once.

Ensure than the shell is not called when not needed, by removing
all ";" and quotes in the commands.

See also: <https://stackoverflow.com/q/67862417/6872717>

Specify conventions and rationales used in the Makefile in a comment.

Add copyright.

Signed-off-by: Alejandro Colomar <alx.manpages@gmail.com>
---


v27: - Remove pattern rule for installing files:  Installing is not something
       needed to create files with a specific extension, so it looks weird.
       Instead, put the installation command in a static pattern rule.
     - Use '%' as little as possible, to not hide information.  Be as specific
       as possible.
     - This improved single-process times, but worsened multi-process times.


 Makefile | 251 +++++++++++++++++++++++++++++++++++++++++++++----------
 README   |   8 ++
 2 files changed, 215 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile
index 609009715..b551e6c98 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,210 @@
-# Do not print "Entering directory ..."
+########################################################################
+# Copyright (C) 2021        Alejandro Colomar <alx.manpages@gmail.com>
+# SPDX-License-Identifier:  GPL-2.0  OR  LGPL-2.0
+########################################################################
+# Conventions:
+#
+# - Follow "Makefile Conventions" from the "GNU Coding Standards" closely.
+#   However, when something could be improved, don't follow those.
+# - Uppercase variables, when referring files, refer to files in this repo.
+# - Lowercase variables, when referring files, refer to system files.
+# - Variables starting with '_' refer to absolute paths, including $(DESTDIR).
+# - Variables ending with '_' refer to a subdir of their parent dir, which
+#   is in a variable of the same name but without the '_'.  The subdir is
+#   named after this project: <*/man>.
+# - Variables ending in '_rm' refer to files that can be removed (exist).
+# - Variables ending in '_rmdir' refer to dirs that can be removed (exist).
+# - Targets of the form '%-rm' remove their corresponding file '%'.
+# - Targets of the form '%/.-rmdir' remove their corresponding dir '%/'.
+# - Targets of the form '%/.' create their corresponding directory '%/'.
+# - Every file or directory to be created depends on its parent directory.
+#   This avoids race conditions caused by `mkdir -p`.  Only the root
+#   directories are created with parents.
+# - The 'FORCE' target is used to make phony some variables that can't be
+#   .PHONY to avoid some optimizations.
+#
+########################################################################
+
 MAKEFLAGS += --no-print-directory
+MAKEFLAGS += --silent
+MAKEFLAGS += --warn-undefined-variables
+
+
+htmlbuilddir := $(CURDIR)/.html
+HTOPTS :=
+
+DESTDIR :=
+prefix := /usr/local
+datarootdir := $(prefix)/share
+docdir := $(datarootdir)/doc
+MANDIR := $(CURDIR)
+mandir := $(datarootdir)/man
+MAN1DIR := $(MANDIR)/man1
+MAN2DIR := $(MANDIR)/man2
+MAN3DIR := $(MANDIR)/man3
+MAN4DIR := $(MANDIR)/man4
+MAN5DIR := $(MANDIR)/man5
+MAN6DIR := $(MANDIR)/man6
+MAN7DIR := $(MANDIR)/man7
+MAN8DIR := $(MANDIR)/man8
+man1dir := $(mandir)/man1
+man2dir := $(mandir)/man2
+man3dir := $(mandir)/man3
+man4dir := $(mandir)/man4
+man5dir := $(mandir)/man5
+man6dir := $(mandir)/man6
+man7dir := $(mandir)/man7
+man8dir := $(mandir)/man8
+manext := \.[0-9]
+man1ext := .1
+man2ext := .2
+man3ext := .3
+man4ext := .4
+man5ext := .5
+man6ext := .6
+man7ext := .7
+man8ext := .8
+htmldir := $(docdir)
+htmldir_ := $(htmldir)/man
+htmlext := .html
 
-htmlbuilddir = $(CURDIR)/.html
-HTOPTS =
+INSTALL := install
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -m 755 -d
+RM := rm
+RMDIR := rmdir --ignore-fail-on-non-empty
 
-DESTDIR =
-prefix = /usr/local
-datarootdir = $(prefix)/share
-docdir = $(datarootdir)/doc
-mandir = $(datarootdir)/man
-htmldir = $(docdir)
-htmldir_ = $(htmldir)/man
-htmlext = .html
+MAN_SECTIONS := 1 2 3 4 5 6 7 8
 
-INSTALL = install
-INSTALL_DATA = $(INSTALL) -m 644
-INSTALL_DIR = $(INSTALL) -m 755 -d
 
 .PHONY: all
 all:
-	$(MAKE) uninstall;
-	$(MAKE) install;
+	$(MAKE) uninstall
+	$(MAKE) install
+
+
+%/.:
+	$(info -	INSTALL	$(@D))
+	$(INSTALL_DIR) $(@D)
+
+%-rm:
+	$(info -	RM	$*)
+	$(RM) $*
+
+%-rmdir:
+	$(info -	RMDIR	$(@D))
+	$(RMDIR) $(@D)
+
+
+.PHONY: install
+install: install-man | installdirs
+	@:
+
+.PHONY: installdirs
+installdirs: | installdirs-man
+	@:
+
+.PHONY: uninstall remove
+uninstall remove: uninstall-man
+	@:
+
+.PHONY: clean
+clean:
+	find man?/ -type f \
+	|while read f; do \
+		rm -f "$(htmlbuilddir)/$$f".*; \
+	done;
+
+########################################################################
+# man
+
+MANPAGES   := $(sort $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)$$'))
+_manpages  := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%,$(MANPAGES))
+_man1pages := $(filter %$(man1ext),$(_manpages))
+_man2pages := $(filter %$(man2ext),$(_manpages))
+_man3pages := $(filter %$(man3ext),$(_manpages))
+_man4pages := $(filter %$(man4ext),$(_manpages))
+_man5pages := $(filter %$(man5ext),$(_manpages))
+_man6pages := $(filter %$(man6ext),$(_manpages))
+_man7pages := $(filter %$(man7ext),$(_manpages))
+_man8pages := $(filter %$(man8ext),$(_manpages))
+
+MANDIRS  := $(sort $(shell find $(MANDIR)/man? -type d))
+_mandirs := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%/.,$(MANDIRS))
+_man1dir := $(filter %man1/.,$(_mandirs))
+_man2dir := $(filter %man2/.,$(_mandirs))
+_man3dir := $(filter %man3/.,$(_mandirs))
+_man4dir := $(filter %man4/.,$(_mandirs))
+_man5dir := $(filter %man5/.,$(_mandirs))
+_man6dir := $(filter %man6/.,$(_mandirs))
+_man7dir := $(filter %man7/.,$(_mandirs))
+_man8dir := $(filter %man8/.,$(_mandirs))
+_mandir  := $(DESTDIR)$(mandir)/.
+
+_manpages_rm  := $(addsuffix -rm,$(wildcard $(_manpages)))
+_man1pages_rm := $(filter %$(man1ext)-rm,$(_manpages_rm))
+_man2pages_rm := $(filter %$(man2ext)-rm,$(_manpages_rm))
+_man3pages_rm := $(filter %$(man3ext)-rm,$(_manpages_rm))
+_man4pages_rm := $(filter %$(man4ext)-rm,$(_manpages_rm))
+_man5pages_rm := $(filter %$(man5ext)-rm,$(_manpages_rm))
+_man6pages_rm := $(filter %$(man6ext)-rm,$(_manpages_rm))
+_man7pages_rm := $(filter %$(man7ext)-rm,$(_manpages_rm))
+_man8pages_rm := $(filter %$(man8ext)-rm,$(_manpages_rm))
+
+_mandirs_rmdir := $(addsuffix -rmdir,$(wildcard $(_mandirs)))
+_man1dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man1dir)))
+_man2dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man2dir)))
+_man3dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man3dir)))
+_man4dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man4dir)))
+_man5dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man5dir)))
+_man6dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man6dir)))
+_man7dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man7dir)))
+_man8dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man8dir)))
+_mandir_rmdir  := $(addsuffix -rmdir,$(wildcard $(_mandir)))
+
+install_manX     := $(foreach x,$(MAN_SECTIONS),install-man$(x))
+installdirs_manX := $(foreach x,$(MAN_SECTIONS),installdirs-man$(x))
+uninstall_manX   := $(foreach x,$(MAN_SECTIONS),uninstall-man$(x))
+
+
+.SECONDEXPANSION:
+$(_manpages): $(DESTDIR)$(mandir)/man%: $(MANDIR)/man% | $$(@D)/.
+	$(info -	INSTALL	$@)
+	$(INSTALL_DATA) -T $< $@
+
+$(_mandirs): %/.: | $$(dir %). $(_mandir)
+
+$(_mandirs_rmdir): $(DESTDIR)$(mandir)/man%/.-rmdir: $$(_man%pages_rm) FORCE
+$(_mandir_rmdir): $(uninstall_manX) FORCE
+
+
+.PHONY: $(install_manX)
+$(install_manX): install-man%: $$(_man%pages) | installdirs-man%
+	@:
+
+.PHONY: install-man
+install-man: $(install_manX)
+	@:
+
+.PHONY: $(installdirs_manX)
+$(installdirs_manX): installdirs-man%: $$(_man%dir) $(_mandir)
+	@:
+
+.PHONY: installdirs-man
+installdirs-man: $(installdirs_manX)
+	@:
+
+.PHONY: $(uninstall_manX)
+$(uninstall_manX): uninstall-man%: $$(_man%pages_rm) $$(_man%dir_rmdir)
+	@:
+
+.PHONY: uninstall-man
+uninstall-man: $(_mandir_rmdir) $(uninstall_manX)
+	@:
+
+
+########################################################################
+# html
 
 # Use with
 #  make HTOPTS=whatever html
@@ -57,28 +241,6 @@ installdirs-html:
 		$(INSTALL_DIR) "$(DESTDIR)$(htmldir_)/$$d" || exit $$?; \
 	done;
 
-.PHONY: install
-install: | installdirs
-	find man?/ -type f \
-	|while read f; do \
-		$(INSTALL_DATA) -T "$$f" "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-	done;
-
-.PHONY: installdirs
-installdirs:
-	find man?/ -type d \
-	|while read d; do \
-		$(INSTALL_DIR) "$(DESTDIR)$(mandir)/$$d" || exit $$?; \
-	done;
-
-.PHONY: uninstall remove
-uninstall remove:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
-		rm -f "$(DESTDIR)$(mandir)/$$f".* || exit $$?; \
-	done;
-
 .PHONY: uninstall-html
 uninstall-html:
 	find man?/ -type f \
@@ -86,12 +248,9 @@ uninstall-html:
 		rm -f "$(DESTDIR)$(htmldir_)/$$f".* || exit $$?; \
 	done;
 
-.PHONY: clean
-clean:
-	find man?/ -type f \
-	|while read f; do \
-		rm -f "$(htmlbuilddir)/$$f".* || exit $$?; \
-	done;
+
+########################################################################
+# tests
 
 # Check if groff reports warnings (may be words of sentences not displayed)
 # from https://lintian.debian.org/tags/groff-message.html
@@ -109,3 +268,7 @@ check-groff-warnings:
 
 # someone might also want to look at /var/catman/cat2 or so ...
 # a problem is that the location of cat pages varies a lot
+
+########################################################################
+
+FORCE:
diff --git a/README b/README
index 6598170c0..484151773 100644
--- a/README
+++ b/README
@@ -26,9 +26,17 @@ To install to a path different from /usr/local, use
 distribution from its destination.  Use with caution, and remember to
 use "prefix" if desired, as with the "install" target.
 
+To install only a specific man section (mandir) such as man3, use
+"make install-man3".  Similar syntax can be used to uninstall a
+specific man section, such as man7: "make uninstall-man7".
+
 "make" or "make all" will perform "make uninstall" followed by "make
 install".
 
+Consider using multiple threads (at least 2) when installing
+these man pages, as the Makefile is optimized for multiple threads:
+"make -j install".
+
 Copyrights
 ==========
 See the 'man-pages-x.y.Announce' file.
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v27] Makefile, README: Break installation into a target for each mandir
  2021-06-09 17:01 ` [PATCH v27] " Alejandro Colomar
@ 2021-06-09 21:21   ` Michael Kerrisk (man-pages)
  0 siblings, 0 replies; 11+ messages in thread
From: Michael Kerrisk (man-pages) @ 2021-06-09 21:21 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: mtk.manpages, linux-man, Dr . Tobias Quathamer

Hello Alex,

On 6/10/21 5:01 AM, Alejandro Colomar wrote:
> Instead of having a monolithic 'make install', break it into
> multiple targets such as 'make install-man3'.  This simplifies
> packaging, for example in Debian, where they break this project
> into several packages: 'manpages' and 'manpages-dev', each
> containinng different mandirs.
> 
> The above allows for multithread installation: 'make -j'
> 
> Also, don't overwrite files that don't need to be overwritten, by
> having a target for files, which makes use of make's timestamp
> comparison.
> 
> This allows for much faster installation times.
> 
> For comparison, on my laptop (i7-8850H; 6C/12T):
> 
> Old Makefile:
> 	~/src/linux/man-pages$ time sudo make >/dev/null
> 
> 	real	0m7.509s
> 	user	0m5.269s
> 	sys	0m2.614s
> 
> 	The times with the old makefile, varied a lot, between
> 	5 and 10 seconds.  The times after applying this patch
> 	are much more consistent.  BTW, I compared these times to
> 	the very old Makefile of man-pages-5-09, and those were
> 	around 3.5 s, so it was a bit of my fault to have such a
> 	slow Makefile, when I changed the Makefile some weeks ago.
> 
> New Makefile (full clean install):
> 	~/src/linux/man-pages$ time sudo make >/dev/null
> 
> 	real	0m5.160s
> 	user	0m4.326s
> 	sys	0m1.137s
> 	~/src/linux/man-pages$ time sudo make -j2 >/dev/null
> 
> 	real	0m1.602s
> 	user	0m2.529s
> 	sys	0m0.289s
> 	~/src/linux/man-pages$ time sudo make -j >/dev/null
> 
> 	real	0m1.398s
> 	user	0m2.502s
> 	sys	0m0.281s
> 
> 	Here we can see that 'make -j' drops times drastically,
> 	compared to the old monolithic Makefile.  Not only that,
> 	but since when we are working with the man pages there
> 	aren't many pages involved, times will be even better.
> 
> 	Here are some times with a single page changed (touched):
> 
> New Makefile (one page touched):
> 	~/src/linux/man-pages$ touch man2/membarrier.2
> 	~/src/linux/man-pages$ time sudo make install
> 	-	INSTALL	/usr/local/share/man/man2/membarrier.2
> 
> 	real	0m0.988s
> 	user	0m0.966s
> 	sys	0m0.025s
> 	~/src/linux/man-pages$ touch man2/membarrier.2
> 	~/src/linux/man-pages$ time sudo make install -j
> 	-	INSTALL	/usr/local/share/man/man2/membarrier.2
> 
> 	real	0m0.989s
> 	user	0m0.943s
> 	sys	0m0.049s
> 
> Also, modify the output of the make install and uninstall commands
> so that a line is output for each file or directory that is
> installed, similarly to the kernel's Makefile.  This doesn't apply
> to html targets, which haven't been changed in this commit.
> 
> Also, make sure that for each invokation of $(INSTALL_DIR), no
> parents are created, (i.e., avoid `mkdir -p` behavior).  The GNU
> make manual states that it can create race conditions.  Instead,
> declare as a prerequisite for each directory its parent directory,
> and let make resolve the order of creation.
> 
> Also, use ':=' instead of '=' to improve performance, by
> evaluating each assignment only once.
> 
> Ensure than the shell is not called when not needed, by removing
> all ";" and quotes in the commands.
> 
> See also: <https://stackoverflow.com/q/67862417/6872717>
> 
> Specify conventions and rationales used in the Makefile in a comment.
> 
> Add copyright.
> 
> Signed-off-by: Alejandro Colomar <alx.manpages@gmail.com>

Patch applied.

Cheers,

Michael

> ---
> 
> 
> v27: - Remove pattern rule for installing files:  Installing is not something
>        needed to create files with a specific extension, so it looks weird.
>        Instead, put the installation command in a static pattern rule.
>      - Use '%' as little as possible, to not hide information.  Be as specific
>        as possible.
>      - This improved single-process times, but worsened multi-process times.
> 
> 
>  Makefile | 251 +++++++++++++++++++++++++++++++++++++++++++++----------
>  README   |   8 ++
>  2 files changed, 215 insertions(+), 44 deletions(-)
> 
> diff --git a/Makefile b/Makefile
> index 609009715..b551e6c98 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1,26 +1,210 @@
> -# Do not print "Entering directory ..."
> +########################################################################
> +# Copyright (C) 2021        Alejandro Colomar <alx.manpages@gmail.com>
> +# SPDX-License-Identifier:  GPL-2.0  OR  LGPL-2.0
> +########################################################################
> +# Conventions:
> +#
> +# - Follow "Makefile Conventions" from the "GNU Coding Standards" closely.
> +#   However, when something could be improved, don't follow those.
> +# - Uppercase variables, when referring files, refer to files in this repo.
> +# - Lowercase variables, when referring files, refer to system files.
> +# - Variables starting with '_' refer to absolute paths, including $(DESTDIR).
> +# - Variables ending with '_' refer to a subdir of their parent dir, which
> +#   is in a variable of the same name but without the '_'.  The subdir is
> +#   named after this project: <*/man>.
> +# - Variables ending in '_rm' refer to files that can be removed (exist).
> +# - Variables ending in '_rmdir' refer to dirs that can be removed (exist).
> +# - Targets of the form '%-rm' remove their corresponding file '%'.
> +# - Targets of the form '%/.-rmdir' remove their corresponding dir '%/'.
> +# - Targets of the form '%/.' create their corresponding directory '%/'.
> +# - Every file or directory to be created depends on its parent directory.
> +#   This avoids race conditions caused by `mkdir -p`.  Only the root
> +#   directories are created with parents.
> +# - The 'FORCE' target is used to make phony some variables that can't be
> +#   .PHONY to avoid some optimizations.
> +#
> +########################################################################
> +
>  MAKEFLAGS += --no-print-directory
> +MAKEFLAGS += --silent
> +MAKEFLAGS += --warn-undefined-variables
> +
> +
> +htmlbuilddir := $(CURDIR)/.html
> +HTOPTS :=
> +
> +DESTDIR :=
> +prefix := /usr/local
> +datarootdir := $(prefix)/share
> +docdir := $(datarootdir)/doc
> +MANDIR := $(CURDIR)
> +mandir := $(datarootdir)/man
> +MAN1DIR := $(MANDIR)/man1
> +MAN2DIR := $(MANDIR)/man2
> +MAN3DIR := $(MANDIR)/man3
> +MAN4DIR := $(MANDIR)/man4
> +MAN5DIR := $(MANDIR)/man5
> +MAN6DIR := $(MANDIR)/man6
> +MAN7DIR := $(MANDIR)/man7
> +MAN8DIR := $(MANDIR)/man8
> +man1dir := $(mandir)/man1
> +man2dir := $(mandir)/man2
> +man3dir := $(mandir)/man3
> +man4dir := $(mandir)/man4
> +man5dir := $(mandir)/man5
> +man6dir := $(mandir)/man6
> +man7dir := $(mandir)/man7
> +man8dir := $(mandir)/man8
> +manext := \.[0-9]
> +man1ext := .1
> +man2ext := .2
> +man3ext := .3
> +man4ext := .4
> +man5ext := .5
> +man6ext := .6
> +man7ext := .7
> +man8ext := .8
> +htmldir := $(docdir)
> +htmldir_ := $(htmldir)/man
> +htmlext := .html
>  
> -htmlbuilddir = $(CURDIR)/.html
> -HTOPTS =
> +INSTALL := install
> +INSTALL_DATA := $(INSTALL) -m 644
> +INSTALL_DIR := $(INSTALL) -m 755 -d
> +RM := rm
> +RMDIR := rmdir --ignore-fail-on-non-empty
>  
> -DESTDIR =
> -prefix = /usr/local
> -datarootdir = $(prefix)/share
> -docdir = $(datarootdir)/doc
> -mandir = $(datarootdir)/man
> -htmldir = $(docdir)
> -htmldir_ = $(htmldir)/man
> -htmlext = .html
> +MAN_SECTIONS := 1 2 3 4 5 6 7 8
>  
> -INSTALL = install
> -INSTALL_DATA = $(INSTALL) -m 644
> -INSTALL_DIR = $(INSTALL) -m 755 -d
>  
>  .PHONY: all
>  all:
> -	$(MAKE) uninstall;
> -	$(MAKE) install;
> +	$(MAKE) uninstall
> +	$(MAKE) install
> +
> +
> +%/.:
> +	$(info -	INSTALL	$(@D))
> +	$(INSTALL_DIR) $(@D)
> +
> +%-rm:
> +	$(info -	RM	$*)
> +	$(RM) $*
> +
> +%-rmdir:
> +	$(info -	RMDIR	$(@D))
> +	$(RMDIR) $(@D)
> +
> +
> +.PHONY: install
> +install: install-man | installdirs
> +	@:
> +
> +.PHONY: installdirs
> +installdirs: | installdirs-man
> +	@:
> +
> +.PHONY: uninstall remove
> +uninstall remove: uninstall-man
> +	@:
> +
> +.PHONY: clean
> +clean:
> +	find man?/ -type f \
> +	|while read f; do \
> +		rm -f "$(htmlbuilddir)/$$f".*; \
> +	done;
> +
> +########################################################################
> +# man
> +
> +MANPAGES   := $(sort $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)$$'))
> +_manpages  := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%,$(MANPAGES))
> +_man1pages := $(filter %$(man1ext),$(_manpages))
> +_man2pages := $(filter %$(man2ext),$(_manpages))
> +_man3pages := $(filter %$(man3ext),$(_manpages))
> +_man4pages := $(filter %$(man4ext),$(_manpages))
> +_man5pages := $(filter %$(man5ext),$(_manpages))
> +_man6pages := $(filter %$(man6ext),$(_manpages))
> +_man7pages := $(filter %$(man7ext),$(_manpages))
> +_man8pages := $(filter %$(man8ext),$(_manpages))
> +
> +MANDIRS  := $(sort $(shell find $(MANDIR)/man? -type d))
> +_mandirs := $(patsubst $(MANDIR)/%,$(DESTDIR)$(mandir)/%/.,$(MANDIRS))
> +_man1dir := $(filter %man1/.,$(_mandirs))
> +_man2dir := $(filter %man2/.,$(_mandirs))
> +_man3dir := $(filter %man3/.,$(_mandirs))
> +_man4dir := $(filter %man4/.,$(_mandirs))
> +_man5dir := $(filter %man5/.,$(_mandirs))
> +_man6dir := $(filter %man6/.,$(_mandirs))
> +_man7dir := $(filter %man7/.,$(_mandirs))
> +_man8dir := $(filter %man8/.,$(_mandirs))
> +_mandir  := $(DESTDIR)$(mandir)/.
> +
> +_manpages_rm  := $(addsuffix -rm,$(wildcard $(_manpages)))
> +_man1pages_rm := $(filter %$(man1ext)-rm,$(_manpages_rm))
> +_man2pages_rm := $(filter %$(man2ext)-rm,$(_manpages_rm))
> +_man3pages_rm := $(filter %$(man3ext)-rm,$(_manpages_rm))
> +_man4pages_rm := $(filter %$(man4ext)-rm,$(_manpages_rm))
> +_man5pages_rm := $(filter %$(man5ext)-rm,$(_manpages_rm))
> +_man6pages_rm := $(filter %$(man6ext)-rm,$(_manpages_rm))
> +_man7pages_rm := $(filter %$(man7ext)-rm,$(_manpages_rm))
> +_man8pages_rm := $(filter %$(man8ext)-rm,$(_manpages_rm))
> +
> +_mandirs_rmdir := $(addsuffix -rmdir,$(wildcard $(_mandirs)))
> +_man1dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man1dir)))
> +_man2dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man2dir)))
> +_man3dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man3dir)))
> +_man4dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man4dir)))
> +_man5dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man5dir)))
> +_man6dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man6dir)))
> +_man7dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man7dir)))
> +_man8dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man8dir)))
> +_mandir_rmdir  := $(addsuffix -rmdir,$(wildcard $(_mandir)))
> +
> +install_manX     := $(foreach x,$(MAN_SECTIONS),install-man$(x))
> +installdirs_manX := $(foreach x,$(MAN_SECTIONS),installdirs-man$(x))
> +uninstall_manX   := $(foreach x,$(MAN_SECTIONS),uninstall-man$(x))
> +
> +
> +.SECONDEXPANSION:
> +$(_manpages): $(DESTDIR)$(mandir)/man%: $(MANDIR)/man% | $$(@D)/.
> +	$(info -	INSTALL	$@)
> +	$(INSTALL_DATA) -T $< $@
> +
> +$(_mandirs): %/.: | $$(dir %). $(_mandir)
> +
> +$(_mandirs_rmdir): $(DESTDIR)$(mandir)/man%/.-rmdir: $$(_man%pages_rm) FORCE
> +$(_mandir_rmdir): $(uninstall_manX) FORCE
> +
> +
> +.PHONY: $(install_manX)
> +$(install_manX): install-man%: $$(_man%pages) | installdirs-man%
> +	@:
> +
> +.PHONY: install-man
> +install-man: $(install_manX)
> +	@:
> +
> +.PHONY: $(installdirs_manX)
> +$(installdirs_manX): installdirs-man%: $$(_man%dir) $(_mandir)
> +	@:
> +
> +.PHONY: installdirs-man
> +installdirs-man: $(installdirs_manX)
> +	@:
> +
> +.PHONY: $(uninstall_manX)
> +$(uninstall_manX): uninstall-man%: $$(_man%pages_rm) $$(_man%dir_rmdir)
> +	@:
> +
> +.PHONY: uninstall-man
> +uninstall-man: $(_mandir_rmdir) $(uninstall_manX)
> +	@:
> +
> +
> +########################################################################
> +# html
>  
>  # Use with
>  #  make HTOPTS=whatever html
> @@ -57,28 +241,6 @@ installdirs-html:
>  		$(INSTALL_DIR) "$(DESTDIR)$(htmldir_)/$$d" || exit $$?; \
>  	done;
>  
> -.PHONY: install
> -install: | installdirs
> -	find man?/ -type f \
> -	|while read f; do \
> -		$(INSTALL_DATA) -T "$$f" "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
> -	done;
> -
> -.PHONY: installdirs
> -installdirs:
> -	find man?/ -type d \
> -	|while read d; do \
> -		$(INSTALL_DIR) "$(DESTDIR)$(mandir)/$$d" || exit $$?; \
> -	done;
> -
> -.PHONY: uninstall remove
> -uninstall remove:
> -	find man?/ -type f \
> -	|while read f; do \
> -		rm -f "$(DESTDIR)$(mandir)/$$f" || exit $$?; \
> -		rm -f "$(DESTDIR)$(mandir)/$$f".* || exit $$?; \
> -	done;
> -
>  .PHONY: uninstall-html
>  uninstall-html:
>  	find man?/ -type f \
> @@ -86,12 +248,9 @@ uninstall-html:
>  		rm -f "$(DESTDIR)$(htmldir_)/$$f".* || exit $$?; \
>  	done;
>  
> -.PHONY: clean
> -clean:
> -	find man?/ -type f \
> -	|while read f; do \
> -		rm -f "$(htmlbuilddir)/$$f".* || exit $$?; \
> -	done;
> +
> +########################################################################
> +# tests
>  
>  # Check if groff reports warnings (may be words of sentences not displayed)
>  # from https://lintian.debian.org/tags/groff-message.html
> @@ -109,3 +268,7 @@ check-groff-warnings:
>  
>  # someone might also want to look at /var/catman/cat2 or so ...
>  # a problem is that the location of cat pages varies a lot
> +
> +########################################################################
> +
> +FORCE:
> diff --git a/README b/README
> index 6598170c0..484151773 100644
> --- a/README
> +++ b/README
> @@ -26,9 +26,17 @@ To install to a path different from /usr/local, use
>  distribution from its destination.  Use with caution, and remember to
>  use "prefix" if desired, as with the "install" target.
>  
> +To install only a specific man section (mandir) such as man3, use
> +"make install-man3".  Similar syntax can be used to uninstall a
> +specific man section, such as man7: "make uninstall-man7".
> +
>  "make" or "make all" will perform "make uninstall" followed by "make
>  install".
>  
> +Consider using multiple threads (at least 2) when installing
> +these man pages, as the Makefile is optimized for multiple threads:
> +"make -j install".
> +
>  Copyrights
>  ==========
>  See the 'man-pages-x.y.Announce' file.
> 


-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2021-06-09 21:22 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-04 20:53 [PATCH] Makefile, README: Break installation into a target for each mandir Alejandro Colomar
2021-06-07 16:49 ` [PATCH v19] " Alejandro Colomar
2021-06-07 18:12 ` [PATCH v20] " Alejandro Colomar
2021-06-07 18:18 ` [PATCH v21] " Alejandro Colomar
2021-06-07 21:36 ` [PATCH v22] " Alejandro Colomar
2021-06-07 21:49 ` [PATCH v23] " Alejandro Colomar
2021-06-08  9:34 ` [PATCH v24] " Alejandro Colomar
2021-06-08 16:51 ` [PATCH v25] " Alejandro Colomar
2021-06-08 21:55 ` [PATCH v26] " Alejandro Colomar
2021-06-09 17:01 ` [PATCH v27] " Alejandro Colomar
2021-06-09 21:21   ` Michael Kerrisk (man-pages)

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.