Getting `make install` to source your bash_completion - bash

This is the install part of my Makefile:
install:
for e in $(EXEC); do \
sudo cp --remove-destination ${CURDIR}/$$e /usr/local/bin; done
sudo cp ${CURDIR}/bin/stage2.d /etc/bash_completion.d/stage2
. /etc/bash_completion
Where "stage2" is the name of my executable.
The last line is what provides the issue. After adding a file into bash_completion.d directory I want to source the bash_completion. But calling source or . yields:
. /etc/bash_completion
/etc/bash_completion: 32: [[: not found
/etc/bash_completion: 38: [[: not found
/etc/bash_completion: 50: Bad substitution
make: *** [install] Error 2

I see two issues:
Using sudo in a Makefile rule is a bad idea. Avoid that, and call sudo make install instead.
Why would you want to source the bash_completion file in the non-interactive shell which is the make rule? It makes no sense.
As to solving them:
install:
$(INSTALL) -m 0755 -d $(DESTDIR)$(bindir)
$(INSTALL) -m 0755 -p $(EXEC) $(DESTDIR)$(bindir)/
$(INSTALL) -m 0755 -d $(DESTDIR)$(sysconfdir)/bash_completion.d
$(INSTALL) -m 0644 -p ${CURDIR}/bin/stage2.d $(DESTDIR)$(sysconfdir)/bash_completion.d/stage2
for the make rule part and
sudo make install && . /etc/bash_completion.d/stage2
for the actual running of that command. You will want to document the latter in a README, or in a line which the install: target prints when finished.

Make uses /bin/sh by default. you have to force it to use bash since [[ is not supported by normal sh.
gnu make lets you set SHELL variable [in the makefile] to force it to use bash. So you would need to add a line
SHELL=/bin/bash
at the top of your makefile

Related

Makefile running command with whoami

I am trying to use a makefile to setup my machine. I am trying to setup FZF and have the following code. However, it seems to be replacing that command with empty space instead of the user I am logged as. I have SSH'd into the Pi, so not sure if that is the cause or if it's something else.
linuxfxf:
sudo mkdir -p /usr/local/opt
sudo chown -R $(whoami) /usr/local/opt
$ is a special character for make: it introduces a make variable. If you want to run a recipe and have the shell see the $ you have to escape it:
linuxfxf:
sudo mkdir -p /usr/local/opt
sudo chown -R $$(whoami) /usr/local/opt
Or you could use the old-school syntax `whoami` instead.

Setup a global variable dynamically in a Makefile

I have the following code in a Makefile, which execute one sequence of command or another based on a environmental variable.
generate :
if test -z "$$VIRTUAL_ENV"; then \
$(PYTHON) -m fades -V &>/dev/null || $(PYTHON) -m pip install --user fades; $(PYTHON) -m fades -r requirements.txt script.py;"; \
else \
python -m pip install -r requirements.txt && python script.py; \
fi
It works as expected, but I would like to do the same thing on multiple targets, to use it on other files, without having to copy this snippet of code multiple times.
My idea would be to set a variable dynamically (based on the condition that has been evaluated), containing the one command or the other, to be used over and over again, like alias in Bash.
Is that a good idea? Is it possible to set a global alias in the Makefile so it can choose between two Python interpreters based on an environmental variable?
Assuming you're using GNU make, you can do it like this:
ifdef VIRTUAL_ENV
PYCMD = python -m pip install -r requirements.txt && python
else
PYCMD = $(PYTHON) -m fades -V >/dev/null 2>&1 || $(PYTHON) -m pip install --user fades; $(PYTHON) -m fades -r requirements.txt
endif
generate:
$(PYCMD) script.py
Note I changed &>/dev/null to >/dev/null 2>&1 because the former is a bash-only feature and is not valid in POSIX sh, and make (by default) always runs /bin/sh which is (on many systems) a POSIX sh.
I don't know why you're using python in one section and $(PYTHON) in the other; it seems like you'd want to use the same in both but anyway.

How to run and interact with a script from within an RPM?

I'm building an RPM which needs to run a bash script as root.
The %install stanza of the spec file is:
%install
cp %{SOURCE1} %{SOURCE2} %{_tmppath}/%{name}-%{version}-%{release}
cd %{_tmppath}/%{name}-%{version}-%{release}
chmod u+x %{installscript}
sudo ./%{installscript}
Where %{installscript} is the script that runs as root with sudo.
rpmbuild executes %{installscript} and creates the RPM (without problems).
However, when I install the RPM:
$ sudo rpm -Uvh $rpmpath
Preparing... ########################################### [100%]
1:tty-cap ########################################### [100%]
$
The %{installscript} script is not executed.
I tried to change the spec file by moving the script invocation to a %post stanza:
%install
cp %{SOURCE1} %{SOURCE2} %{_tmppath}/%{name}-%{version}-%{release}
cd %{_tmppath}/%{name}-%{version}-%{release}
%post
chmod u+x %{installscript}
sudo ./%{installscript}
But the %post doesn't seem to do anything.
How can I pack an RPM that will execute a script when installed?
Edit 1:
After reviewing the helpful comments below, here's a spec file with a %post stanza that actually gets executed during the RPM installation.
However, the script %{installscript} does not interact with the user (as it does when run from the shell), but seems to accept all its defaults without user interaction.
What should I change so that the script will interact with the rpm command user?
$ cat ~/RPMBUILD/SPECS/demo.spec
#
# %_topdir and %_tmppath are defined in ~/.rpmmacros
%define name tty-cap
%define version 5.2
%define release 1
%define buildroot %{_tmppath}/%{name}-%{version}-%{release}
%define tarversion tty-5.2.0-00-70270
%define tarfile %{tarversion}.tar
%define installscript tty.install.sh
Name: %{name}
Version: %{version}
Release: %{release}
BuildArch: noarch
Summary: Bla
License: Proprietary
Source1: %{installscript}
Source2: tty-5.2.0-00-70270.tar
Prefix: /opt/Intellinx/TTYCapture
BuildRoot: %{_builddir}/%{name}-root
%description
Demonstration RPM
%prep
%build
%install
cp %{SOURCE1} %{SOURCE2} %{_tmppath}/%{name}-%{version}-%{release}
cd %{_tmppath}/%{name}-%{version}-%{release}
%clean
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT}
%post
echo ">>> Inside post <<<"
chmod u+x %{installscript}
./%{installscript}
%files
%define tmp /
%{tmp}/%{tarfile}
%{tmp}/%{installscript}
$ rpmbuild -v -bb ~/RPMBUILD/SPECS/demo.spec
Executing(%prep): /bin/sh -e /home/ronbarak/RPMBUILD/tmp/rpm-tmp.oEOM10
+ umask 022
+ cd /home/ronbarak/RPMBUILD/BUILD
+ LANG=C
+ export LANG
+ unset DISPLAY
+ exit 0
Executing(%build): /bin/sh -e /home/ronbarak/RPMBUILD/tmp/rpm-tmp.qQFuTA
+ umask 022
+ cd /home/ronbarak/RPMBUILD/BUILD
+ LANG=C
+ export LANG
+ unset DISPLAY
+ exit 0
Executing(%install): /bin/sh -e /home/ronbarak/RPMBUILD/tmp/rpm-tmp.8rTMLa
+ umask 022
+ cd /home/ronbarak/RPMBUILD/BUILD
+ '[' /home/ronbarak/RPMBUILD/tmp/tty-cap-5.2-1 '!=' / ']'
+ rm -rf /home/ronbarak/RPMBUILD/tmp/tty-cap-5.2-1
++ dirname /home/ronbarak/RPMBUILD/tmp/tty-cap-5.2-1
+ mkdir -p /home/ronbarak/RPMBUILD/tmp
+ mkdir /home/ronbarak/RPMBUILD/tmp/tty-cap-5.2-1
+ LANG=C
+ export LANG
+ unset DISPLAY
+ cp /home/ronbarak/RPMBUILD/SOURCES/tty.install.sh /home/ronbarak/RPMBUILD/SOURCES/tty-5.2.0-00-70270.tar /home/ronbarak/RPMBUILD/tmp/tty-cap-5.2-1
+ cd /home/ronbarak/RPMBUILD/tmp/tty-cap-5.2-1
+ /usr/lib/rpm/check-buildroot
+ /usr/lib/rpm/redhat/brp-compress
+ /usr/lib/rpm/redhat/brp-strip /usr/bin/strip
+ /usr/lib/rpm/redhat/brp-strip-static-archive /usr/bin/strip
+ /usr/lib/rpm/redhat/brp-strip-comment-note /usr/bin/strip /usr/bin/objdump
+ /usr/lib/rpm/brp-python-bytecompile
+ /usr/lib/rpm/redhat/brp-python-hardlink
+ /usr/lib/rpm/redhat/brp-java-repack-jars
Processing files: tty-cap-5.2-1.noarch
Requires(interp): /bin/sh
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires(post): /bin/sh
Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/ronbarak/RPMBUILD/tmp/tty-cap-5.2-1
Wrote: /home/ronbarak/RPMBUILD/RPMS/noarch/tty-cap-5.2-1.noarch.rpm
Executing(%clean): /bin/sh -e /home/ronbarak/RPMBUILD/tmp/rpm-tmp.Yag9bm
+ umask 022
+ cd /home/ronbarak/RPMBUILD/BUILD
+ '[' /home/ronbarak/RPMBUILD/tmp/tty-cap-5.2-1 '!=' / ']'
+ rm -rf /home/ronbarak/RPMBUILD/tmp/tty-cap-5.2-1
+ exit 0
$ sudo rpm -Uvh /home/ronbarak/RPMBUILD/RPMS/noarch/tty-cap-5.2-1.noarch.rpm
Preparing... ########################################### [100%]
1:tty-cap ########################################### [100%]
>>> Inside post <<<
###### Starting tty capturing installation ######
Specify installation directory full path [/opt/Intellinx/TTYCapture]:
/opt/Intellinx/TTYCapture already exists. continue installation? (y/n) [y]
Extracting archive tty-5*.* ...
Please specify TTY sensor owner user [ronbarak]:
TTY sensor will be accessible by user 'ronbarak' in group 'ronbarak'
Specify sensor working directory full path [/opt/Intellinx/TTYCapture/work]:
Specify sensor listening port [1024-65000] [8888]:
Do you want to register the sensor service? (y/n) [n]
Server gxttySensorService is not register as a daemon
Executing default user shell (y/n) [y]
TTY capturing has been installed successfully.
You are mixing several concepts here. So let make step back.
In %prep section you should unpack your %{SOURCE0} and apply patches if any. This usually do
%setup -q
However if you want you can extract it manually. For more info about this macro see http://www.rpm.org/max-rpm/s1-rpm-inside-macros.html
In %build section you usually compile source into binaries. Likely empty if you use interpreted language or your tar contains already compiled binaries.
In %install section you should copy the files into %{buildroot} and create there structure which will land in package. E.g. %{buildroot}/etc/yourconfig, %{buildroot}/usr/bin/yourcommand etc. You can run there any script you want, but keep in mind that it is run only in build time. I.e. only on your machine (or build system). This is intended for creating files which are automatically generated (e.g. documentation of libraries from source code).
Then you have section %post which is run on user machine after the package was installed. And all files are installed in final path. Not in buildroot. At the beginning you are changed to / so you need to specify full path on that user machine.
So in your case it should be probably look like:
%install
mkdir -p %{buildroot}%{_bindir}
cp -a %{installscript} %{buildroot}%{_bindir}/
chmod a+x %{buildroot}%{_bindir}/%{installscript}
%files
%{_bindir}/%{installscript}
%post
%{_bindir}%{installscript}
Sever notes:
%post section is executed under root, so sudo is not needed.
Running interactive script is strongly discouraged. RPM was designed as non-interactive and every utility around assume no interaction during package installation (e.g. PackageKit, Spacewalk etc.). So sooner then later you will get some compains. It is much safer to say user to run some command after installation manually (or automate it using Ansible or Puppet).

How to create symlinks in a specific directory

I'm working on an automated installation of a openSUSE system using AutoYAST, and I'm stumped on a small detail. In order to setup relevant applications in the user's environment, I try to symlink to all applications located in /usr/local/bin in ~/bin (so say /usr/local/bin has the addr2line utility, then I want to have a symlink to that in ~/bin).
I've tried to execute the following snipped to accomplish this:
su -c "for program in `ls /usr/local/bin`; do ln -s /usr/local/bin/$program ~/bin/$program; done" <user>
This snippet executes in the post-script phase of the automatic installation, which is executed as root (and seeing as I want the owner of the symlinks to be the user, this command is executed using su).
However, this does not work, and gives the following output:
++ ls /usr/local/bin
+ su -c 'for program in addr2line
ar
as
c++
c++filt
cpp
elfedit
g++
gcc
gcc-ar
gcc-nm
gcc-ranlib
gcov
gprof
i686-pc-linux-gnu-c++
i686-pc-linux-gnu-g++
i686-pc-linux-gnu-gcc
i686-pc-linux-gnu-gcc-4.9.3
i686-pc-linux-gnu-gcc-ar
i686-pc-linux-gnu-gcc-nm
i686-pc-linux-gnu-gcc-ranlib
ld
ld.bfd
nm
objcopy
objdump
ranlib
readelf
size
strings
strip; do ln -s /usr/local/bin/ ~/bin/; done' <user>
bash: -c: line 1: syntax error near unexpected token `ar'
bash: -c: line 1: `ar'
I've tried several variations of the command, but all seem to not exactly do what I want.
For example, I've also tried:
su -c "for program in /usr/local/bin/*; do ln -s $program ~/bin/; done" <user>
But this only created a symlink to /usr/local/bin in ~/bin.
So I'm a bit stuck on this one... Does anybody have an idea?
You're using double quotes to define your su command, so $program is being evaluated immediately. You want it evaluated when su executes the command. Use single quotes instead:
su -c 'for program in `ls /usr/local/bin`; do ln -s /usr/local/bin/$program ~/bin/$program; done' <user>
You can also use cp -s to create symlinks on a system with GNU cp (like your suse system), which gives you the ability to use recursion and the other fun options of cp.
In the end, I decided to go with the command posted by pacholik to fix this, as my original attempt was over-engineered and thus not necessary.
ln -s /usr/local/bin/* ~/bin

How to install directory structure recursively in OpenEmbedded BitBake recipe?

I'd like to simplify a BitBake recipe that installs a large directory structure by using some sort of recursive install routine rather than calling install many times. The source directory layout is frequently changing during development, which is causing far more recipe revisions than I want to deal with.
As an example, how would the following do_install() be simplified from this:
do_install() {
install -d ${D}/foo
install -m 0644 ${S}/foo/*.* ${D}/foo
install -d ${D}/foo/a
install -m 0644 ${S}/foo/a/*.* ${D}/foo/a
install -d ${D}/foo/b
install -m 0644 ${S}/foo/b/*.* ${D}/foo/b
install -d ${D}/foo/c
install -m 0644 ${S}/foo/c/*.* ${D}/foo/c
install -d ${D}/bar
install -m 0644 ${S}/bar/*.* ${D}/bar
install -d ${D}/bar/a
install -m 0644 ${S}/bar/a/*.* ${D}/bar/a
install -d ${D}/bar/a/bananas
install -m 0644 ${S}/bar/a/bananas/*.* ${D}/bar/a/bananas
}
To something more like this pseudocode:
do_install() {
for each subdir in ${S}/foo/
install subdir recursively to ${D}/foo/subdir
end
for each subdir in ${S}/bar/
install subdir recursively to ${D}/bar/subdir
end
}
The top-level directories in our source files (foo & bar in the above example) rarely change, so calling them out in the recipe is fine. It's the lower level directories that are frequently changing.
It may be that cp -r ends up being the way to go, but I believe I've read that it's frowned upon in BitBake recipes, so I'm wondering if BitBake provides some alternate mechanism, or if there's
some other reasonably standardized way to do this.
We used to do it in that way:
do_install() {
find ${WORKDIR}/ -type f -exec 'install -m 0755 "{}" ${D}/var/www/' \;
}
The canonical form in OE is
cp -R --no-dereference --preserve=mode,links -v SOURCE DESTINATION
see the answer here (while they looks a bit different in code, the questions are semantically equivalent)

Resources