Makefile: Check write permission to directory - linux

Is there a way to, in a Makefile rule, check if it has permission to write to a specified directory? This is a small part of the Makefile I'm writing:
APP_NAME = electro
CC = gcc
C_FLAGS = -Wall
LIB_DIR = lib
SRC_DIR = src
INST_DIR = /usr/bin
INST_LIBDIR = /usr/lib
LIBS = libcomponent.so libpower.so libresistance.so
L_FLAGS = -lresistance -lpower -lcomponent
.PHONY: lib
lib:
#$(MAKE) --directory=$(LIB_DIR)
install: $(SRC_DIR)/main.c lib
(cd $(LIB_DIR); cp $(LIBS) $(INST_LIBDIR))
$(CC) $(C_FLAGS) -o $(INST_DIR)/$(APP_NAME) $(L_FLAGS) $<
#echo "$(APP_NAME) installed successfully."
The 'install' rule will obviously fail if the user doesn't have permission to write to /usr/lib and /usr/bin. Is there a way to check this permission before executing these two lines and in that case output an appropriate error message (perhaps something along the lines of "You do not have sufficient permissions to run this command")?

Shell's /bin/test or /bin/[ will let you /bin/test -d /path/to/foo -a -w /path/to/foo, even if make doesn't by itself.
An alternate way to make a directory accessible is to try to create it with mkdir -p before testing whether it's writable. I've been known to do the following:
mkdir -p ${TARGETDIR} && test -w ${TARGETDIR}
If the first command fails, the second isn't tried. If the directory already exists, mkdir -p will succeed.

You do things like that more or less by embedding small bits of shell scripts into your makefile.

Related

Set env variable for HSL so IPOPT can use the solver `ma57`

I am sorry but I am not good at setting environment variables / manual builds. I have downloaded the HSL solvers for IPOPT and I believe that I successfully install the whole package but I cannot set the environment variables so I can use it from IPOPT. I am getting this error message:
Exception message: Selected linear solver MA57 not available.
Tried to obtain MA57 from shared library "libhsl.so", but the following error occured:
libhsl.so: cannot open shared object file: No such file or directory
I went according this advice https://stackoverflow.com/a/67655434/12312879 but I am not sure what to do next. The last command sudo make install printed this:
libtool: install: /usr/bin/install -c .libs/libcoinhsl.so.2.2.1 /usr/local/lib/libcoinhsl.so.2.2.1
libtool: install: (cd /usr/local/lib && { ln -s -f libcoinhsl.so.2.2.1 libcoinhsl.so.2 || { rm -f libcoinhsl.so.2 && ln -s libcoinhsl.so.2.2.1 libcoinhsl.so.2; }; })
libtool: install: (cd /usr/local/lib && { ln -s -f libcoinhsl.so.2.2.1 libcoinhsl.so || { rm -f libcoinhsl.so && ln -s libcoinhsl.so.2.2.1 libcoinhsl.so; }; })
libtool: install: /usr/bin/install -c .libs/libcoinhsl.lai /usr/local/lib/libcoinhsl.la
libtool: finish: PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/sbin" ldconfig -n /usr/local/lib
----------------------------------------------------------------------
Libraries have been installed in:
/usr/local/lib
If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the '-LLIBDIR'
flag during linking and do at least one of the following:
- add LIBDIR to the 'LD_LIBRARY_PATH' environment variable
during execution
- add LIBDIR to the 'LD_RUN_PATH' environment variable
during linking
- use the '-Wl,-rpath -Wl,LIBDIR' linker flag
- have your system administrator add LIBDIR to '/etc/ld.so.conf'
If you have a current (3.14) Ipopt, then try setting Ipopt option hsllib to /usr/local/lib/libcoinhsl.so.
If Ipopt is older, then it doesn't have the hsllib option. You can try copying your libcoinhsl.so.2.2.1 to libhsl.so and ensure that Ipopt finds it (e.g., by having its path in LD_LIBRARY_PATH).
I you build Ipopt from source, then the preferred way would be to provide HSL at buildtime, see also https://coin-or.github.io/Ipopt/INSTALL.html#DOWNLOAD_HSL.

Is it possible to change glibc's default rpath? [duplicate]

I'm trying to build and install my own gcc 4.7.2 in /usr/local to use in place of the gcc 4.4.6 in /usr. (This is on CentOS 6.3.)
gcc makes executables and dynamic libraries that dynamically link to its own dynamic libraries, e.g. libstdc++.so. How do I build and install gcc so that the generated binaries automatically get a linker -rpath option (-rpath /usr/local/lib64) that causes dynamic libraries in /usr/local/lib64 to be linked instead of those in /usr/lib64 or /lib64?
If it works properly, after I build an executable with the gcc without specifying "-Wl,-rpath=/usr/local/lib64", when I ldd the executable, it should show /usr/local/lib64/libstdc++.so.6 instead of /usr/lib64/libstdc++.so.6. Similarly for the libgcc_s.so.1.
I have tried different approaches, including specifying LDFLAGS_FOR_TARGET=-Wl,-rpath=/usr/local/lib64,-rpath=/usr/local/lib on the 'configure' command-line, but nothing worked.
If you don't want to export paths there's an alternative solution:
with your toolchain in the PATH:
gcc -dumpspecs > specsfile
edit specsfile and in the section link add your -rpath example:
*link:
%{!static:--eh-frame-hdr} -m %(link_emulation) %{shared:-shared} %{!shared: %{!static: %{rdynamic:-export-dynamic} -dynamic-linker %(dynamic_linker)} %{static:-static}} -rpath /usr/local/lib64
at this point you can test if it work with:
g++ -specs=specsfile test.cpp
readelf -d a.out |grep RPATH
if it work you can make it permanent (no need to pass -specs everytime)
strace -fF -o /tmp/g++.log g++ test.cpp
grep specs /tmp/g++.log
the grep should show the paths where gcc look for the default specs file.
The specs files are flexible enough to allow conditional linking depending on variables, example:
{!static: %{mabi=n32:-rpath-link %R/lib32:%R/usr/lib32} %{mabi=64:-rpath-link %R/lib64:%R/usr/lib64} %{mabi=32:-rpath-link %R/lib:%R/usr/lib}}
should use different and multiple paths depending on mabi (untested, yet), %R should be the sysroot path, can be changed with needed full path.
There's also a --with-specs= option gcc configure eventually to be used at build time, not clear to me yet how to use with the link section (working on it).
--with-specs="%{shared:-Wl,-rpath -Wl,$(DESTDIR)/lib}%{!shared:-Wl,-rpath -Wl,$(DESTDIR)/lib}"
It work, I used both shared and not !shared just for test, probably some smarter condition should be used, note that it isn't reported with -dumpspecs.
Reading through some thread of the gcc mailing list I had the impression specs aren't liked by everyone (but if I'm not wrong 4.9 add another option --with-extra-specs) instead preferred way to do such customizations appears to be configure.host, but I'm done and not looking into it, have fun! :-)
see also: gcc faq rpath
update above
I don't know if you can set a pre-defined rpath, probably if you can would be in the linker ld of binutils not in gcc/g++, but why would you do that?
Just export LD_LIBRARY_PATH at runtime and LD_RUN_PATH at build time
export LD_LIBRARY_PATH=/usr/local/lib64:$LD_LIBRARY_PATH
ldd a.out
ldd should show the paths you exported.
To quote a message given when a shared library is built with libtool:
If you ever happen to want to link against installed libraries in a given directory, LIBDIR, you must either use libtool, and specify the full pathname of the library, or use the `-LLIBDIR' flag during linking and do at least one of the following:
add LIBDIR to the `LD_LIBRARY_PATH' environment variable during execution
add LIBDIR to the `LD_RUN_PATH' environment variable during linking
use the `-Wl,--rpath -Wl,LIBDIR' linker flag
have your system administrator add LIBDIR to `/etc/ld.so.conf'
See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
for completeness
the Makefile I used for testing the thing, all the configure options, environment variables (see boot ldflags) I tried didn't work, --enable-rpath included.
use with mkdir ~/gcc copy the Makefile below into ~/gcc then cd ~/gcc && make build-gcc
notice the options used are only for this test case, don't use as reference.
FETCH = aria2c --file-allocation=none -c -d dl
NICE = nice -n 19
PARALLEL = -j4
DESTDIR = $(HOME)/gcc/install
SRCDIR = $(HOME)/gcc/src
all:
# if more downloads are added just remove {dl,src}/*-my-stamp not the .bak
# the .bak should avoid to rebuild targets because of timestamp
touch_stamp = if [ -f $#.bak ]; then \
touch -r $#.bak $#; \
else \
touch $# $#.bak; \
fi
dl/dl-my-stamp:
$(FETCH) https://ftp.gnu.org/gnu/gcc/gcc-4.7.2/gcc-4.7.2.tar.bz2
$(FETCH) http://ftp.gnu.org/gnu/gmp/gmp-4.3.2.tar.bz2
$(FETCH) ftp://gcc.gnu.org/pub/gcc/infrastructure/mpc-0.8.1.tar.gz
$(FETCH) https://ftp.gnu.org/gnu/mpfr/mpfr-2.4.2.tar.bz2
$(FETCH) --check-certificate=false http://www.mirrorservice.org/sites/sourceware.org/pub/binutils/snapshots/binutils-2.24.51.tar.bz2 \
ftp://sourceware.org/pub/binutils/snapshots/binutils-2.24.51.tar.bz2
$(touch_stamp)
untar_dep = src/untar-my-stamp
src/untar-my-stamp: dl/dl-my-stamp
mkdir -p src
tar -C src -xjf dl/gcc-4.7.2.tar.bz2
tar -C src -xjf dl/gmp-4.3.2.tar.bz2
tar -C src -xzf dl/mpc-0.8.1.tar.gz
tar -C src -xjf dl/mpfr-2.4.2.tar.bz2
tar -C src -xjf dl/binutils-2.24.51.tar.bz2
$(touch_stamp)
define configure-rule
$(1)_install = $(DESTDIR)/$(1)-install-my-stamp
$(1)_builddir = $$($(1)_dir)/build
$(DESTDIR)/$(1)-install-my-stamp: $$($(1)_deps)
mkdir -p $$($(1)_builddir)
cd $$($(1)_builddir) && \
$$($(1)_env) ../configure --cache-file=$(SRCDIR)/$(1)-config.cache \
$$($(1)_configure)
$(NICE) make -C $$($(1)_builddir) $$($(1)_make_target) $(PARALLEL)
ifneq ($$($(1)_post_make),)
$$($(1)_post_make)
endif
touch $$#
.PHONY: build-$(1) clean-$(1)
build-$(1): $$($(1)_install)
clean-$(1):
-rm -fr $$($(1)_builddir)
endef
gmp_dir = src/gmp-4.3.2
gmp_env = CONFIG_SITE=$(SRCDIR)/config.site
gmp_configure = --prefix=$(DESTDIR) \
--disable-shared --enable-static --enable-cxx
gmp_deps = $(untar_dep)
gmp_make_target = install
$(eval $(call configure-rule,gmp))
mpfr_dir = src/mpfr-2.4.2
mpfr_env = CONFIG_SITE=$(SRCDIR)/config.site
mpfr_configure = --prefix=$(DESTDIR) \
--disable-shared --enable-static \
--with-gmp=$(DESTDIR)
mpfr_deps = $(untar_dep) $(gmp_install)
mpfr_make_target = install
$(eval $(call configure-rule,mpfr))
mpc_dir = src/mpc-0.8.1
mpc_env = CONFIG_SITE=$(SRCDIR)/config.site
mpc_configure = --prefix=$(DESTDIR) \
--disable-shared --enable-static \
--with-gmp=$(DESTDIR) --with-mpfr=$(DESTDIR)
mpc_deps = $(untar_dep) $(gmp_install) $(mpfr_install)
mpc_make_target = install
$(eval $(call configure-rule,mpc))
gcc_dir = src/gcc-4.7.2
gcc_env = CONFIG_SITE=$(SRCDIR)/config.site \
CFLAGS="-I/usr/include/i386-linux-gnu" \
CXXFLAGS="-I/usr/include/i386-linux-gnu"
gcc_configure = --prefix=$(DESTDIR) \
--libdir=$(DESTDIR)/lib \
--with-local-prefix=$(DESTDIR) \
--with-gmp=$(DESTDIR) --with-mpfr=$(DESTDIR) \
--with-mpc=$(DESTDIR) \
--disable-bootstrap \
--enable-languages=c,c++ \
--disable-libgomp --disable-multilib \
--disable-libmudflap --disable-libssp \
--disable-libquadmath \
--enable-rpath \
MAKEINFO=missing
gcc_deps = $(untar_dep) $(gmp_install) $(mpfr_install) $(mpc_install)
gcc_make_target =
gcc_post_make = make -C $(gcc_builddir) install
$(eval $(call configure-rule,gcc))
binutils_dir = src/binutils-2.24.51
#binutils_env = LDFLAGS=-Wl,-rpath\ $(DESTDIR)/lib
binutils_env = CONFIG_SITE=$(SRCDIR)/config.site \
CFLAGS="-I/usr/include/i386-linux-gnu" \
BOOT_LDFLAGS="-rpath-link=$(DESTDIR)/lib -rpath=$(DESTDIR)/lib"
binutils_configure = --prefix=$(DESTDIR) \
--libdir=$(DESTDIR)/lib \
--with-gmp=$(DESTDIR) \
--enable-rpath
binutils_deps = $(untar_dep) $(gmp_install)
#binutils_make_target = install
binutils_post_make = make -C $(binutils_builddir) install
$(eval $(call configure-rule,binutils))
.PHONY: env
env:
#echo export PATH=$(DESTDIR)/bin:\$$PATH
#echo export LIBRARY_PATH=/usr/lib/i386-linux-gnu
I was installing httpd-2.4.51 and needed to specify -rpath to compile the program. I use the linker flag -Wl,-rpath -Wl,LIBDIR.
Use commands:
sudo mv httpd.c /usr/lib #create a .c file and move it to the default directory for the library file
sudo gcc -o httpd httpd.c -L$--prefix=/usr/lib -lhttpd -Wl,-rpath=$--prefix=/usr/lib
Your can check where you have gcc installed:
whereis gcc
gcc: /usr/bin/gcc /usr/lib/gcc /usr/share/man/man1/gcc.1.gz

How can I give permissions in a basic makefile

How do I add permissions to this simple make file?
all: shell ls cat groups
shell: shell.o cd.o
gcc shell.o cd.o -o shell
cat: cat.o
gcc -c cat.c -o cat
ls: ls.o header.h
gcc -c ls.c -o ls
groups: groups.o groups.h
gcc -c groups.c -o groups
shell.o: shell.c tlpi_hdr.h
gcc -c shell.c
cd.o: cd.c tlpi_hdr.h
gcc -c cd.c
cat.o: cat.c tlpi_hdr.h
gcc -c cat.c
ls.o: ls.c header.h
gcc -c ls.c
groups.o: groups.c groups.h
gcc -c groups.c
clean:
rm *.o shell cat ls groups
I need to run cat/ls/groups/cd within the shell that I created, but can't because it doesn't have permissions. How can I do this?
As an example, this rule:
groups: groups.o groups.h
gcc -c groups.c -o groups
tells GCC to produce an object file called groups (instead of the normal groups.o) because you included the -c option. Object files are not executables, so the compiler doesn't make them executable, hence your problem with the permissions. Note that the rule requires make to build groups.o, but then proceeds to ignore the object file because it recompiles the source code.
You wanted to write something more like:
groups: groups.o
gcc groups.o -o groups
groups.o: groups.h
You need to use a lot more macros — or you could leave the rules out of the makefile since make knows how to build groups from groups.c.
You should also be compiling with warning options. At minimum -Wall; preferably -Wall -Wextra -Werror (and I use still more options than these). You might want optimization (-O3); you might want debugging (-g). These are normally handled via setting macros and then using them in the commands.

Dependencies not being made in makefile

I've been trying to get the following makefile to work.
INCLUDE=Include/
LIBRARY=Lib/
CC=g++
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
LIBRARIES=-lGLEW -framework OpenGL -framework GLUT
else
LIBRARIES=-lGL -lglut -lGLEW
endif
SRC := $(shell find -name *.cpp | tr '\n' ' ')
all: release debug
.PHONY: init
init:
#mkdir -p build/release/object
#mkdir -p build/debug/object
debug: init
debug: CC = g++ -g
debug: BUILD_DIR = build/debug
debug: makegeneral
release: init
release: CC = g++
release: BUILD_DIR = build/release
release: makegeneral
makegeneral: OBJ = $(SRC:./src/%.cpp=$(BUILD_DIR)/object/%.o)
makegeneral: $(OBJ)
$(CC) -I$(INCLUDE) -L$(LIBRARY) $(OBJ) $(LIBRARIES) -o VoxPop
#rm -rf $(BUILD_DIR)/shaders
#mkdir -p $(BUILD_DIR)/shaders
#cp -r src/shaders/* $(BUILD_DIR)/shaders
$(BUILD_DIR)/object/%.o: src/%.cpp
$(CC) -I$(INCLUDE) -L$(LIBRARY) -o $# -c $<
.PHONY: clean
clean:
#rm build/debug/object/* build/release/object/*
Essentially, it sets a few variables specific to a debug build and a release build and then calls a common target, makegeneral. When I run make, I get the following output:
g++ -IInclude/ -LLib/ build/release/object/VoxPop.o build/release/object/Utils.o -lGL -lglut -lGLEW -o VoxPop
g++: error: build/release/object/VoxPop.o: No such file or directory
g++: error: build/release/object/Utils.o: No such file or directory
make: *** [makegeneral] Error 1
When I echo out SRC and OBJ at the beginning of makegeneral, they appear to be correct. It seems that the problem is with the dependencies for makegeneral, since the rule for compiling object files is never invoked and no there is no "No rule to make target..." message spit out.
For reference, this is what I get when I echo out SRC and OBJ at the beginning of makegeneral.
SRC: ./src/VoxPop.cpp ./src/Utils.cpp
OBJ: build/release/object/VoxPop.o build/release/object/Utils.o
BUILD_DIR is not set at the top level so expands to the empty string in the pattern rule. This is also the reason (I believe) for why make isn't failing on unbuildable prereqs. There aren't even any rules for how to do so (beyond the built-in rules). (Though I don't have access to a machine with make at the moment to test my theories.)
I'm also don't believe (though I can't test at the moment) that make will run your makegeneral target twice in this configuration to get you what you want. I believe you will only get it run once with whichever target make chooses to build first (the first listed I believe so in this case release).

How to add my own software to a Buildroot Linux package?

I am trying to add my own program to a small linux, created with Buildroot.
What I've done so far:
I've created a new directory inside my 'buildroot/package/' called 'HelloWorld'.
Inside 'buildroot/package/HelloWorld' I have : a Config.in, HelloWorld.mk and HelloWorld directory.
Config.in holds this:
config BR2_PACKAGE_HELLOWORLD
bool "helloworld"
default y
help
Hello world component.
HelloWorld.mk holds this:
HELLOWORLD_VERSION:= 1.0.0
HELLOWORLD_SITE:= /home/userpc/Downloads/helloworld/
HELLOWORLD_SITE_METHOD:=local
HELLOWORLD_INSTALL_TARGET:=YES
define HELLOWORLD_BUILD_CMDS
$(MAKE) CC="$(TARGET_CC)" LD="$(TARGET_LD)" -C $(#D) all
endef
define HELLOWORLD_INSTALL_TARGET_CMDS
$(INSTALL) -D -m 0755 $(#D)/helloworld $(TARGET_DIR)/bin
endef
define HELLOWORLD_PERMISSIONS
/bin/helloworld f 4755 0 0 - - - - -
endef
$(eval $(generic-package))
(inspiration source)
The HelloWorld directory contains: main.c & Makefile:
main.c :
#include <stdio.h>
int main()
{
printf("\nMain entry.\n");
return 0;
}
Makefile:
CC=gcc
CFLAGS=-I.
all: *.c
$(CC) -Os -Wall *.c -o helloworld
# $(STRIP) helloworld
clean:
rm -f a.out helloworld
rm -f *.o
Edit: And I have also added source "package/HelloWorld/Config.in" to 'package/Config.in'
But when I mount my rootfs.ext2 partition I can't find my HelloWorld executable inside /usr/bin .., I am really new to this and don't have any prior knowledge, so could you please explain to me, what am I missing from this, because I'm sure I'm doing something wrong.
UPDATE: The program builds and install at the desired location but when I try to run it like so: ./helloworld, I get: bash: ./helloworld: No such file or directory It has execution rights. What is the matter with it? (I try to run it after I mount the rootfs.ext2 into a ubuntu directory, the target arch for buildroot is i386, so it should be ok, right?)
After building and installing the HelloWorld program, and eventually running it, I'd like to add to init.d so it starts after booting, and replace the HelloWorld with a Qt Window that doesn't need a X server, like this thing here.
The main source of inspiration here.
Minimal tested example on top of 2016.05
GitHub upstream:
https://github.com/cirosantilli/buildroot/tree/in-tree-package-2016.05
This example adds the package source in-tree, which is simple for educational purposes and the way to go if you want to merge back (kudos!),
If you do not intend on merging back (booooh!), it is more likely that you will want to use Buildroot as a git submodule and either:
an out of tree package with BR2_EXTERNAL as shown at: https://github.com/cirosantilli/buildroot/tree/out-of-tree-2016.05
*_OVERRIDE_SRCDIR + other git submodules as explained at: How to modify the source of Buildroot packages for package development?
Files modified:
package/Config.in
menu "Misc"
source "package/hello/Config.in"
endmenu
package/hello/Config.in
config BR2_PACKAGE_HELLO
bool "hello"
help
Hello world package.
http://example.com
package/hello/hello.mk
################################################################################
#
# hello
#
################################################################################
HELLO_VERSION = 1.0
HELLO_SITE = ./package/hello/src
HELLO_SITE_METHOD = local
define HELLO_BUILD_CMDS
$(MAKE) CC="$(TARGET_CC)" LD="$(TARGET_LD)" -C $(#D)
endef
define HELLO_INSTALL_TARGET_CMDS
$(INSTALL) -D -m 0755 $(#D)/hello $(TARGET_DIR)/usr/bin
endef
$(eval $(generic-package))
package/hello/src/.gitignore
hello
package/hello/src/Makefile
CC = gcc
.PHONY: clean
hello: hello.c
$(CC) -o '$#' '$<'
clean:
rm hello
package/hello/src/hello.c
#include <stdio.h>
int main(void) {
puts("hello");
}
Usage:
make qemu_x86_64_defconfig
echo 'BR2_PACKAGE_HELLO=y' >> .config
make BR2_JLEVEL=2
qemu-system-x86_64 -M pc -kernel output/images/bzImage -drive file=output/images/rootfs.ext2,if=virtio,format=raw -append root=/dev/vda -net nic,model=virtio -net user
From inside qemu:
hello
Expected output:
hello
Tested in Ubuntu 16.04.
In general, the sources for buildroot packages are taken from a (downloaded) tarball. What you are doing right now (placing the sources inside package/HelloWorld) is not the right way to proceed.
Buildroot does have provisions for 'local' package sources, which you could use if you really need to. You'll need the HELLOWORLD_SITE_METHOD variable for that.
Please refer to http://buildroot.uclibc.org/downloads/manual/manual.html#adding-packages for more information.
Also, you don't need to define HELLOWORLD_DIR, HELLOWORLD_BINARY, HELLOWORLD_TARGET_BINARY.
Update: regarding your additional question:
UPDATE: The program builds and install at the desired location but when I try to run it
like so: ./helloworld, I get: bash: ./helloworld: No such file or directory, it has
execution rights.. what is the matter with it? (I try to run it after I mount the
rootfs.ext2 into a ubuntu directory, the target arch for buildroot is i368, so it should
be ok, right?)
No, it does not work like that. You can't just mount rootfs.ext2 and expect to run programs from it. This is, among others, because the programs inside rootfs.ext2 are compiled against the libraries also inside rootfs.ext2, but if you run it like that it will use the libraries in /usr/lib. You either have to boot your system entirely with the rootfs.ext2, use qemu, or use a chroot environment. For chroot, you should use the 'tar' filesystem format, not ext2. See also here:
http://buildroot.uclibc.org/downloads/manual/manual.html#_chroot

Resources