I have a directory where I have some C program files and a Makefile with executables of those C files. I can simply run this to remove all the executable at once in rm !(*.c|Makefile).
But when i am adding this to my makefile
CFLAGS= -Wall -g -lm
SHELL=/bin/bash
careful:
echo "nothing"
clean:
rm -i !(*.c|Makefile)
I am getting this error while executing the command
/bin/bash: -c: line 1: syntax error near unexpected token `('
/bin/bash: -c: line 1: `rm -i !(*.c|Makefile)'
make: *** [Makefile:8: clean] Error 2
I am not too knowledgeable about Bash syntax but I know that () is treated as a subshell, I think that's why I can't run rm (*.c) directly in the terminal because *.c is not a command (or any valid syntax). But running rm -i !(*.c) works in the terminal. So I guess !() is treated differently in Bash.
My assumption on all this: in Makefile when I am running make clean it is treating !(*.c|Makefile), differently than in normal terminal, or somehow it is disregarding ! in !(*.c|Makefile)
So my questions are:
Are !() and () treated differently in Bash?
Why does !(wildcard) work in the terminal but not in my Makefile
How can I make it work in the Makefile?
The Bash-specific extended globbing patterns are not enabled out of the box for noninteractive shells.
!(...) is purely part of this wildcard syntax, and has nothing with subshells to do.
Probably a better solution anyway is to refactor this so you don't depend on Bash.
.PHONY: clean
clean:
rm -i $(filter-out $(wildcard *.c) Makefile,$(wildcard *))
The Bash Reference Manual doesn't have a good anchor to link to, but the extended globbing syntax available with extglob is described in the section on Pattern Matching
If you really wanted to, you could shopt -s extglob in your Makefile, too, but this gets rather complicated as you will have to hide the statement from the parser until the shell is configured to understand this syntax. For example,
.PHONY: clean
clean:
shopt -s extglob; \
eval 'rm -i !(*.c|Makefile)'
Notice also how shopt and eval need to be on the same logical line, as make out of the box executes each recipe line in a separate new shell instance (though you can change that with .ONESHELL)
Related
I'm trying to use the makefile on windows, it seems not to be very common to use Makefile on Windows, so I followed some steps installing MingW, which is used for compilation, but when running make, this error occurred:
mkdir -p ./obj/
The command syntax is incorrect.
Makefile:30: recipe for target 'obj/main.o' failed
mingw32-make.exe: *** [obj/main.o] Error 1
Here's the code:
NAME = minishell
LIBFT = libft.a
CC = cc
CF = -g -Wall -Wextra -Werror
CFI = -I $(INCLUDE)
CREADLINE = -lreadline
LIBFT_PATH = ./libs/libft/
SRC_PATH = ./sources/
OBJ_PATH = ./obj/
INCLUDE = ./includes/
SRC = main.c\
prompt.c\
exec.c pipe.c paths.c command.c\
utils_pipes.c\
signals.c\
VPATH := $(SRC_PATH)\
$(SRC_PATH)prompt\
$(SRC_PATH)execute\
$(SRC_PATH)utils\
OBJ = $(addprefix $(OBJ_PATH), $(notdir $(SRC:.c=.o)))
RM = rm -rf
$(OBJ_PATH)%.o: %.c
mkdir -p $(OBJ_PATH)
$(CC) $(CF) $(CFI) -c $< -o $#
$(NAME): $(OBJ)
make -C $(LIBFT_PATH) $(LIBFT)
$(CC) -g $(CF) -I $(INCLUDE) -o $(NAME) $(OBJ) -L $(LIBFT_PATH) -lft $(CREADLINE)
#echo "$(GREEN)$(NAME) created$(DEFAULT)"
all: $(NAME)
re: fclean all
clean:
make -C $(LIBFT_PATH) clean
$(RM) $(OBJ) $(OBJDIR)
#echo "$(YELLOW)object files deleted$(DEFAULT)"
fclean: clean
make -C $(LIBFT_PATH) fclean
$(RM) $(NAME)
#echo "$(RED)all deleted$(DEFAULT)"
install:
sudo apt-get install -y libreadline-dev valgrind
leak:
valgrind --suppressions=readline.supp --leak-check=full --track-origins=yes --show-leak-kinds=all ./$(NAME)
.PHONY: all clean fclean re bonus rebonus
#COLORS
RED = \033[1;31m
GREEN = \033[1;32m
YELLOW = \033[1;33m
DEFAULT = \033[0m`
Could anyone give an idea how to fix this?
A makefile contains two different types of text. There is makefile syntax, which is parsed by make. This is all the text in the makefile which is not indented with a TAB character. The syntax for this is described in the make documentation.
Then there is shell script syntax, which is not parsed by make. This is all the text that is indented by a TAB character. This text is not handled by make; instead make invokes a shell program to run these commands.
On a POSIX-based system such as GNU/Linux, MacOS, the *BSD systems, etc., they all use the same basic shell and set of utilities as defined by the POSIX standard. mkdir -p is a POSIX command plus option, directories are separated by forward-slash, and everything works nicely.
The make utility was created in the 1970's on a UNIX (POSIX-base) system and appears on every POSIX systems since.
On Windows, the world is very different. The tools (like mkdir) work differently on Windows, the paths are constructed differently ("drive letters", backslashes for separators, etc.), there are multiple shells (cmd.exe and PowerShell) and none of these shells work the same way as the POSIX standard.
All this to say, it's very tricky to write a single makefile that will work on both POSIX and Windows environments because they are very very different.
Your options are (a) install a POSIX environment on you Windows system and run make from there using all POSIX tools, or (b) use make variables to hold different commands based on the OS type you're using, so that your rules can be portable.
In your makefile, it is invoking the Windows cmd.exe shell, which is trying to run the Windows mkdir program, which does not understand the POSIX -p option. So you get this error.
First: Make expects as first character of a command a real tab ('\t'), not blanks (' ').
Enable your editor to show you white space character and check this.
You can put blanks or more tabs after the initial tab, if you need to.
Anyway, this seems not to be the core issue.
Your Makefile is using command lines for another shell than CMD. You need to adjust them to the other syntax.
For example, unixoid operating systems know the command "mkdir", which allows the option "-p" to create all directories on the way to the final one, if they do not exist. This is for safety reasons. CMD's version of "mkdir" does not know this option, beside the fact that it does not accept forward-slashes as path separators.
We are trying to compile this by following instructions in the readme. I must say that we are not specialists with C at all, we are students of a web development bootcamp and trying to do our last project.
It's a command line tool to calculate ephemerides of multiple celestial bodies, and as you can read in the setup in the readme file, it need to download certain data from the internet, and then compile.
All is done through the setup.sh script.
So, we have tried:
In Windows 10 ubuntu WSL terminal
If we type $./setup or $./prettymake, after download the data, gives the error:
$mkfifo: cannot create fifo 'stderr': Operation not supported
$mkdir -p obj obj/argparse obj/coreUtils obj/ephemCalc obj/listTools obj/mathsTools obj/settings
cc -Wall -Wno-format-truncation -Wno-unknown-pragmas -g -c -I /mnt/d/reboot/ephemeris-compute-de430/src -O3 -D DEBUG=0 -D MEMDEBUG1=0 -D MEMDEBUG2=0 -fopenmp -D DCFVERSION=\"2.0\" -D DATE=\"09/06/2019\" -D PATHLINK=\"/\" -D SRCDIR=\"/mnt/d/reboot/ephemeris-compute-de430/src/\" src/ephemCalc/constellations.c -o obj/ephemCalc/constellations.o
If we do it with $sudo ./setup, the error printed is:
$mkfifo: cannot create fifo 'stderr': Operation not supported
$cat: stderr: No such file or directory
$mkdir -p obj obj/argparse obj/coreUtils obj/ephemCalc obj/listTools obj/mathsTools obj/settings
cc -Wall -Wno-format-truncation -Wno-unknown-pragmas -g -c -I /mnt/d/reboot/ephemeris-compute-de430/src -O3 -D DEBUG=0 -D MEMDEBUG1=0 -D MEMDEBUG2=0 -fopenmp -D DCFVERSION=\"2.0\" -D DATE=\"09/06/2019\" -D PATHLINK=\"/\" -D SRCDIR=\"/mnt/d/reboot/ephemeris-compute-de430/src/\" src/ephemCalc/constellations.c -o obj/ephemCalc/constellations.o
In macOS terminal
If we type $./prettymake, gives the error:
$mkdir -p obj obj/argparse obj/coreUtils obj/ephemCalc obj/listTools obj/mathsTools obj/settings
cc -Wall -Wno-format-truncation -Wno-unknown-pragmas -g -c -I /Users/rominaelorrietalopez/Documents/Descargas2/ephemeris-compute-de430-master/src -O3 -D DEBUG=0 -D MEMDEBUG1=0 -D MEMDEBUG2=0 -fopenmp -D DCFVERSION=\"2.0\" -D DATE=\"09/06/2019\" -D PATHLINK=\"/\" -D SRCDIR=\"/Users/rominaelorrietalopez/Documents/Descargas2/ephemeris-compute-de430-master/src/\" src/argparse/argparse.c -o obj/argparse/argparse.o
$clang: error: unsupported option '-fopenmp'
$make: *** [obj/argparse/argparse.o] Error 1
We have tried certain things to no avail, like granting permissions and what not, but have no idea what to do next.
It seems that it have something to do with the prettymake file:
mkfifo stderr
cat stderr | sed 's/\(.*\)/\1/' &
make $# 2>stderr | sed 's/\(.*\)/\1/'
rm stderr
It's like its trying to create a pipe to save the errors of the compilation but somehow it fails.
Also possibly worth of mention, it have a Makefile associated.
Since the github project does not have Issues, we've contacted the creator via email, but well, we thought maybe someone could help us here too.
Any kind of help would be honestly appreciated, thanks.
A comment from the OP invites me to answer; here it is.
The prettymake script creates a named fifo in order to receive the messages produced by make on its standard error.
A background process (cat) consumes the data from this fifo and sends them to a sed command (see right after) in order to transform these data before writing to standard output.
(note that cat is useless here since sed could have directly read from the named fifo thanks to <)
However, the two sed commands as shown in the question don't do anything since they just capture each line of text (\(.*\)) and repeat them unchanged (\1), thus they could have been omitted.
In this case, the script could just contain make $# 2>&1, it would have produced the same effect.
On a system where creating the named fifo is problematic (old version of WSL apparently), this change in the script should produce the same effect as expected.
Looking at the link provided in the question, we can see that the original prettymake script actually contains transformations in the sed commands in order to display standard output and standard error of the make command with different colours.
I am following the book "Learn C the Hard Way" and I have reached the section where you compile the code using makefiles. I keep getting errors that the file was not found when running make clean.
Here is the error followed by the make file and the directory
C:\Users\Me\Desktop\C Code\HARD WAY\Dust off that compiler>make clean
rm -f main.c
process_begin: CreateProcess(NULL, rm -f main.c, ...) failed.
make (e=2): The system cannot find the file specified.
make: *** [makefile:7: clean] Error 2
EXECUTABLE=main.exe
CC="C:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin\gcc.exe"
CFLAGS=-Wall -g
clean:
rm -f main
directories
EDIT: I removed the .c from the "rm -f main" as it seems to be a small error but has made no difference to the end result
EDIT 2: I worked out that the make file is issuing linux commands which is why it wasn't working, replacing rm with del fixes the issue
I worked out that the make file is issuing linux commands which is why it wasn't working, replacing rm with del and removing -f fixes the issue
my makefile is:
CFLAGS=-g
all: mcast_client mcast_server
mcast_client: mcast_client.o $(ARG1)
mcast_server: mcast_server.o
clean:
rm -f mcast_client mcast_server mcast_client.o mcast_server.o
in the command window i type,
$ make ARG1=hello, world!
is this correct?
Use:
$ make ARG1="hello, world!"
Your invocation make ARG1="hello, world!" is almost correct (but the quotes are needed for the shell), but your Makefile is not.
A more realistic approach would be to pass the message as a preprocessor macro.
Assume hello.c contains (in the middle of some C function)
printf("%s\n", MESSAGE);
Then you could have a Makefile rule like
hello.o: hello.c
$(COMPILE.c) -DMESSAGE=\"$(MSG)\" $< -o $#
The quotes are backslashed because the preprocessor macro MESSAGE should have quotes.
And finally you could invoke
make "MSG=hello friend"
Beware that this won't work if MSG contains quotes " or backslashes \ .... In the command above the quotes are interpreted by the shell...
Notice that you are supposed to invoke make with the same command every time... (since hello.o won't be rebuilt if the MSG has changed).
BTW, take the habit of always compiling with -Wall so
CFLAGS= -Wall -g
and look at predefined rules of make given by make -p
Consider using remake (notably invoked with -x) to debug tricky or complex Makefile-s.
I have a bunch of C source files named sequentially (say f1.c, f2.c, f3.c etc).
In my Makefile I have a clean: definition which used to look like this:
rm -f f1
rm -rf f1.dSYM
rm -f f2
rm -rf f2.dSYM
# etc
So I wanted to replace that with a regex, and this works great if I input it directly into the command line:
ls | grep -P ^f[0-9]+(|\.dSYM)$ | xargs rm -rf
However, if I then put that command in my clean definition, when I run make clean, I get this:
$ make clean
ls | grep -P ^f[0-9]+(|\.dSYM)| xargs rm -rf
/bin/sh: -c: line 0: syntax error near unexpected token `('
/bin/sh: -c: line 0: `ls | grep -P ^ex[0-9]+(|\.dSYM)| xargs rm -rf'
make: *** [clean] Error 2
I guess there are some special characters in my regex that are causing a syntax error... I've tried quoting and escaping stuff but nothing's really helping, does anyone know how I could get this working inside my Makefile?
Yet another solution, using $(wildcard) to find the C sources and pattern substitution to get the derived file names:
SOURCES := $(wildcard f[0-9]*.c)
clean :
rm -f $(SOURCES:.c=)
rm -rf $(SOURCES:.c=.dSYM)
ls | grep
is a useless use of ls. http://porkmail.org/era/unix/award.html#ls
rm -rf f[0-9] f[0-9]*[0-9] f[0-9]*.dSYM
In clear, use globing. See http://mywiki.wooledge.org/glob.
Direct solution: quote your regex. Better solution: globs, brace expansion, and/or find ... -delete.
rm -rf f{1,2}{,.dSYM}
rm -rf f? f?.dSYM
ffind . -regex '.*/f[0-9]' -o -regex '.*/f[0-9].dSYM' -delete
Your command line shell is probably different from the shell make uses. I guess you use /bin/bash at the command line, but make uses /bin/sh by default. You can change it by prepending
SHELL=/bin/bash
to the Makefile.
Adding quotes around vertical bars and parentheses is always safer than use them unquoted. Also note that the dollar sign must be doubled in a Makefile not to be treated as a special character.