What is an efficient workflow with C? - Makefile + bash script - c

I'm working on one of my first projects that will span more than one C file. For my first couple practice programs, I just wrote my code in main.c and compiled using gcc main.c -o main. This worked for me as I was learning.
Now, I'm working on a much bigger project on my own. I want to continue doing compilation on my own (or at least setting it up manually) so I can understand the process. After reading a bit, I decided to make a Makefile.
Note: I'm also using GTK+, so I had to look up how to add that into the compile command.
This is what it looks like after a bit of research:
main:
gcc -Wall -g main.c -o main `pkg-config --cflags --libs gtk+-2.0`
At first, I was just running "make". Then I was having some problems getting the error "main is up to date" even though I had changed the file.
So I wrote a script:
#!/bin/bash
rm main
make
./main
So I make changes, and then I run this script.
Is this a good/normal system? I want to have scalable system, since my project will grow. I assume I can keep that script and just add dependencies to the makefile and change the main compile command in the makefile. Am I correct?
Thanks in advance.
EDIT:
Thanks for the feedback about how to fix my Makefile.
So is the typical compilation process 1) type make then 2) ./main regardless of how the project is setup or its size (assuming you've written a proper makefile)?

You need to tell make that main depends on main.c. That way every time you make changes to main.c and then run make, main is regenerated. To delete main you can have a phony target called clean as:
main:main.c
gcc -Wall -g main.c -o main `pkg-config --cflags --libs gtk+-2.0`
.PHONY: clean
clean:
rm -f main
Now to delete main you can do : make clean
If you get make: main is up to date. It means you've not modified main.c and hence there is not need for regenerating main. But if you have to force regenerating main even when the dependencies have not been updated you can also use the -B option of make as suggested by Sjoerd in other answer.

Use make -B or make --always-make to compile even though the target is up to date
Append filenames after the colon to check whether these are updated.
Example:
a: a.c
gcc -o a a.c
a would only be built if a.c is newer than a.

I find command-line make to be quite sufficient for my needs, but writing Makefiles by hand becomes quite a chore. As your project grows in complexity, you'll find managing the dependencies by hand to become more and more annoying. What I suggest you do is learn how to do at least one of the following:
Write a dependency-tracking Makefile by calling e.g., gcc -M.
Learn to use a Makefile generator such as automake or CMake. I personally prefer automake because it is more mature (and doesn't do stupid things like try to put semicolon-separated lists on a command line).

Related

Running c program from command line in one step?

I just got started writing some C programs.
To start with I was just running them through VS code. Nice and easy, I just had to press a button and bam, there it was.
But now I need to pass files as arguments to my program, which creates the need of running it from the command line.
The way I do it now, is using this two step process, (which I think is just the basic way of doing it):
ask#Garsy:~/Notes/ethHack/crpytifiles$ gcc test.c -o test
and then running the file:
ask#Garsy:~/Notes/ethHack/crpytifiles$ ./test
This is a bit tedious in the long run. Is there any way I could do this process in one step?
And perhaps also without creating the executable?
It would be really cool if I could just run it as you normally would with a python or java file, one command, and the thing runs.
You could do that with a makefile. More about GNU Make here.
all:
gcc test.c -o test
./test
The file should be called Makefile or makefile (it can have different names,just keeping it simple), and you can run it by executing:
make
Assuming you have GNU Make installed and test.c is located in the same directory with makefile.
This is a bit tedious in the long run. Is there any way I could do this process in one step?
Yes. You could create a shell function (or an alias if your shell supports alias arguments, which bash does not), e.g.:
ccr() { gcc "$1" -o x.$$ && ./x.$$; rm -f x.$$ }
$ ccr hello.c
Hello, world!
$
which will compile the script, run it if compilation succeeded, then remove the compiled binary.
And perhaps also without creating the execuable?
No (well, not easily). Executing binaries is offloaded to the exec*() function family, and the operations performed are complex and, I suspect, incompatible with stdin operations. So you cannot send the executable to a pipe and execute it from the pipe.
What you can do is use a C interpreter, albeit it is not exactly the same thing.
I am wondering that nobody is issuing the general comparison between an IDE and shell. So yes IDE may give you some comfort. But you will be happy if you learnt the fundamentals of linking & Co from scratch - otherwise the configuration of the
IDE can get pretty challenging, when you start stuff that does not work out of the box.
The rest are helpful tips to increase the efficiency on the shell - like make or
other automation builders. Shell editors provide additional tools and plugins to increase your workflow - eg with vim as an shell editor (and some plugins) you come pretty close to an IDE. This includes syntax highlight,
code check, compile and run of the program, etc... just my 2 cents
As #alex01011 correctly stated, what you need is a Makefile, and his solution should work. What I want to suggest here is a better Makefile.
First make already know how to use build test from test.c in the simple case. It will add parameters to the preprocessor, compilation and linker steps from Makefile variables, so it is better to use the built-in command for better fleksibility.
# Tell make that `all` and `run` is technically not files that will be built
.PHONY : all run
# These flags are passed to the compiler, we always want to compile with
# warnings when developing
CFLAGS= -Wall
# `all` is the first rule, so that is the one that will be build not
# specifying anything on the command line
# `all` also depends on `test` so that will be built from `test.c` calling
# `make` or `make all`
all: test
# `make run` will run your command. `run` depends on `all` to make sure the
# program exist before calling `./test`
# Note the the indent must be made with a tab and not spaces
run: all
./test
If your program is composed of more files, things get a lit more complicated, but still easily manageable:
# Example of a Makefile for a project that is composed of the files
# test.c foo.c, bar.c, foo.h and bar.h
# The main-function is in test.c, and the generated program will be
# called `test`
#
.PHONY: all run
CFLAGS= -Wall
all: test
# foo.c includes foo.h therefore foo.o depends on foo.h in addition to foo.c
foo.o: foo.h
# bar.c includes bar.h therefore foo.o depends on bar.h in addition to bar.c
bar.o: bar.h
# test.c includes both foo.h and bar.h
test.o: foo.h bar.h
# test should be linked with foo.o and bar.o in addition to test.o
test: foo.o bar.o
run: all
./test
Now typing make run will automatically build and link test, if needed, and the run ./test if there was no errors.
Other variables you may set in addition to CFLAGS are CC, CPPFLAGS, LDFLAGS, LOADLIBES and LDLIBS.
Often you also want to have a clean targets in your Makefile for typing make clean to remove generated files. See info make for more details.

How exactly "make all" works?

So I am studying makefiles usage. Let's say I 've many functions fun_i.c where i is in [1,100]. I have wrote them all in seperate .c files in order to make faster compilation when I only change some of them. I need to create a Makefile. I was thinking something like that:
all : program
program: fun1.o fun2.o ........ fun100.o main.o
gcc -o program fun1.o fun2.o ..... fun100.o
fun1.o: fun1.c
gcc -c fun1.c
.
.
.
fun100.o: fun100.c
gcc -c fun100.c
main.o : main.c
gcc -c main.c
I 've read many articles online talking about how the target "all" makes sure that everytime I change something in a function and I call make it will update only what changes (so I'm going to avoid uneccessary recompilation).
However, I am not sure if I am using it right here. I checked my makefile ( with 2 functions though) and it worked fine. But I am not sure if that's the way to take advantage of all here.
I tried to remove it and just leave program and below.. there and it worked as fine.
What am I missing?
Could you may provide examples that all is critical?
I 've read many articles online talking about how the target "all" makes sure that everytime I change something in a function and I call make it will update only what changes
all does not mean anything. It's just a convention. You have to define the targets correctly to do what you want. If you want make all to recompile everything, you need to define it so that it does that.
so I'm going to avoid uneccessary recompilation
In practice, I'd say that this is a completely unnecessary concern, and it's a waste of time creating makefiles that does this. If you have a large project you should not create makefiles manually. You use a build system like cmake.
If the project is small and only contain a few files, then the compilation time is in general not an issue. For a small project, my goal with a makefile would be to not have to type the compiler flags all the time.
Here is a makefile that would work fine for most very small projects like school assignments:
CFLAGS=-Wall -Wextra -pedantic -std=c17 -fsanitize=address
LIBS=-lm -lpthread
program:
gcc $(CFLAGS) $(LIBS) *.c
clean:
rm -rf *.o *~
Your makefile is fine, both with and without all: program.
The word all has no special significance in makefiles, using is just a convention. In your makefile all has no recipe (i.e. no commands associated with it), so make all and make program are equivalent.
Note that if you don't specify a target when invoking Make (e.g. make as opposed to make program), the first target in the makefile is built.

Making a makefile for C program on linux

I have a C programming exercise, which I have written (and runs perfectly) in Visual Studio on Windows. I now have to make sure it runs OK on Linux as well, and need to create a makefile for it (it is part of the assignment). Here is my makefile:
all: genericdfs.a sudSolver
genericdfs.a: genericdfs.c genericdfs.h
gcc -Wvla -c genericdfs.c
ar rs genericdfs.a genericdfs.c
sudSolver.o: sudSolver.c sudTree.h genericdfs.h
gcc -Wvla -c sudSolver.c -lm
sudukutree.o: sudTree.c sudTree.h
gcc -c sudTree.c -lm
sudSolver: sudSolver.o sudTree.o genericdfs.a
gcc -Wvla sudSolver.o sudTree.o -L. -lgenericdfs -o sudukusolver -lm
clean:
rm -f sudSolver.o
rm -f sudTree.o
rm -f genericfs.o
OK so the main C file is sudSolver which has includes for sudTree.h and math.h (hence the -lm)
sudTree.c includes sudTree.h and genericdfs.h as well.
One of the requirements is to create a .a library which should be linked to the main C file at the linkage operation.
We were given next to nothing of an explanation as how to write these makefiles so all I wrote above was according to makefile tutorials I found online.
This makefile however doesn't work, there seems to be a problem with the linkage to the library as this is the error that is being returned:
cannot find cannot find -lgenericdfs
I tried shifting things around but nothing seems to work, another error which appeared when I put -L. genericfs.a in the linkage line:
genericdfs.a: error adding symbols: Archive has no index; run ranlib to add one
Could anyone please explain how I link to the .a library which was created? I suppose its not that complicated but for the life of me I cannot get it to work
Thank you to anyone who helps!
EDIT
I managed to make it work by changing the line
ar rs genericdfs.a genericdfs.c
into
ar rs libgenericdfs.a genericdfs.o
and updating final linkage line to libgenericdfs.a
But now there is a different problem. I included a couple rm -f commands to a clean: tag, but they don't delete the files written there when i run "make" from the terminal.
If I run "make clean" then everything gets removed. Do I need to add "clean" to the "all" tag at the top? I read that you should not do that
gcc is passed libraries by using
gcc -Lfull/path/to/library
Or if the library name starts with 'lib' and is on a library search path then you can use -l with lib and .a removed. For example with library called libtest.a .
gcc -ltest
There are a couple of special cases for well used libraries like maths -lm and zlib I think.

How to write a Makefile to compile a simple C program

Compile the following program
#include <stdio.h>
int main(void)
{
printf ("Hello from your first program!\n");
return 0;
}
a)-by using file of type Makefile
b)-the executable will be named Hello
"Please help to do an exercise. I know how to do it in CodeBlocks, but I don't know what Makefile is and how to write it in Linux. I compiled it using command "gcc filename.c" and subsequently "./a.out" but I still don't understand what the Makefile is. Is it a sort of shell script, an instruction? How would a Makefile for this task exactly look? Thanks in advance :) "
This is your simple make file for hello program.
CC = gcc
CFLAGS = -g
RM = rm -f
default: all
all: Hello
Hello: Hello.c
$(CC) $(CFLAGS) -o Hello Hello.c
clean veryclean:
$(RM) Hello
Suppose you have two makefiles in one directory named makefile.m1 and makefile.m2 and if you want build both make file then please use following commands
make -f makefile.m1
make -f makefile.m2
or use single Makefile that contains:
m1:
make -f makefile.m1
m2:
make -f makefile.m2
and use make m1 or make m2
Now lets clear your doubt about name of make file must not require Makefile
You can name makefile whatever you want. suppose i would like to give name myfirstmakefile.mk. To use it later you need to tell make what makefile you want. Use -f option for this:
make -f myfirstmakefile.mk
And again extantion .mk is also not manadatory you can use whatever you want but never forgot to use -f option.
so may this help make sense to you.
A makefile is a recipe for the make utility how to create some file (called a target) from some other files (called dependencies) using a set of commands run by the shell. A makefile typically looks like this:
target: dependency [...]
command1
command2
Try running man make for details.
Now for your task, really there is no need for a Makefile, since make has built-in rules that know how to compile a simple program. All you need to do is place your C source in a file named after the executable name (Hello) and with a .c extension, i.e. Hello.c.
Then a simple
$ make Hello
cc Hello.c -o Hello
does everything. If you want to use gcc instead of cc, you can run
$ rm Hello
$ make CC=gcc Hello
gcc Hello.c -o Hello
If you tell your instructor/teacher/prof that an empty makefile is all you need since you know the built-in rules do the right thing, you'll get some extra credit and maybe your instructor has learnt something new :-) If you are asked for a reference, you could quote the relevant parts of the make manual, or, do it like a pro, quote from the POSIX Standard for the make utility, section Default Rules.
before going for makefile you have to know what's it and why we need it
What is Makefile?
Makefile is a script written in a certain prescribed syntax which helps to build the target output (normally, one or more executables) from source files by compilation and linking. In simple words, makefile will compile your source code in simple & fast way.
Why we need Makefile?
=> Large projects can contain multiple source files which are dependent in one another or arranged in hierarchical manner for example, in order to compile file A, you have to first compile B; in order to compile B, you have to first compile C; and so on.
=> Make is a solution to these problems. It can be used to compile whole project in well arranged manner and generate your target according to your make rule(which we will discuss later) by entering single command that is make.
=> An important feature is that when a project is recompiled after a few changes, it will recompile only those files which are changed, and any other files that are dependent on it. This saves a lot of time.
=> For a large project, when a few changes are made to the source, manually recompiling the entire project each time is tedious, error-prone and time-consuming.
Here is nice link for it :How to write first makefile
A makefile is a recipe for computers with instructions how to perform certain tasks and with dependencies between those tasks.
In the simple form, it looks like so:
a.out: filename.c
gcc filename.c
Read: "To build a.out from filename.c, run the command gcc filename.c. If a.out is newer than filename.c, then don't do anything"
Note: The first character in the gcc line must be a tab.

compile .c with gcc

I'm trying to compile a program that have main.c and a lot of .c and .h files .
Is there any way to compile and link without passing all .c file in the gcc command
like
gcc main.c file.c file2.c -o main
Your shell can expand wildcards. So you can:
gcc *.c -o main
Of course, you'll have to make sure that you don't have any extra *.c files in the directory that you don't actually want compiled. A better option is to use a build system such as Make or SCons.
'make' is the tool for building C apps. Below is the hello world version.
$ cat main.c
#include <stdio.h>
int main (char *argv[], int argc) {
printf("Hello World\n");
return 0;
}
$ make main
cc main.c -o main
$ ./main
Hello World
Edited in deference to Shahbaz comment:
The original question was trying to simplify the command-line for gcc. The right direction for the programmer is to learn about make. Since there is a bit of a learning curve with make, I wanted to offer simple stepping stone which does something useful. By getting started in this way, you don't need a make file. Simply type 'make programname' as shown above. Make uses its default rules and associated varabiles. $(CC) -c $(CFLAGS) $(CPPFLAGS) The astute programmer can build on this by setting well-know variables.
From here one can tinker with a makefile. In the spirit of stepping stones, consider this trival makefile
$ cat makefile
SRCS = main.c
OBJ = ${SRCS:.c=.o}
CC=gcc
main: ${OBJ}
$ make
gcc main.o -o main
By setting the well-known make variable CC to control which compiler is used. OBJ is computed from the list of source files. The trival target starts one on the road to rules.
Anyway, my hope is this post and other answers get the original questioner on there way.
Regards,
-jk
If you're asking about the command-line, you can just use wildcards to specify all the .c files as #Man of One Way suggested. But in general, C applications of any-but-trivial-size are built using "makefiles" which are extremely helpful.
You might want to read a tutorial such as http://www.cs.umd.edu/class/fall2002/cmsc214/Tutorial/makefile.html
gcc -o main -I. `find . -name "*\.c"`
This way you could have the .c and .h files in subfolders if you wish.
However, this is not a good way of doing it. A better way would be to create a Makefile
As already said, make is the best way to go
Learn just what you need at every point
Besides, it is important to also use some flags that will help you out while coding:
-Wall -> sets all warning flags
-g, -ggdb -> generates debug code
-ansi, -std=c99
A really good book on how to use make is,
http://shop.oreilly.com/product/9780937175903.do

Resources