How can I compile all .cpp files except one using g++? - bash

To compile all C++ files in my source directory I run
g++ -std=c++17 ../src/*.cpp -o ../out/a.out
How can I compile all cpp files in a given directory except for main.cpp?

bash:
shopt -s extglob
g++ -std=c++17 ../src/!(main).cpp -o ../out/a.out
ref: https://www.gnu.org/software/bash/manual/bash.html#Pattern-Matching

for f in $(find /path/to/files -name "*.cpp" ! -name "main.cpp")
do
g++ -std=c++17 path/to/files/"$f" -o /path/to/out/....
done

We can filter the glob into a Bash array:
unset files
for i in ../src/*.cpp
do test "$i" = '../src/main.cpp' || files+=("$i")
done
g++ -std=c++17 "${files[#]}" -o ../out/a.out
Or, using GNU grep and mapfile:
mapfile -d $'\0' -t files < <(printf '%s\0' ../src/*.cpp | grep -zv '/main\.cpp$')
g++ -std=c++17 "${files[#]}" -o ../out/a.out

you can also ls all .cpp files and pipe that into grep -v, to exclude a specific result.
g++ -std=c++17 `ls *.cpp | grep -v main.cpp` -o a.out

Related

Makefile filter-out not removing a file from a list of files

I'm just starting out with Make and I have this file:
CODE_FILES := $(shell find . -regextype posix-egrep -regex ".*/[a-z_]*[a-z]\.(cpp|h)")
EXCLUDE := "./bytecode/operators.cpp"
CODE_FILES_COPY = $(filter-out $(EXCLUDE), $(CODE_FILES))
# All nested header files (.h) and source files (.cpp)
# all: a.out
a.out: main.cpp $(CODE_FILES_COPY)
#echo $(EXCLUDE)
#echo $(CODE_FILES_COPY)
#echo $(CODE_FILES)
clang++ main.cpp -o a.out -pthread -std=c++17 -g \
-Wall -Wpedantic -Wextra \
-D DO_CACHE_DECL
# Define: -D PLUMBER_DEBUG -D DO_CACHE_DECL -D TRACKER_DEBUG
./bytecode/operators.out: ./bytecode/operators.cpp
clang++ ./bytecode/operators.cpp -o ./bytecode/operators.out -std=c++17 \
-Wall -Wpedantic -Werror
make clean:
rm -f a.out ./bytecode/operators.out
However, CODE_FILES_COPY and CODE_FILES are exactly the same (a long list of files containing e.g. ./foo/bar/baz.cpp) - the filter-out seems to have not worked. What am I doing wrong in this situation?
I've also probably done some things wrong in the rest of the file, but the focus is on a.out being rebuilt if I modify ./bytecode/operators.cpp which I don't want to happen since it takes a long time to rebuild the whole project and very little time to compile operators.cpp.
You have quotes around "./bytecode/operators.cpp" in your EXCLUDE :=. Remove them and your exclude will match. Your files will not have quotes around them in the list.
Try using
$(info CODE_FILES: $(CODE_FILES))
$(info EXCLUDE: $(EXCLUDE))
to see the difference

Unterminated quoted string : bin/sh

I have the following makefile:-
all:
find | grep -E "\.c\$" | xargs gcc -c -I src -I include -w
gcc -o main *.o -lm -pthread
On running make I get the following error:-
find | grep -E "\.c\ | xargs gcc -c -I src -I include -w
/bin/sh: 1: Syntax error: Unterminated quoted string
Makefile:2: recipe for target 'all' failed
make: *** [all] Error 2
I tried answers to similar questions, but adding a SHEBANG line did not help. Also, I have already escaped the $ character. What am I doing wrong here?
make already does most of what you are trying to do. Assuming you aren't expecting find to look recursively in subdirectories, all you really need is
# Rule for building a c file
.c:
gcc -c -I src -I include -w -o $# $<
# Make sure all C files are compiled, then link the resulting object files
all: *.c
gcc -o main *.o -lm -pthread
If you were expecting to find C source files in subdirectories, you might want to restructure your project slightly, by adding Makefiles to each subdirectory and calling make recursively in those directories.

Pattern only matches once per invocation and make is deleting intermediate files

I have the following rules in a Makefile to build an executable in 3 stages:
all: build/myexe
build/myexe: output/main_dats.o output/foo_dats.o | build/
gcc $^ -o $#
output/%.o: output/%.c
patscc -c $< -o $#
output/%_dats.c: src/%.dats | output/
patsopt -cc -o $# -d $<
build/:
mkdir -p build/
output/:
mkdir -p output/
An src/%.dats source file is used to generate an output/%_dats.c source file which is compiled to an output/%.o object file and finally they are linked into the executable build/myexe.
Running make the first time will only successfully build the first of the two .o files:
$ make
mkdir -p output/
patsopt -cc -o output/main_dats.c -d src/main.dats
patscc -c output/main_dats.c -o output/main_dats.o
make: *** No rule to make target `output/foo_dats.o', needed by `build/myexe'. Stop.
rm output/main_dats.c
But running again will build the second .o file and successfully link the executable:
$ make
patsopt -cc -o output/foo_dats.c -d src/foo.dats
patscc -c output/foo_dats.c -o output/foo_dats.o
mkdir -p build/
gcc output/main_dats.o output/foo_dats.o -o build/myexe
rm output/foo_dats.c
and note that at the end of each invocation the command rm output/..._dats.c is deleting the generated .c source file.
Here is a Makefile written without pattern matching:
all: build/myexe
build/myexe: output/main_dats.o output/foo_dats.o | build/
gcc $^ -o $#
output/foo_dats.o: output/foo_dats.c
patscc -c $< -o $#
output/main_dats.o: output/main_dats.c
patscc -c $< -o $#
output/foo_dats.c: src/foo.dats | output/
patsopt -cc -o $# -d $<
output/main_dats.c: src/main.dats | output/
patsopt -cc -o $# -d $<
build/:
mkdir -p build/
output/:
mkdir -p output/
which works more predictably:
$ make
mkdir -p output/
patsopt -cc -o output/main_dats.c -d src/main.dats
patscc -c output/main_dats.c -o output/main_dats.o
patsopt -cc -o output/foo_dats.c -d src/foo.dats
patscc -c output/foo_dats.c -o output/foo_dats.o
mkdir -p build/
gcc output/main_dats.o output/foo_dats.o -o build/myexe
and note that the generated .c files are not being removed any more.
Apparently I am misusing the pattern matching mechanism. I know there is some kind of wildcard function but I believe it is intended for file globbing.
To avoid removing intermediate files, you just need to list them as actual targets somewhere. For example you could write a separate rule:
make_srcs: output/main_dats.c output/foo_dats.c
You don't have to list this target make_srcs as a prerequisite, or provide it a recipe, etc. Just listing the _dats.c files as actual targets or prerequisites in the makefile is enough to keep them from being deleted.
As for your "only building some output" behavior, I don't know: it works fine for me:
$ make --version | head -n1
GNU Make 4.2.1
$ cat Makefile
all: build/myexe
build/myexe: output/main_dats.o output/foo_dats.o | build/
touch $#
output/%.o: output/%.c
touch $#
output/%_dats.c: src/%.dats | output/
touch $#
build/:
mkdir -p build/
output/:
mkdir -p output/
make_srcs: output/main_dats.c output/foo_dats.c
$ rm -rf output build && make
mkdir -p output/
touch output/main_dats.c
touch output/main_dats.o
touch output/foo_dats.c
touch output/foo_dats.o
mkdir -p build/
touch build/myexe
So there's something about your setup which hasn't been made clear in your question. As the comment suggested you need to run make -d (I would leave off the -R option, I don't know why you'd add that) and figure out why make throws that error.
Pattern rules should ideally be deprecated. They are prone to over-matching (because, well, patterns), they can be hard to get working, they bring with them the whole "intermediate target" issue (that's the deletion of output/*.c files that you are observing), they need another dubious feature ("secondary expansion") to make them usable in some more involved scenarios, etc.
In short: using pattern rules is not advised, and using multi-level pattern rules is definitely not advised. Just more trouble than it's worth. IMHO, anyway.
(end rant)
So I suggest that you write a simple macro instead, so your makefile ends up looking like this:
all: build/myexe
# $(call dats,basename)
define dats
output/$1_dats.o: output/$1_dats.c
patscc -c $$< -o $$#
output/$1_dats.c: src/$1.c | output
patcc -cc -o $$# -d $$<
endif
build/myexe: output/main_dats.o output/foo_dats.o | build
gcc $^ -o $#
$(eval $(call dats,foo))
$(eval $(call dats,main))
build:
mkdir -p build
output:
mkdir -p output

Redirecting input separated with spaces

Hi I've a question about feeding inputs to this simple bash script I wrote. All it does is add a set of flags to my compile operation to save me having to write them in myself every time. I can run it by using echo myprogram.c -o myprogram -llibrary | ./Compile.
But I can't find a way to run it in the way I expected to be able to, ./Compile < myprogram.c -o myprogram -llibrary
I've tried a few combinations of quotes and brackets to no avail, can anyone tell me how to feed the same input as produced by echo using the redirect input command.
#!/bin/bash
# File name Compile
#Shortcut to compile with all the required flags, name defaulting to
#first input ending in .c
echo "Enter inputs: "
read inputs
gcc -Wall -W -pedantic -std=c89 -g -O $inputs
exit 0
Just change your shell to:
#!/bin/bash
gcc -Wall -W -pedantic -std=c89 -g -O "$#"
Then you can only write(no redirection needed):
./Compile myprogram.c -o myprogram -llibrary
BTW, don't explicitly write exit 0 at end of this shell. It is redundant when gcc succeeds, and is wrong when gcc fails(exit code 1 will be overwritten).
You can use process substitution:
./Compile < <( echo myprogram.c -o myprogram -llibrary )
the above line produces the same result as your original command:
echo myprogram.c -o myprogram -llibrary | ./Compile

gcc sequential list of -L paths and -l libraries

I am looking at a Makefile written by someone else and I see that (after variable expansion) he calls gcc with a sequential list of -L paths followed by -l libraries.
For example:
gcc file.o -Lpath_1 -la -lb -c -Lpath_2 -ld -le -lf ... -o binary_file
My questions are:
Does this call guarantee that gcc:
only looks into path1 when searching for -la, -lb and -lc,
and only looks into path2 when searching for -ld, -le, and -lf?
Is this "proper" (recommended) gcc syntax?

Resources