# Completed Makeshift Bash & Makefile To Compile C++ Examples

I am just experimenting with Bash and Makefiles to make my life easier, and here's what I came up with.

Scenario: Files are in a directory containing a tutorial, or an iteration. For example, one wants to experiment with Boost Libraries.

Code: There's two bashes and a Makefile. First the Makefile

Makefile

#PROJECT=
#DIRECTORY=
#PARAMETER=
OBJ=$(DIRECTORY)$(PROJECT).o
OUT=$(DIRECTORY)$(PROJECT)
CPP=$(DIRECTORY)$(PROJECT).cpp
CC=g++-7
DEBUG=-g
CFLAG=-Wall -c $(DEBUG) LFLAG=-Wall$(DEBUG)
I=-I/user/local/boost
STD=-std=c++1z

$(OBJ):$(CPP)
$(CC)$(STD) $(CFLAG)$(CPP)

$(OUT):$(OBJ)
$(CC)$(STD) $(LFLAG)$(OBJ) -o $(OUT)$(I) $(L) clean: rm -rf$(OUT) || true
rm -rf $(OBJ) || true redo: make PROJECT=$(PROJECT) DIRECTORY=$(DIRECTORY) PARAMETER=$(PARAMETER) clean
clear
make PROJECT=$(PROJECT) DIRECTORY=$(DIRECTORY) PARAMETER=$(PARAMETER)$(OBJ)
mv $(PROJECT).o$(DIRECTORY) || true
make PROJECT=$(PROJECT) DIRECTORY=$(DIRECTORY) PARAMETER=$(PARAMETER)$(OUT)
./$(DIRECTORY)$(PROJECT) $(PARAMETER)  What this does is make the file with minimal editing allowing bash scripts to run the makes. Though I still need to work-out and externalize the L variable for bash scripts. Here's m.sh. A better name could be chosen, but I was in a hurry. Perhaps bmake.sh. #!/bin/bash PROJECT=$1
DIRECTORY=$2 ACTION=$3
FLAG=$4 PARAMETER=$5

make PROJECT=$PROJECT DIRECTORY=$DIRECTORY PARAMETER=$PARAMETER$ACTION $FLAG unset PROJECT unset DIRECTORY unset ACTION unset FLAG unset PARAMETER  This is the function that calls the Makefile. So instead of "make me some blah blah, it's "bash make.sh". So, the test case is for this tutorial found here. It's a remarkable tutorial on network programming with Boost::Asio, and all of the examples are in a zip (1a.cpp, 1b.cpp, etc.). THe files are to be unzipped into a directory, which is hard coded in a bash, in this example "example/" is used. The scripts do what I wish. I suppose I have two scripts to make the distinction between the general and the specific. #!/bin/bash PROJECT=$1
DIRECTORY=example/ # or $2 ACTION=$3
FLAG=$4 PARAMETER=$5

bash m.sh $PROJECT$DIRECTORY $ACTION$FLAG $PARAMETER  This file is not strictly necessary, though, as you can simply execute m.sh. I simply created it for this test example. For FLAG, I recommend passing in -n, which outputs what the make file would've done if it had not been a test. This is very useful as it is dangerous to execute the makes cold out of the code freezer. I thought I'd share and get some feedback on how to make it better as I am somewhat of a beginner on both bash scripting and Makefiles. This is how to do a trial run: bash example.sh 1a example redo -n  And, this is how to run it: bash example.sh 1a example redo  Result: make PROJECT=1a DIRECTORY=example/ PARAMETER= example/1a.o make[1]: Entering directory '/wherever' g++-7 -std=c++1z -Wall -c -g example/1a.cpp make[1]: Leaving directory '/wherever' mv 1a.o example/ || true make PROJECT=1a DIRECTORY=example/ PARAMETER= example/1a make[1]: Entering directory '/wherever' g++-7 -std=c++1z -Wall -g example/1a.o -o example/1a -I/user/local/boost -lboost_system -lboost_thread -lpthread make[1]: Leaving directory '/wherever' ./example/1a Do you reckon this line displays?  The redo allows it to clean itself from the last build, and do the build over again. It's important to force the directory so that you never do something stupid like delete a bunch of files. Makefile (redo) redo: make PROJECT=$(PROJECT) DIRECTORY=$(DIRECTORY) PARAMETER=$(PARAMETER) clean
clear
make PROJECT=$(PROJECT) DIRECTORY=$(DIRECTORY) PARAMETER=$(PARAMETER)$(OBJ)
mv $(PROJECT).o$(DIRECTORY) || true
make PROJECT=$(PROJECT) DIRECTORY=$(DIRECTORY) PARAMETER=$(PARAMETER)$(OUT)
./$(DIRECTORY)$(PROJECT) $(PARAMETER)  Though, instead of two bash files, I really only need one bash file with a configuration file, which the bash file can draw upon, which I think is the next logical step. Though it does suit my needs. Enjoy, and if so inclined, comment, and let me know how to best make a config file for this. Bash it if you must, haha. • Take a look at: Built Rules Makefile and example usage: Example Usage Makefile – Martin York Jun 16 '17 at 9:26 • I think that CMake will make your life much easier. It is much easier to deal with(in my experience) and is cross platform. – Incomputable Jun 16 '17 at 23:08 ## 3 Answers The biggest problem I can see is that you are not using idiomatic Makefile names, which will confuse readers and frustrate users who want to change things via the command line. Typically CFLAGS is compiler flags passed to the C compiler, CPPFLAGS the preprocessor and CXXFLAGS the C++ compiler.$(CPP), $(I) and$(L) are are also just plain confusing and hard to read. I'd change $(CPP) to$(SOURCES), and $(I) to$(INCLUDES) AND $(L) to$(LDFLAGS). Please see the manual; the tutorial you are likely following (that I found via google) gives bad advice).

Another problem is that you put -c inside CFLAGS. This isn't a good place to put it as if someone wants to amend/change CFLAGS, then your Makefile will no longer work. You should have that line under a target/rule like this:

$(OBJ):$(CPP)
$(CC)$(STD) -c $^ -o$@


You should always use the automatic variables here and not the actual variables.

# Use Make built-in rules to compile source

The Makefile duplicates rules that are built into Make. Instead of writing

$(OBJ):$(CPP)
$(CC)$(STD) $(CFLAG)$(CPP)

$(OUT):$(OBJ)
$(CC)$(STD) $(LFLAG)$(OBJ) -o $(OUT)$(I) $(L)  You only need to write default:$(OUT)
.PHONY: default


You will need to re-write to use the standard Make variables to take advantage of the built-in rules:

CXX := g++-7
# (not CC)

The redo target uses make - it's important if using a sub-make that you use $(MAKE) rather than make. That does the Right Thing in parallel builds and when just showing the commands (e.g. with make -n). And there's no need to explicitly pass Make variables to a sub-make - that happens automatically. The redo target is probably unnecessary anyway - you'll get the same effect much more simply by typing make -B. # Always specify delete-on-error Make doesn't automatically remove a target if the command fails half-way through, unless you add this line:  .DELETE_ON_ERROR:  Every makefile should include that - it's very rare you'd like a half-written file to be considered as completely made. BTW, is that a typo, or do you really have Boost in /user/local/ instead of /usr/local/? Sure: #!/bin/bash  This does make it readable. PROJECT=$1
DIRECTORY=$2 ACTION=$3
FLAG=$4 PARAMETER=$5


Here you should quote all your parameters.

make PROJECT=$PROJECT DIRECTORY=$DIRECTORY PARAMETER=$PARAMETER$ACTION $FLAG  If any of the parameters have a space (modern directories tend to have them). So best just to add double quotes around all parameters (I personally also like to add {} to make them stand out more but thats more optional). make PROJECT="${PROJECT}" DIRECTORY="${DIRECTORY} PARAMETER="${PARAMETER}" "${ACTION}" "${FLAG}"


These are completely un-needed. The set above don't export them to the environment. So when this shell exits they don't exist anymore.

unset PROJECT
unset DIRECTORY
unset ACTION
unset FLAG
unset PARAMETER


In this line:

    mv $(PROJECT).o$(DIRECTORY) || true


The || just hides errors. You still want this to fail. Make has a special prefix that makes it ignore errors and continue. The following has the same affect:

    -mv $(PROJECT).o$(DIRECTORY)


Same applies to clean

clean:
-rm -rf $(OUT) -rm -rf$(OBJ)


When you specify directories they normally don't include the trailling slash. So the following may not work as you expect:

OBJ=$(DIRECTORY)$(PROJECT).o
OUT=$(DIRECTORY)$(PROJECT)
CPP=$(DIRECTORY)$(PROJECT).cpp


Personally I think CPP should be SRC and OBJ should be generated from the set of SRC files.

SRC=$(wildcard$(DIRECTOR)/*.cpp)
OBJ=$(patsubst %.cpp, %.o,$(SRC))


Prefer the standard naming convention

CC is the C compiler. CXX is the C++ compiler

CC=g++-7


CFLAGS (note trailing S) is flags for the C compiler. CXXFLAGS are the flags for the C++ compiler.

CFLAG=-Wall -c $(DEBUG)  The linker flags should be in LDFLAGS (note trailing S) LFLAG=-Wall$(DEBUG)


You can use += on variables in Make to incrementally add flags. This is better because a lot of people will set the standard flags on the command line before calling make

This is quite common

CXXFLAGS=-O3 make

Make also has a bunch of standard rules:

# Standard C++ rule
%.o: %.cpp
$(CXX)$(CPPFLAGS) $(CXXFLAGS) -c$@ $<  So using this you can remove your custom rule. So I would add the following definition CXX =$(CC)
CXXFLAGS += $(CFLAG)$(I) $(L)$(STD)


Then remove your rule for building objects.