Best practices for linking with X11 libraries - makefile

I have an X11 app I developed on a RedHat box. When I moved to CentOS, the X11 libraries moved as well. From /usr/X11R6/lib64 to /usr/X11R6/lib (even though both were running 64-bit), and I have seen them elsewhere on other Linux/Unix systems.
I had used the -I and -L compile flags to point to the X11 lib directories directly in the makefile, but have since realized, this requires changes to the makefile when switching machines.
My question is, for the sake of portability... should I keep using -I and -L in the makefile? And if so, is there a way to detect the location automatically? Or, should I just rely on the user having these in their LD_LIBRARY_PATH?

If you're relying on GNU make, you could do something like:
X11LIBS ?= $(word 1, $(dir $(wildcard /usr/X11R6/lib64/libX11.so.*)))
X11LIBS ?= $(word 1, $(dir $(wildcard /usr/X11R6/lib/libX11.so.*)))
X11LIBS ?= $(word 1, $(dir $(wildcard /usr/lib/libX11.so.*)))
# and so on for whatever paths you come across
LDFLAGS += -L$(X11LIBS) -R$(X11LIBS)
This allows the user to override X11LIBS on the command line (make X11LIBS=/usr/local/X11R6/lib), and tries a couple of locations if no user option is given.
You could even abort the build if no path is found, like
ifeq ($(X11LIBS),)
$(error Could not find X11 library path, please specify in X11LIBS
endif
All this is only an option if you're content with restricting the Makefile's portability: non-GNU make very likely won't be happy with these macros.

Related

Re-evaluating GNU make makefile variable

I have inherited a large branched project? that requires a volatile set of .a archives $(LIB_FILES) to be included into link target, located in some directories $(LIB_DIRS). I can write an expression like this:
LIBDEP = $(foreach ldir, $(LIB_DIRS), \
$(filter $(addprefix %/, $(LIB_FILES)), $(wildcard $(ldir)/* )))
The problem is that they might not exist at moment of make's invocation and would be built by invoking $(MAKE) inside of another target's rule, which is a prerequisite to the link step.
The problem is actual list of files that should be created varies on external factors determined at their build steps, that I can't hard-code it properly, without turning makefile into a spaghetti mess and said variable is not re-evaluated at the moment of link command invocation.
I have suspicion that $(eval ) function can be used somehow, but manual is not very forthcoming as well as I didn't found examples of its use in this way.
Toolchain: GCC and binutils, make 3.81
Another solution is to create an explicit dependency of your make script on the output of the step which currently creates the variable $(LIB_FILES). This is what the manual is dealing with in the chapter How makefiles are remade and it aims at the technique which make is best at, namely deriving dependencies from the existence and timestamp of files (instead of variables). The following hopefully depicts your situation with the process of deducing a new set of libraries simulated by the two variables $(LIBS_THIS_TIME) and $(LIB_CONFIG_SET).
LIBS_THIS_TIME = foo.a:baz.a:bar.a
LIB_CONFIG_SET = $(subst :,_,$(LIBS_THIS_TIME))
include libdeps.d
linkstep:
#echo I am linking $^ now
touch $#
libdeps.d: $(LIB_CONFIG_SET)
-rm libdeps.d
$(foreach lib,$(subst :, ,$(LIBS_THIS_TIME)),echo linkstep: $(lib) >> libdeps.d;)
$(LIB_CONFIG_SET):
touch $#
If make finds that libdeps.d is not up to date to your current library configuration it is remade before make executes any other rule, although it is not the first target in the makefile. This way, if your build process creates a new or different set of libraries, libdeps.d would be remade first and only then make would carry on with the other targets in your top makefile, now with the correct dependecy information.
It sometimes happens that you need to invoke make several times in succession. One possibility to do this is to use conditionals:
ifeq ($(STEP),)
all:
<do-first-step>
$(MAKE) STEP=2 $#
else ifeq ($(STEP),2)
all:
<do-second-step>
$(MAKE) STEP=3 $#
else ifeq ($(STEP),3)
all:
<do-third-step>
endif
In each step you can generate new files and have them existing for the next step.

Universal make-based build system design

I am aware of tools like CMake and GNU Autotools but I'm trying to write a universal build system myself, to use for my C and C++ projects. I'll briefly explain how it works and hopefully, someone can suggest either improvements or a better design altogether.
The build system proper lives in one of the project's subdirectories (I import it as a Git submodule). The project's root directory has a wrapper makefile that defines a couple of macros and includes the main makefile from said subdirectory. That does most of the work: it follows the directory organization scheme (i.e., it outputs libraries in lib, binaries in bin, etc.), it handles automatic dependencies for the source code and the DocBook documentation, and provides the de facto standard targets: all, test, clean, install, as well as others.
Here's what a wrapper makefile that builds two binaries, foo and bar, might look like:
# foo-specific macros
FOO_SRC_FILES = foo1.c foo2.c foo3.c
FOO_OBJ_FILES = $(FOO_SRC_FILES:.c=.o)
FOO_BIN_FILE = foo
# bar-specific macros
BAR_SRC_FILES = bar1.c bar2.c
BAR_OBJ_FILES = $(BAR_SRC_FILES:.c=.o)
BAR_BIN_FILE = bar
# Inform the build system about them
SRC_FILES = $(FOO_SRC_FILES) $(BAR_SRC_FILES)
OBJ_FILES = R(BAR_OBJ_FILES) $(BAR_OBJ_FILES)
BIN_FILES = $(FOO_BIN_FILE) $(BAR_BIN_FILE)
# Only install the binaries. If I were building a library, I would instead
# select the "lib" and perhaps "include" directories.
INSTALL = bin
INSTALL_DIR = /usr/share
# Use the build system
include build/build.mk
Now here's the problem. While build.mk can use pattern rules to create dependency and object files, there's only one OBJ_FILES and only one BIN_FILES. So if I put a pattern rule like the following in the build system that looks like this:
$(BIN_DIR)/$(BIN_FILES): $(OBJ_FILES:%=$(OBJ_DIR)/%) $(LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $(OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(LIB_FILES:lib%.a=-l %)
then foo would depend on and link with everything that bar does and vice versa. So what I end up doing is asking the user to put these rules in the wrapper makefile, even though they feel like they belong in build.mk:
$(BIN_DIR)/$(FOO_BIN_FILE): $(FOO_OBJ_FILES:%=$(OBJ_DIR)/%) $(FOO_LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $(FOO_OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(FOO_LIB_FILES:lib%.a=-l %)
$(BIN_DIR)/$(BAR_BIN_FILE): $(BAR_OBJ_FILES:%=$(OBJ_DIR)/%) $(BAR_LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $(BAR_OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(BAR_LIB_FILES:lib%.a=-l %)
The same issue applies to libraries as well, of course. The upside is that these rules can be copied and pasted almost verbatim; only the prefixes need to be changed (e.g., FOO or BAR).
Ideas to fix this include:
Asking the user to have separate wrapper makefiles for separate things (e.g., one for foo and another for bar) but that is just terrible.
Changing things up a little bit and then using m4 to do some preprocessing but I don't want to go through that unless a more elegant solution doesn't exist.
I would really appreciate some ideas.
PS: I know that the pattern matching expressions in the last two code samples can be replaced with text functions but those are GNU Make-specific. The style I used is more portable and is in fact on the list of additions for the next version of the POSIX standard.
I have begin to develop a similar system for my own C projects, but the logic I use does rely on some features which I believe are specific to GNU Make.
The main idea is to use a combinaison of $(eval) and $(call), by defining the logic of the build system, and then applying to the project tree.
To do so, I have in each of my directories and subdirectories a piece of Makefile of the following form, which I name Srcs.mk:
SRC := foo.c foo_bar.c bar.c
TARGET := foo_bar
SRC_DIR := src
OBJ_DIR := obj
I define a variable, which is in fact a macro, which is expanded with $(call) and then passed to $(eval). It's defined this way:
define get_local_variables
include Srcs.mk
$1SRC := $(SRC)
$1SRC_DIR := $(SRC_DIR)
$1OBJ_DIR := $(OBJ_DIR)
$1TARGET := $(TARGET)
TARGET :=
SRC :=
SRC_DIR :=
OBJ_DIR :=
$(call get_local_variables, $(DIR)) will expand to the above, with $1 replaced by the content of $(DIR). Then it will be treated as a Makefile fragment by $(eval)
This way, I fill per-directory variables for each of my directory.
I have then a handful or other rules which use this variables, using the same principles.
### Macros ###
obj = $(patsubst %.c,$($1OBJ_DIR)/%.o,$($1SRC))
define standard_rules
$($1TARGET): $(obj)
$$(LINK)
$(obj): $($1OBJ_DIR)/%.o:$($1SRC_DIR)/%.c | $($1OBJ_DIR)
$$(COMPILE)
endef
The variable are computed $(call), then expanded and read as makefile fragments by $(eval).
(I use static pattern rules but that it not intrinsic to the idea).
The whole idea is basically to define directories as a kind of namespace, with data attached to them, and then run function over them.
My actual system is a bit more complicated, but that the whole idea.
If you have a way to emulate $(eval) and $(call) (I think these are specific to GNU make, but not sure), you could try that approach.
You can also implement non recursive make this way, by adding a SUBDIRS variables in each directory and running recursively the same macro which is run on the current one. But it should been done carefully, not to mess it up with the order of expansion and evaluation in make.
So get_local_variables need to be evaluated before the rest of the macros are expanded.
(My project is visible on my Github account if you want to take a look, under make-build-system. But it is far from be complete enough^).
Be aware, though, that this is quite painful to debug when things go wrong. Make (at least, GNU) basically catch the error (when there is one) on the higher $(call) or $(eval) expansion.
I have developed my own non-recursive build system for GNU make, called prorab, where I solved the problem you described as follows.
The approach to solve your problem is somewhat similar to what #VannTen described in his answer, except that I use a macro to clean all state variables before defining build rules for the next binary.
For example, a makefile which builds two binaries could look like this:
include prorab.mk
this_name := AppName
this_ldlibs += -lsomelib1
this_cxxflags += -I../src -DDEBUG
this_srcs := main1.cpp MyClass1.cpp
$(eval $(prorab-build-app))
$(eval $(prorab-clear-this-vars))
this_name := AnotherppName
this_ldlibs += -lsomelib1
this_cxxflags += -I../src -DDEBUG
this_srcs := main2.cpp MyClass2.cpp
$(eval $(prorab-build-app))
So, in this example it will build two binaries: AppName and AnotherppName.
As you can see the build is configured by setting a number of this_-prefixed variables and the calling the $(eval $(prorab-build-app)) which expands to defining all the build, install, clean etc. rules.
Then a call to $(eval $(prorab-clear-this-vars)) clears all this_-prefixed variables, so that those can be defined again from scratch for the next binary, and so on.
Also, the very first line which includes the prorab.mk also cleans all this_-prefixed variables of course, so that the makefiles can be safely included into each other.
You can read more about that build system concepts here https://github.com/cppfw/prorab/blob/master/wiki/HomePage.adoc

Setting OS variable from rule

I am trying to build a shared library with one set of code, and everything works, except for this issue with my Makefile. Here's my (simplified) Makefile thus far:
OBJS = bar.o
libfoo.so: OS = LINUX # These don't seem to happen
libfoo.dll: OS = WINDOWS
# Linux
ifeq ($(OS), LINUX)
CC = gcc
...
# Windows
else ifeq ($(OS), WINDOWS)
CC = i686-pc-mingw32-gcc
...
endif
all: libfoo.so libfoo.dll
libfoo.so: clean $(OBJS)
...
libfoo.dll: clean $(OBJS)
...
bar.o: bar_$(OS).c bar.h
...
So, when you type make libfoo.so, I expect it to set OS = LINUX first. Then, when it gets to bar.o (it is a dependency of libfoo) it should know which bar_$(OS).c to use. However, I get the error:
make: *** No rule to make target `bar_.c', needed by bar.o. Stop.
Which tells me that when it tries to make bar.o, $(OS) is not set. But shouldn't that be the first thing that happens when I try to make libfoo.so, and that rule is evaluated?
Target-specific variables are available in the body of the rule, not in its prerequisites. But even if you could get this to work, you'd be asking for trouble: if you build one library and then the other, there's no way for Make to know that the bar.o that was made for the first is wrong for the second and should not be used.
There are several ways to get the effect you want, but none is perfect. I'd suggest using two different object file names, like bar_unix.o and bar_windows.o.
If you want to set a target-specific variable, and then have that variable available outside the body of that rule, you can recursively call the Makefile, after exporting the variable:
OBJS ?= foo.o # Use ? so it isn't blown away on recursive call
libfoo.so: OS = LINUX
libfoo.so: OBJS += linux_only.o
libfoo.so:
$(MAKE) -s build_libfoo_linux
build_libfoo_linux: $(OBJS)
#echo "OS = $(OS)" # Should print "OS = LINUX"
export OS # Can be anywhere
You have to remember to export the variables you want to "persist" after the recursive make call. And also, as shown above, if you append to any variables before the call, you'll want to make their initial assignment with ?= so they aren't set the second time.
You might want to detect the OS using uname and then conditionally compile. This explains

makefile question

I'm looking to modify a makefile to include a local directory of headers. However, most of the makefile compilation happens outside my current makefile limiting what I can do. Here's the current makefile:
TARGET = final
LIBS = -lsimple -lcheck -lsuif -luseful
OBJS = doproc.o main.o
all: $(TARGET)
install-bin: install-prog
include $(SUIFHOME)/Makefile.std
The directory of headers (boost) I wish to use of is in the same directory as this makefile and doproc.cc/main.cc. I tried modifying the makefile to this
TARGET = final
INC= -I/boost
LIBS = -lsimple -lcheck -lsuif -luseful
OBJS = doproc.o main.o
all: $(TARGET) $(INC)
install-bin: install-prog
include $(SUIFHOME)/Makefile.std
but to no effect. I have an account on this machine but no root access and I can't change Makefile.std. If this is trivial, I apologize. I'm not used to using makefiles.
Thanks in advance
EDIT:
The suggestions so far are helpful for getting me the right directory. But It's still not making the directory be included in the compilation
Okay, it's really hard to discern all details without knowing the internals of Makefile.std. However, let's assume that $(INC) is not somehow magically consumed inside Makefile.std (it's at least not one of the default macros, see make -p for those), in this case you would have to append your include folders to CXXFLAGS (for C++) or CFLAGS (for C) like so:
CXXFLAGS+=$(CXXFLAGS) -I$(MAKEDIR)/boost
Side-note: There are variations on this, but from where I stand
all: $(TARGET) $(INC)
doesn't make too much sense for a value of -I/boost or even the corrected -I$(MAKEDIR)/boost. The reason is that it is no variable assignment (which is possible instead of dependencies) and thus would be considered as a file dependency (unless declared .PHONY).
I should add that I'm assuming the usual default rules that come with GNU make. Otherwise you'd have to show us the actual rules %.o: %.cpp etc ...
Use this - Your current INC specifies /boost not ./boost:
INC= -I$(PWD)/boost
/ is filesystem root; ./ is current directory.
Did you mean -I./boost? -Iboost works too.
Remove the leading / in front of boost. You're telling the compiler to look in /boost under the root folder.

Compiling with different flags in Makefile?

I have a single program used to interact with a joystick. It uses conditional compilation to specify a specific joystick. We do this right now by just hard coding the correct flag into the Makefile.
I'd like to make it so it uses a different flag based on the command given to the Makefile. So for example, I currently have this:
.PHONY: saitek
saitek: $(SOURCES)
$(COMPILE) -DSAITEK
.PHONY: logitech
logitech: $(SOURCES)
$(COMPILE) -DLOGITECH
I want only one of these commands to ever be run, and I want them all to make the same executable. But if I rerun 'make' it will compile the program again. I'd like it to recognize that it's already built the program.
Is there anyway to do this with a Makefile?
If you're using GNUMake, this will do what you're asking. It uses a different flag based on the command given to Make, and it doesn't rebuild the program unnecessarily.
.PHONY: saitek logitech
saitek: JOYSTICK=SAITEK
logitech: JOYSTICK=LOGITECH
# Suppose the actual name of your executable is "program"
saitek logitech: program
program: $(SOURCES)
$(COMPILE) -D$(JOYSTICK)
GNU make inherits variables from its environment, so if you specify
$ JOYSICK=LOGITECH
in your shell, and use
CFLAGS+=-D$(JOYSTICK)
in your makefile.
I question the necessity of this. You could just call make as something like make CFLAGS=-DSAITEK or use autoconf and substitute in the correct defines.
That said, how about something like this:
saitek logitech: program
.PHONY: saitek logitech
ifeq ($(MAKECMDGOALS),saitek)
CFLAGS += -DSAITEK
endif
ifeq ($(MAKECMDGOALS),logitech)
CFLAGS += -DLOGITECH
endif
program: $(OBJS)
# Whatever

Resources