How to pass arguments to commands in GNU make shell function? - makefile

I'm compiling a SDL program using a Makefile. Typically I can compile my SDL projects with gcc like so:
# gcc -c test.c `sdl-config --cflags`
# gcc -o test test.o `sdl-config --libs`
# ./test
I'm having trouble executing sdl-config in my Makefile however. This is what I have:
CFLAGS := $(shell sdl-config --cflags)
LDFLAGS := $(shell sdl-config --libs)
test : test.o
gcc $(CFLAGS) $(LDFLAGS) -o test test.o
test.o:
But I keep getting the sdl-config usage line back rather than the respective output. I suspect the arguments (--cflags and --libs) are not being passed to sdl-config.
How do I pass arguments to the shell function? Is there a better way to achieve my end goal?

What you're doing is correct. If you run "sdl-config --cflags" from the command line, does it work or do you get the usage line? The best way to debug scripting problems is to run the script from your shell prompt. If it works there, it will work in make, too.

Related

Im trying to compile program on Ubuntu and dont understand some things

Im a Windows dev who has no expirience on building C/C++ programs on Linux, but now I need to. Right way would be to go and learn Make and g++ compiler, but before I commit to that I want to figure out some basic stuff.
So I have .c program which is compiled with this makefile:
CUDA_VER=11.5
ifeq ($(CUDA_VER),)
$(error "CUDA_VER is not set")
endif
APP:= deepstream-test3-app
TARGET_DEVICE = $(shell gcc -dumpmachine | cut -f1 -d -)
NVDS_VERSION:=6.0
LIB_INSTALL_DIR?=/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/lib/
APP_INSTALL_DIR?=/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/bin/
ifeq ($(TARGET_DEVICE),aarch64)
CFLAGS:= -DPLATFORM_TEGRA
endif
SRCS:= $(wildcard *.c)
$(info info is $(SRCS))
INCS:= $(wildcard *.h)
PKGS:= gstreamer-1.0
OBJS:= $(SRCS:.c=.o)
CFLAGS+= -I../../../includes \
-I /usr/local/cuda-$(CUDA_VER)/include
CFLAGS+= $(shell pkg-config --cflags $(PKGS))
LIBS:= $(shell pkg-config --libs $(PKGS))
LIBS+= -L/usr/local/cuda-$(CUDA_VER)/lib64/ -lcudart -lnvdsgst_helper -lm \
-L$(LIB_INSTALL_DIR) -lnvdsgst_meta -lnvds_meta \
-lcuda -Wl,-rpath,$(LIB_INSTALL_DIR)
$(info info is $(CFLAGS))
all: $(APP)
%.o: %.c $(INCS) Makefile
gcc -c -o $# $(CFLAGS) $<
$(APP): $(OBJS) Makefile
gcc -o $(APP) $(OBJS) $(LIBS)
install: $(APP)
cp -rv $(APP) $(APP_INSTALL_DIR)
clean:
rm -rf $(OBJS) $(APP)
First thing I tried is to change this Makefile to compile it as C++ program. I changed .c file into .cpp, in makefile I change gcc to g++ everywhere and .c to .cpp everywhere. It gave me error that it couldnt find "main" entry point.
I gave up on that pretty fast and decided just to use lines output of original makefile, ending up with this:
g++ -c -o deepstream_test3_app.o -I../../../includes -I /usr/local/cuda-11.5/include -pthread -I/usr/include/gstreamer-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include ./deepstream_test3_app.cpp
g++ -o deepstream-test3-app deepstream_test3_app.o -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0 -L/usr/local/cuda-11.5/lib64/ -lcudart -lnvdsgst_helper -lm -L/opt/nvidia/deepstream/deepstream-6.0/lib/ -lnvdsgst_meta -lnvds_meta -lcuda -Wl,-rpath,/opt/nvidia/deepstream/deepstream-6.0/lib/
First question, can I combine this 2 launches of g++ into one?
Second, when I make changes to "./deepstream_test3_app.cpp" they are not noticed by compiler. I added
#include <iostream>
...
std::cout << "hello!" << std::endl;
and they are ignored. Its like g++ gets as input some other copy/older version of the file and I dont understand how to go about it.
Hope for any help, sorry if it's all sounds stupid.
Ignoring for the moment the issues surrounding compiling C code with a C++ compiler,
g++ -c -o deepstream_test3_app.o -I../../../includes -I /usr/local/cuda-11.5/include -pthread -I/usr/include/gstreamer-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include ./deepstream_test3_app.cpp
g++ -o deepstream-test3-app deepstream_test3_app.o -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0 -L/usr/local/cuda-11.5/lib64/ -lcudart -lnvdsgst_helper -lm -L/opt/nvidia/deepstream/deepstream-6.0/lib/ -lnvdsgst_meta -lnvds_meta -lcuda -Wl,-rpath,/opt/nvidia/deepstream/deepstream-6.0/lib/
First question, can I combine this 2 launches of g++ into one?
Yes. It is a common practice in makefiles to separate the compilation and linking steps, but that is not mandatory. When there are multiple sources, the separation makes it possible to limit recompilations to only the source files that have changed, but it doesn't make much difference, makefile or not, when there is only one source file.
The one-command version would be mostly a concatenation of the two commands you gave. One would omit the -c option, which instructs g++ to compile but not link, and one would omit the -o deepstream_test3_app.o, which specifies the name of the object file that we are no longer going to create. One would also omit the appearance of deepstream_test3_app.o drawn from the link (second) command, as we are going straight from source file to program. The rest of the options can be reordered to some extent, but all the -l options need to remain in the same order relative to each other and to any object files among the inputs. Here is how I would write it:
g++ -c -o deepstream_test3_app -I../../../includes -I /usr/local/cuda-11.5/include -pthread -I/usr/include/gstreamer-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -Wl,-rpath,/opt/nvidia/deepstream/deepstream-6.0/lib/ ./deepstream_test3_app.cpp -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0 -L/usr/local/cuda-11.5/lib64/ -lcudart -lnvdsgst_helper -lm -L/opt/nvidia/deepstream/deepstream-6.0/lib/ -lnvdsgst_meta -lnvds_meta -lcuda
Second, when I make changes to "./deepstream_test3_app.cpp" they are not noticed by compiler.
The compiler compiles the source file(s) you tell it to.
Its like g++ gets as input some other copy/older version of the file
It is possible that you are indeed telling it to compile a different version than the one you modified. It is also possible that compilation fails, so you don't get a new executable. And it is possible that when you try to run the result, you are not running the program you think you are running. We don't have enough information to know.
With regard to the last, however, do be aware that on Linux, unlike on Windows, the working directory is not automatically in the executable search path. If you want to run the compiled result from the above command, you would want to specify the path to it, which you could most easily do by prepending ./ to its simple name: ./deepstream-test3-app.

bash substring and positional parameters

I have to compile some mysql c api code and tired of writing all this in command line:
gcc main.c -o main `mysql_config --cflags --libs`
I wrote a shell script in bash and pass positional parameter in bash:
gcc $1 -o ${"$1":0:2} 'mysql_config --cflags --libs' but this doesn't work. I get error message: ./compile: line 2: ${"$1":0:-2}: bad substitution. Can someone please tell me what I am doing wrong?
The only way I got this to work is by assigning a new variable:
filename=$1;
gcc $filename -o ${filename:0:-2} `mysql_config --cflags --libs`
Is this the only way to do it or is there a way to fix what I am doing wrong in the first case?
You almost had it:
${1:0:2}
You don't need another reference to $1 inside the brackets since everything in it will be interpreted as the name of the variable, as in the case of ${filename:0:-2}.
In response to the comments under the question, here's an example makefile for this situation:
MSQL_FLAGS := $(shell mysql_config --cflags)
MSQL_LIBS := $(shell mysql_config --libs)
main : main.c
gcc $(MSQL_FLAGS) -o $# $< $(MSQL_LIBS)

clang: warning: -l*: 'linker' input unused

When I compile code using GNU Make I get multiple warnings like:
clang: warning: -lGui: 'linker' input unused
This is probably because I have messed something up in my Makefile (below). Can anyone point me toward the problem?
CXX=g++
CC=g++
CXXFLAGS=-g -Wall -W -Wshadow -Wcast-qual -Wwrite-strings $(shell root-config --cflags --glibs)
CPPFLAGS+=-MMD -MP
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
xSec_x: xSec_x.o xSec.o Analysis.o
-include xSec_x.d xSec.d Analysis.d
xSec.o: xSec.cpp xSec.h Analysis.h Analysis.cpp
xSec_x.o: xSec_x.cpp xSec.h Analysis.h
clean:
rm -f #rm -f $(PROGRAMS) *.o *.d
That message means you are passing linker flags (like -l which tells the linker to pull in a library) to the compiler.
This means that the result of running root-config --cflags --glibs is generating linker flags, and those are going into CXXFLAGS, which is being passed to the compiler. I don't know what root-config is, but you should investigate its command line and invoke it in a way where it doesn't generate linker flags. Probably removing the --glibs option will do it.
ETA: you really want to be using := to assign these flags variables if you're going to run $(shell ...) there. It will work either way, but if you use = then the shell command will be run every time make expands the variable, which is once per compilation. If you use := it will only be run once, when the makefile is parsed.
I got this same error and the reason was that I forgot to add -I in front of my included paths for cflags in makefile. For example:
CFLAGS += $(path)/dir/subdir/include -> Got the above mentioned error.
CFLAGS += -I$(path)/dir/subdir/include -> Fixed the issue.

windows equivalent of command substitution in makefiles

I want to display current build(hg revision) number in the about box of my program. I thought about using a "define" (std::string rev = REVISION;) in the code and pass the value to g++ via makefile:
$(CPP) -c main.cpp -o main.o -DREVISION=`hg id -i`
would work like a charm, but im developing on windows for windows, so my Q: how to create such a behavior on windows.
If you're using g++ then your assumption is mostly right, excepting that passing a macro definition is done using -D option, not -d. Also, $(CPP) in Make usually refers to C PreProcessor. C++ compiler is $(CXX).
$(CXX) -c main.cpp -o main.o -DREVISION=`hg id -i`
Regarding command substitution, it should work fine if you run your build in UNIX-ish compatibility layer, like Cygwin or MinGW. If not, you could avoid using command substitution at all, and pass the result of hg id -i to the compiler literally, e.g. as follows:
REVISION := $(shell hg id -i)
...
$(CXX) -c main.cpp -o main.o -DREVISION=$(REVISION)

Compile C++ with Cygwin

How do I compile my C++ programs in Cygwin. I have gcc installed. What command should I use? Also, how do I run my console application when it is in a .cpp extension. I am trying to learn C++ with some little programs, but in Visual C++, I don't want to have to create a seperate project for each little .cpp file.
You need to use a command like:
g++ -o prog prog.cpp
That's a simple form that will turn a one-file C++ project into an executable. If you have multiple C++ files, you can do:
g++ -o prog prog.cpp part2.cpp part3.cpp
but eventually, you'll want to introduce makefiles for convenience so that you only have to compile the bits that have changed. Then you'll end up with a Makefile like:
prog: prog.o part2.o part3.o
g++ -o prog prog.o part2.o part3.o
prog.o: prog.cpp
g++ -c -o prog.o prog.cpp
part2.o: part2.cpp
g++ -c -o part2.o part2.cpp
part3.o: part3.cpp
g++ -c -o part3.o part3.cpp
And then, you'll start figuring how to write your makefiles to make them more flexible (such as not needing a separate rule for each C++ file), but that can be left for another question.
Regarding having a separate project for each C++ file, that's not necessary at all. If you've got them all in one directory and there's a simple mapping of C++ files to executable files, you can use the following makefile:
SRCS=$(wildcard *.cpp)
EXES=$(SRCS:.cpp=.exe)
all: $(EXES)
%.exe: %.cpp
g++ -o $# $^
Then run the make command and it will (intelligently) create all your executables. $# is the target and $^ is the list of pre-requisites.
And, if you have more complicated rules, just tack them down at the bottom. Specific rules will be chosen in preference to the pattern rules:
SRCS=$(wildcard *.cpp)
EXES=$(SRCS:.cpp=.exe)
all: $(EXES)
%.exe: %.cpp
g++ -o $# $^
xx.exe: xx.cpp xx2.cpp xx3.cpp
g++ -o $# $^
echo Made with special rule.
You will need g++. Then try g++ file.cpp -o file.exe as a start. Later you can avoid much typing by learning about Makefiles.
if you want to use cygwin you should use the normal gcc syntax
g++ -o foobar foobar.cpp
but that doesn't really play well with Visual C++. I advise you to take a look into Eclipse CDT if you prefer using GCC over the visual C++ compiler.
What I do to compile a cpp program:
g++ -Wall Test.cpp -o Test
-Wall enables warning and error messages to be shown
-o Test creates an Test.exe after compilation
If you want to compile files separately:
g++ -Wall -c File1.cpp
g++ -Wall -c File2.cpp
Now create an executable with the combined object files as:
g++ -Wall File1.o File2.o -o File.exe
This way you can compile your header files and you can include in your application programs.

Resources