Including input statements and conditions in the make file - c

I am trying to create a makefile, for the first time. I went through some tutorials and I managed to create one, but I am having trouble with a couple of things. Below are the details.
Below are the files in the order of execution:
CSV_to_txt.c - no dependency on any other files.
I want to include CSV_files/Equilibrium_trajectories.csv, which is my input, in the make file. Further, I run the command tac Chemical_Equilibrium.txt in the terminal. Can I include this in the make file as well?
fluid_profile.c - depends on pdfutil.h and beta_util.h.
I have the same problem of reading the inputs, for ex:
Enter the number of points
1000 --> to be included in the make file.
This file creates a text file called fluid_points.txt. What I want to include in the makefile is if this file already exists don't execute the command gcc fluid_points.c -o fluid_points.o -lm.
Structure of the make file:
all:
gcc CSV_to_txt.c -o CSV_to_txt.o -lm
./CSV_to_txt.o
#Include the file path and name when asked for it
#ubuntu terminal command --> tac filename.txt > filename_changed.txt
gcc fluid_profile.c -o fluid_profile.o -lm
./fluid_profile.o
#Enter the number of points when prompted to do so
#If fluid_points.txt file is already existing don't execute the above command, instead execute the below one
gcc blah.c -o blah.o -lm
./blah.o
clean:
$(RM) *.o *~
Any sort of help or even a link to a tutorial would be helpful.

A suggested makefile:
run:
.PHONY: run
CSV_to_txt: CSV_to_txt.c
gcc CSV_to_txt.c -o CSV_to_txt -lm
fluid_profile: fluid_profile.c
gcc fluid_profile.c -o fluid_profile -lm
blah: blah.c
gcc blah -o blah.c -lm
run: CSV_to_txt fluid_profile blah
echo "CSV_files/Equilibrium_trajectories.csv" | ./CSV_to_txt.o
tac Chemical_Equilibrium.txt
echo "1000" | ./fluid_profile.o
./blah.o
clean:
$(RM) *.o *~
So, a break down -- first line, predeclare target run, such that it becomes the default target (if you do make, it will run the first target ). Declare this as a phony target (This means there's no actual file called run being produced. You can look up .PHONY for more details)
Then create some rules to generate the executables. Each executable has its own rule to generate it. Typically you would use automatic variables for these like $# and $<, but I wanted to keep it simple for now.
Then the rule for run. This is dependent on the executables (so executables will finish building before this rule runs).
Then, to pass the filename into the executable, you can simply echo the filename, and then pipe that into the executable.

You have a common newbie error... this is to think that a source file depends on other source files (a .c file depends on some .h files) This is an error and probably the cause you are not getting your result.
The objective of a Makefile is to describe file dependencies in order to do the minimum set of commands to build the final target you specify. For this you need to think that a target is something you are goint to create.
Is a source .c file something you create during the build proces? Not, so it cannot be a target of a rule. The target, indeed is the result of the compilation. The source file doesn't depend on a header file... it just includes it to make the compilation of the .o target (this is, actually the target).
Let's say you have a program hello.c that includes modA.h and modB.h. (and even modB.h includes modB2.h) If you modify any of them, you need to recompile hello.c, so your rule will be:
# (1)
hello.o: hello.c modA.h modB.h modB2.h
cc -c hello.c # (2) see below.
(1) a rule line starts at column 1 and has a left hand side (the target file) and a list of sources (dependencies). Each time make sees that the target doesn't exist or has a last change date earlier than the change dates of any of the dependencies, the command lines below are executed, one after the other.
(2) a command rule starts with a <tab> char in the first column of the line. It represents a command (or a list of commands, each in it's command line) that are required to generate the target file from the sources.
a line starting with # is a comment line (also valid to start in the middle of a rule or a command line)
There is anothe type of line (a macro definition) but you need to learn first how to create dependencies and get used to them, before starting learning how to create macros. Read the make(1) doc first.
you see that we only compile hello.c, but we have to do it every time we change any of the other files above. There are two modules, modA.o and modB.o, each of them with their .c file and the includes needed in hello.c. So:
modA.o: modA.c modA.h
cc -c modA.c
modB.o: modB.c modB.h modB2.h
cc -c modB.c
so when we change any of modA.c or modA.h then modA.o will be created. And as modB.h we said above that included modB2.h, then if we modify it, it should be compiled.
Now the dependency of the program to be linked: As the program is compiled, it has just three modules: hello.o, modA.o and modB.o. To create hello all these three modules must be given to the linker.... so the Makefile needs also:
hello: hello.o modA.o modB.o
cc -o hello hello.o modA.o modB.o
and so, the complete Makefile is:
# this rule is put first to become the default target.
# the default target is the final program.
hello: hello.o modA.o modB.o
cc -o hello hello.o modA.o modB.o
# this rule is for the hello.o target only.
hello.o: hello.c modA.h modB.h modB2.h
cc -c hello.c
# this rule is for modA.o
modA.o: modA.c modA.h
cc -c modA.c
# and this one for modB.o
modB.o: modB.c modB.h modB2.h
cc -c modB.c
and with this Makefile you'll enjoy, because you can touch any file, but the compiler will compile only the correct dependencies to generate the final executable program.
Make has a lot of more functionality, but that requires you to know at least the most basic of it. Once after you have succeeded on the creation of the correct dependencies, you can start to study the other facilities of make, that are only there to abbreviate/avoid rewritting the same thing several times. But read at least the make manual page.

Related

Printing a sentence, only if a Makefile rule is executed

I am customizing my Makefile for a school project.
I would like to print the following sentence but only when the .c files from my SRCS_DIR have been actually compiled.
All the .c files have been compiled successfully !
If I move the printf that you see in line 3 of the code block below to the last line, it prints the message after each .c file being compiled...
Thus, I created a COMPILED variable which I set to 0 at the beginning of my Makefile, and then I change its value to 1 during compilation (line 11 in the code block below). I tried to use the ifeq condition (line 2 in the code block below), but the sentence does not print when I do that.
$(NAME): $(LIBFT_AR) $(OBJS)
ifeq ($(COMPILED), 1)
printf "$(GREEN)> All the .c files have been compiled successfully !$(END)\n"
endif
printf "$(BLUE)> Creating the executable file :$(END) $#\n"
$(CC) $(OBJS) $(LIBFT_AR) -lreadline -o $(NAME)
printf "$(GREEN)> Executable file has been created successfully !$(END)\n"
$(OBJS_DIR):
mkdir -p $(addprefix $(OBJS_DIR)/, $(SUBDIRS_LST))
$(OBJS_DIR)/%.o: $(SRCS_DIR)/%.c $(INCS) Makefile | $(OBJS_DIR)
override COMPILED=1
$(CC) $(CFLAGS) -I $(INCS_DIR) -c $< -o $#
printf "$(BLUE)> Compiling :$(END) $<\n"
Do you have any explanation regarding this issue and/or a solution that could help me to solve the problem ?
Thank you !
Makefiles are not scripting languages. Make doesn't read the makefile and run each rule as it's read. Make will (1) parse the entire makefile (and any included files) and build an internal graph of all the prerequisites, than (2) run recipes for targets that are outdated. Content that is NOT IN A RECIPE is always evaluated during the first step. Content that IS IN A RECIPE is always evaluated during the second step.
Lines that are not indented with TABs, are not in recipes (and so are evaluated during the first step). Lines that are indented with TABs, are in recipes (and so are evaluated--which means, given to the shell to execute--during the second step).
Maybe you can now see why your attempts cannot work: the if-statements and variable assignment of COMPILED are always evaluated, during the first step, before make has decided whether or not any targets should be built.
I'm not really sure I understand your goal. If the recipe of your executable is being invoked then it means that all your source files have been compiled: that's what a makefile does. Maybe you are trying to make a distinction between a build where at least one source file was compiled, and a build where no source files had to be recompiled but the target (the executable) was out of date?
If that's what you want the simple way to solve your problem is with automatic variables; for example the $? automatic variable expands to the list of prerequisites that were out of date. You can do something like:
$(NAME): $(LIBFT_AR) $(OBJS)
test -z '$(filter %.o,$?)' || printf "$(GREEN)> All the .c files have been compiled successfully !$(END)\n"
printf "$(BLUE)> Creating the executable file :$(END) $#\n"
$(CC) $(OBJS) $(LIBFT_AR) -lreadline -o $(NAME)
printf "$(GREEN)> Executable file has been created successfully !$(END)\n"
The $(filter ...) function will expand to the list of .o files in the $? variable; if that's empty then no .o files were rebuilt.

C Makefile compilation error - "linker input file unused because linking not done"

I'm having a problem with a C Makefile.
This is the code for the Makefile in bash:
CC=gcc
CFLAGS=-g -Wall
CCLINK=$(CC)
OBJS=flight.o runway.o airport.o main.o
RM=rm -f
# Creating the executable (airport)
airport: $(OBJS)
$(CCLINK) -o airport $(OBJS)
# Creating object files using default rules
main.o: main.c airport.h ex2.h flight.h runway.h
airport.o: airport.c airport.h ex2.h flight.h runway.h
runway.o: runway.c runway.h ex2.h flight.h
flight.o: flight.c flight.h ex2.h
# Cleaning old files before new make
clean:
$(RM) airport *.o *.bak *~ "#"* core
When I make the file, it says that:
make: `airport` is up to date.
After that - I can call "airport" in bash and it lets me enter some inputs the way I want it to be.
BUT- when I'm trying to check if "airport" is compiled by:
gcc -g -Wall -c airport
I get an error says that:
gcc: airport: linker input file unused because linking not done
Does someone know what could be the problem?
Thanks!
Gavriel.
The aim of Makefile is to avoid recompiling a file if its source is unchanged; when it happens, make says that the file is up to date.
This might be annoying if you want to check again the warnings. Then, simply call make to recompile everything, by typing
make clean ; make
Another goal of Makefile is to avoid typing the gcc commands by yourself, prone to errors. For instance, at the end of your question, you ask to make an object file from an executable (option -c), which is wrong. The good way to make an object file is to call make :
make airport.o
Finally, to produce the executable, you can either type
make airport
or, since airport: is the first target, type
make

Why is my very simple makefile not working

When I say very simple I mean it. I have a main.c and a header file called input_error.h.
main.o : main.c input_error.h
gcc -c main.c
When I run the command "make" gcc -c main.c is executed but it's not updating any changes I make to my main.c file. When I manually type in "gcc main.c" it works fine.
EDIT: It seems like I need to add another rule but I'm not sure what that entails
At the moment your makefile only builds the .o file. You can build your binary in 2 ways. Note that make requires the indentation in the targets statements to be a tab and not 4 spaces, as it may have been converted to by the browser.
build .o separately then link binary. Note that using the -c switch causes gcc to build only the object file.
main: main.o
gcc main.o -o main
main.o : main.c input_error.h
gcc -c main.c -o main.o
build in one step
main: main.c input_error.h
gcc main.c -o main
You can also avoid repetition in your makefile by using special variables to denote the target ($#), the first dependency ($<) and all (#^) the dependencies.
e.g. one of the above lines could become
main.o : main.c input_error.h
gcc -c $< -o $#
Which seems a bit cryptic at first but you get used it. The implicit rules in #kaylums answer will also help to cut down on typing.
The Makefile you have only has a single rule to compile the .o file. That is, it does not have any rule to link the final executable.
make has implicit rules for building many common targets. So your Makefile could be as simple as the following:
all: main
main.o : input_error.h
For further explanation:
all: main: Since this is the first target it is the one that will be built by default if no explicit target is provided to the make command line. It depends on a single target main.
There is no explicit rule for main but make has an implicit rule which will build it from main.c.
main.o : input_error.h: Tells make that main.o needs to be rebuilt if input_error.h changes. There is no need to put main.c here as make has that implicit knowledge. There is also no need for an explicit command as make also has that implicit.

Issues with makefile producing fatal error "Don't know how to make target" using C source files

I am having an issue with this makefile giving the fatal error: "Don't know how to make target calc.o". The naming is correct along with being in the working directory, and the other issue is that when I switch the order of the object files for default target file it will say it does not know how to make that target either, so the order does not seem to change anything or be one specific file. I am running the make command on a sun sparc unix server as well. The makefile is pasted below:
#The following rule tells make about possible suffixes
#(extensions) of file names.
.SUFFIXES: .c .o
#The following definition of CC ensures that
#gcc will be used to compile the C source files.
CC = gcc
#The following definition of CFLAGS ensures that
#the debugger can be used with the executable file (p1)
#created by running make.
CFLAGS = -g
#The following rule tells make how a ".o" file should
#be created from the corresponding ".c" file.
#Note that the "-c" option must be used here since we are
#compiling source files separately. (Note that the line
#following the ".c.o:" line begins with the "tab" character.)
.c.o:
$(CC) $(CFLAGS) -c $<
#Dependency rule for the default target and how the
#default target is to be created. (Note that the line
#following the dependency rule begins with the "tab"
#character.)
p2: main.o textToBin.o binToText.o calc.o
gcc main.o textToBin.o binToText.o calc.o -o p2
#Dependency rules for other targets. (We don't need to
#specify how these targets are created since we have already
#given a general rule for creating a ".o" file from the
#corresponding ".c" file.)
#NO HEADER FILES
#Target for removing unnecessary files.
clean:
rm -f *.o core

Makefile dependency being ignored

I created a simple makefile to understand the basic idea behind how they work and it's not performing the way I expected. Please see the following:
test: test.c
gcc -o test test.c
My understanding is this should only run when there have been changes to the test.c file. The problem is it runs every time regardless of whether or not there were changes.
I've noticed that this occurs when I use arbitrary target names. If I make the target name an actual file name such as "test.exe", it works correctly, but all the tutorials I've seen show dependencies working with arbitrary target names. Any idea as to why this is occurring?
Under Windows, Make automatically adds the suffix .exe to the generated program.
Since the name of the target is different from the result of the rule, Make tries to generate it again.
You should write your makefile like this:
EXE := test.exe
$(EXE): test.c
gcc -o $# $^
If you need portability and don't want to rewrite the rule, use this:
EXE := test
if ($(OS),Windows_NT)
EXE := $(EXE).exe
endif
$(EXE): test.c
gcc -o $# $^

Resources