sudo make install
I originally wrote this in April 2022, but only published it now because... reasons.
I found a weird bug. It occurred while installing neatroff, which is a nice, new, tidy implementation of troff. It can be used from the source dir itself, or can be installed to a $(BASE)
like /usr/local/share/neatroff.
But when I was trying to compile some documents, the neatpdf PDF postprocessor couldn't find the requested fonts and it looked all wrong with weird or no fonts. Everything was installed in $(BASE)
correctly. I could compile the same document in the git source directory and get the right results, so it had to be something to do with the way in which contents of files are changed for being used from $(BASE)
.
The font definitions contain a line fontpath /path/to/font
so that PDFs know where to find the fonts. This is written to using a sed call. Neatroff was meant to only be used from the source dir, so font files usually say fontpath $(PWD)/fonts
, where $(PWD)
hopefully refers to the toplevel source directory (ie. you're running make install
not make -f ../Makefile install
).
To permit installing neatroff to the system, the sed call changes fontpath $(PWD)/fonts
to fontpath $(BASE)/fonts
:
@for f in "$(BASE)/devutf"/*; do \
sed "/^fontpath /s=$(PWD)/fonts=$(BASE)/fonts=" <$$f >.fd.tmp \
mv .fd.tmp $$f \
done
All fine, you might think.
Consider where the makefile actually gets $(PWD)
from. It isn't a defined macro at the top (PWD = ...
), so it inherits it from the environment, which is a shell, where we can rely on $PWD
being set. All fine.
This is how GNU make does it - it gets $(PWD)
from its parent process - usually, your shell. BSD make sets $(PWD)
explicitly.
However, when you run make
under sudo (or doas, or ssu), these privilege-escalting tools do not set $PWD
in their default configuration. Therefore, GNU make can't find a value for $(PWD)
, so it is empty.
The line the sed calls act on start by looking something like
fontpath /home/me/src/neatroff_make/fonts/NimbusRoman-Regular.t1
and should be turned into something like
fontpath /usr/local/share/neatroff/fonts/NimbusRoman-Regular.t1
but under sudo with gmake, with $(PWD)
unset, it actually becomes
fontpath /fonts/NimbusRoman-Regular.t1
which clearly doesn't work.
There are ways to make sudo et al. keep $PWD
defined (by using the -E
flag, or a config line) but that's not something that should be relied on when doing sudo make install
.
Luckily, POSIX requires that shells set PWD themselves, so we can always find the name of the current working directory by doing $$(pwd)
(which runs the pwd
program), or $$PWD
(which is given to a shell as $PWD
, which is dereferenced to what we want). Interestingly, both BSD make and GNU make set special variables with the value of PWD: BSD make sets $(.CURDIR)
, and GNU make sets $(CURDIR)
. See the difference? It's really annoying that the two biggest make implementations have this slight inconsistency, and POSIX doesn't require any special make macro with the value of PWD
. (OpenGroup members, something for POSIX.1-202x?)
I chose to go with $$PWD
:
PWD = $${PWD}
Now, anywhere in the Makefile that uses $(PWD)
gets sent to the shell as ${PWD}
, which is fine.
There's one last problem: the change from using $(PWD)
, which is dereferenced by make, to $$PWD
, which is dereferenced by the shell running each rule line, caused another problem with lines like
@cd neatmkfn && ./gen.sh "$(PWD)/fonts" "$(PWD)/devutf" >/dev/null
which cd
somewhere before doing something. They get $PWD
of the shell in the new directory, but we want the old directory (the $PWD
of the Makefile). I thought the most simple way to fix this was with a shell variable, like
@pwd="$(PWD)" && \
cd neatmkfn && ./gen.sh "$$pwd/fonts" "$$pwd/devutf" >/dev/null
This is a little bit annoying but it is done to preserve the portability of the Makefile (it is POSIX.1-2017 compliant). Both GNU and BSD make have syntaxes for immediate-expansion macro definitions. These would fix this problem because it would save the string of the current working directory into $(PWD)
, rather than a kind of nested macro which gets dereferenced later. (Note that the syntax is mostly incompatible between GNU and BSD make).
For example, we could do
PWD != pwd
which runs pwd then and there, and saves it. However, this is not in POSIX (currently).
In my opinion, the best solution that POSIX.1-202x should use is to require make to set the PWD
macro itself. The whole SCCS stuff in POSIX already requires make to think about PWD
, so I don't see why this shouldn't happen. PWD
is a better macro than CURDIR
or .CURDIR
because of the discrepancies between GNU and BSD make.
written 2022-12-28, originally 2022-04-17
from Hacker News https://ift.tt/0jXq7Mv
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.