Is it possible to configure automake to generate a Makefile which, in addition to building the .o files and linked binary, also has targets for %.s? I want to be able to review the compiler output in a text format without having to invoke binutils on the .o files.
Specifically, if I have main.c as a source file, I want to be able to run make main.s. The desired recipe would be the same as that for main.o, but using CC1 := $(CC) -S.
The question is a little XY.
You want to be able make the intermediate assembly file foo.s, where
the source file foo.c is one of the sources in an autotooled project, using
a makefile that is generated by the project's ./configure script. You
assume that to do this you must do something to the automake inputs -
the Makefile.ams? - that will cause ./configure to generate Makefiles
that include assembly targets *.s matching all object targets *.o.
Well you could, but then your project would not be a regular autotooled
project as usually distributed, and there is no need to make it irregular
to get what you want.
The GCC option -save-temps
exists to let developers see the intermediate files of compilation - the preprocessor
output, the assembly.
$ gcc -c -o foo.o foo.c
outputs foo.o
$ gcc -save-temps -c -o foo.o foo.c
outputs:
foo.o
foo.i # preprocessed source
foo.s # assembly
As I expect you know, GNU Make receives compiler options from the make-variable
CFLAGS, and automake respects this convention, independently of and in addition to any compiler
options prescribed by the project's autotooling. So, if you would otherwise generate
makefiles with:
$ ./configure ...
then, to add -save-temps to the C compiler options, generate makefiles instead
with:
$ ./configure CFLAGS=-save-temps ...
And if you are already using CFLAGS, e.g.
$ ./configure CFLAGS="-g -O0" ...
then append -save-temps:
$ ./configure CFLAGS="-g -O0 -save-temps" ...
Then,
$ make main.o
will make main.o, main.i and main.s up-to-date.
To disable -save-temps, of course, rerun ./configure, removing it from
the CFLAGS.
If the project involves C++ compilation, then CXXFLAGS affects the C++
compiler in the same way that CFLAGS affects the C compiler. Note that
the generated preprocessed C++ sources will be called *.ii, not *.i.
With -save-temps enabled, make clean will not delete the *.i and *.s
files. You may not care, since compilation will always clobber them. If you
do care, you may take advantage of automake's standard phony target clean-local,
which is supported to let an autotooling maintainer extend the behaviour of
clean. Add the following recipe to the Makefile.am of each source directory
in the project:
clean-local:
$(RM) *.i *.ii *.s
Then update the autotooling and regenerate Makefiles:
$ autoreconf
$ ./configure ...
While the COMPILE variable in the generated Makefile.in is technically an internal detail, and this solution relies on the compiler to understand -c -S, adding:
.c.s:
$(COMPILE) -c -S $<
to the Makefile.am has worked for as long as I've been using the autotools. It might also be convenient to add:
clean-local:
rm -f *.s
I find this useful in development to have a look at the assembly output for specific configure and CC, CFLAGS options.
The COMPILE variable will be defined as something like:
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
Similarly, for C++ source we have:
.cc.s:
$(CXXCOMPILE) -c -S $<
Related
I am currently learning C with the book "Learn C The Hard Way" and looked into reverse engineering as well. What I want to do is read through the compiler generated assembly for the C programs I have written. I know the option for gcc here is gcc -S -masm=intel file.c.
My question now is: can I automatically have gcc create the .s file and the .out file using the Makefile?
My previous Makefiles always looked like this:
CC=gcc
CLFAGS=-Wall -g
all: file
clean:
rm -f file
How can I extend my Makefile to make it work?
You could do something like the following:
CC=gcc
CFLAGS=-Wall -Werror -Wextra -O2 -g
SOURCE=file.c
all: binary assembly
binary: $(SOURCE)
$(CC) $(CFLAGS) $(SOURCE)
assembly: $(SOURCE)
$(CC) $(CFLAGS) -fverbose-asm -S -masm=intel $(SOURCE)
clean:
rm -f file.s a.out
And use it like this:
$ make # same as `make all`
$ make all # makes both executable (binary) and assembly
$ make binary # makes binary file only
$ make assembly # makes assembly only
It seems that you can execute other labels from other labels. However
how do they differ from just writing the desired filename like I did?
Remember that Makefiles consist of "rules" with the format:
target: dependencies
system command(s)
See more here.
The make program decides on what should be done based on how you invoke it and how you define your rules. For example, the first rule with target all depends on binary and assembly, so make checks those rules, and carries out the relevant steps (in this case executing gcc).
How does gcc differentiate between them?
The gcc program is a compiler, which is a completely separate program. It is merely invoked by make, just like rm is for the clean rule. It has no involvement in processing the Makefile itself, so it doesn't need to "differentiate" anything -- make does all the processing of the Makefile.
Also, the $() variables you use, do they only exist for gcc or could
you also define them globally in your operating system?
The variables have nothing to do with gcc. The make program parses the Makefile and performs the necessary substitutions before invoking the commands (e.g. gcc).
The variables could be used elsewhere in the Makefile too, for example with the rm command. They are not specific to any command in particular.
You can also make use of environment variables in a Makefile, as explained in this post.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I'm really desperate when trying to write a C makefile for windows for my school project. I already have my makefile written and used for ubuntu and it works perfectly like this:
all: clean install
install: dynarr.o broadcaster.o main.o
gcc -o freq dynarr.o broadcaster.o main.o
clean:
rm -f *.o
dynarr.o:
gcc -c dynarr.c
broadcaster.o:
gcc -c broadcaster.c
main.o:
gcc -c main.c
I've already tried just about 10 tutorials on various sites and it is getting on my nerve.
I need to compile dynarr.c broadcaster.c main.c in this particular order and the output should be freq.exe. Compiler on the school server is gcc for both versions(linux and windows).
Please help me.
Edit: For some reason our teacher's online validator required freq.exe for both windows and linux and he did not bother to mention it anywhere. Everything I had to edit was freq to freq.exe. Thanks for support.
On Windows, you expect the compiler to produce a file named freq.exe (or the test system does, anyway) but the command you issue tells GCC to emit a file named just freq, instead. The latter seems natural for Linux, so it is not surprising that your build works as expected there.
It is tricky to support different OS families with the same makefile, which is why utilities such as the Autotools and CMake were invented. Supposing that you only need a Makefile for Windows, this variation should suffice:
all: clean install
install: dynarr.o broadcaster.o main.o
gcc -o freq.exe dynarr.o broadcaster.o main.o
clean:
rm -f *.o
dynarr.o:
gcc -c dynarr.c
broadcaster.o:
gcc -c broadcaster.c
main.o:
gcc -c main.c
With that said, I have some suggestions for further improvement:
A rule for building a specific output file should name that file as its target.
Conversely, targets of rules that do not correspond to a file created by that rule should be declared "phony" by naming them as prerequisites of a target named .PHONY.
By convention, an "install" target copies built files from their build location to a permanent system location. Neither your install rule nor anything else in your makefile does that, so you probably don't need an "install" target.
It is perhaps intentional that your rule for the default target cleans before it builds, but that is abnormal, as it defeats one of the major reasons for using make in the first place: to avoid needless work.
It is good form and it protects against errors to use Make's automatic variables to avoid repeating yourself in rules.
There is some divergence of opinion, but I think the general consensus is that a clean target should clean up all built objects, not just intermediate ones.
Applying all those would yield a makefile such as this:
all: freq.exe
freq.exe: dynarr.o broadcaster.o main.o
gcc -o $# $^
clean:
rm -f freq.exe *.o
dynarr.o:
gcc -c dynarr.c
broadcaster.o:
gcc -c broadcaster.c
main.o:
gcc -c main.c
.PHONY: all clean
Personally, I would also suggest introducing variables for the executable target (which would make it easier to adapt the makefile between Windows and Linux / macOS) and for the required object files (which is typical, and which would allow the clean target to be more precise). You could also consider relying on Make's built-in rules for building .o files from .c files, as the explicit rules you're using don't do anything that the is not also done by the built-in one. The result might be:
PROG = freq.exe
OBJS = dynarr.o broadcaster.o main.o
all: $(PROG)
$(PROG): $(OBJS)
gcc -o $# $^
clean:
rm -f $(PROG) $(OBJS)
# Relies on the built-in rules for building object files from C sources
.PHONY: all clean
I created a file.h and a file.c how can I compile them on Ubuntu?
You only need to compile your .c file(s), not your .h file(s).
To compile file.c on Ubuntu, you can use GCC:
gcc file.c -o my_program
...or Clang:
clang file.c -o my_program
It is possible to precompile your header files, but you only need precompiled headers in particular cases. More information here.
If file.h is not in the same folder as file.c, you can use GCC or Clang's -I option.
Example if file.h is in the include/ folder:
gcc -I include/ file.c -o my_program
In file.c you still have this instruction, with only the filename:
#include "file.h"
You can also use a more generic approach by the usage of a makefile.
Here is a short example of such a file:
# Declaration of variables
CC = gcc
CC_FLAGS = -w -Werror -Wall
# File names
# "prgoram" will be the name of the output produced from the make process
EXEC = program
#Incorporates all the files with .c extension
SOURCES = $(wildcard *.c)
OBJECTS = $(SOURCES:.c=.o)
# Main target
$(EXEC): $(OBJECTS)
$(CC) $(OBJECTS) -o $(EXEC)
# To obtain object files
%.o: %.c
$(CC) -c $(CC_FLAGS) $< -o $#
# To remove generated files
clean:
rm -f $(EXEC) $(OBJECTS)
To use this utility just make sure that the file itself is within the directory containing your source files and its name is either "makefile" or "Makefile".
To compile the code simply run the following command from your working directory:
make program
This command will automatically link all the source files within your working directory into one executable file with the name of "program". To run the program itself just use the command:
./program
To clean your project and the created executable you can run the command:
make clean
The makefile is very powerful when dealing with larger projects that contain a larger number of source files. Here you can check for more guidance on how to use makefiles. This is also a very detailed tutorial on the topic.
Use following command to compile your program(For GCC Compiler):
gcc file.c -o file
No need to compile file.h file.
I was looking at the net-snmp code and I found something odd in the Makefile of the snmplib itself. The last couple of thousands of lines are nothing but rules in this form:
./dir_utils.lo: ../include/net-snmp/output_api.h
for EVERY library object and header file. Even those that are not compiled, depending on which flags are selected in the configure script.
I looked at the "make" manual but I didn't find this exact case. It may be (as stated in 5.9) that they do this to exclude the possibility that an implicit recipe is called on the target, but other than that I have no idea.
Another reason might be to "break" the compilation if the library is tampered with (deleting whatever header in the project causes in fact the makefile to crash because it can't execute the rule).
This is an educated guess but I would like to know the theory behind this. I mean the makefile already builds whatever it has to build, why include all these rules in explicit form?
Thanks
As I expect you know, the Makefile is generated by the ./configure script.
All the lines that interest you are auto-generated dependencies. Actually,
they are just the appended contents of the file Makefile.depend in the same
directory, which is part of the distribution and was generated with the aid
of gcc ahead of time.
So e.g.
./dir_utils.lo: ../include/net-snmp/output_api.h
just informs make of the vitally important fact that ./dir_utils.lo
depends on ../include/net-snmp/output_api.h. Then if ./dir_utils.lo
is older than ../include/net-snmp/output_api.h, make will re-make
./dir_utils.lo provided it has some recipe to do that, which it has.
Here is a project:
main.c
#include "hw.h"
#include <stdio.h>
int main(void)
{
puts(HW);
return 0;
}
hw.h
#ifndef HW_H
#define HW_H
#define HW "Hello World"
#endif
Makefile
CC := gcc
.PHONY: all clean
all: hw
hw: main.o
$(CC) -o $# $<
clean:
rm -f hw main.o
Build and run it:
$ make && ./hw
gcc -c -o main.o main.c
gcc -o hw main.o
Hello World
But there's a bug in the makefile. It doesn't know that main.o
depends on hw.h:
$ touch hw.h
$ make
make: Nothing to be done for 'all'.
Append that dependency to the makefile:
main.o: hw.h
and retry:
$ make
gcc -c -o main.o main.c
gcc -o hw main.o
Bug fixed.
My instructions are:
The steps necessary to produce this program are:
Compile cpp2html.c to produce cpp2html.o. (Important: the source code in this project is C, not C++, and so must be compiled and linked with gcc, not g++.)
Run the command
flex cppscanner.l
to produce the file lex.yy.c from the language description in cppscanner.l.
Compile lex.yy.c to produce lex.yy.o. (This often produces a warning message about extra tokens. Ignore it.)
Link the .o files to produce an executable program named cpp2html
Write a makefile that will carry out these steps. Your makefile should result in only the minimum required amount of steps when any input file to this process is changed. (Note: you will probably not be able to base this makefile upon my self-updating makefile as in the earlier part of the assignment. Instead, you will probably find it necessary to write this one from scratch.
Here is my makefile:
cpp2html: cpp2html.o lex.yy.o
gcc -g -DDEBUG cpp2html.o lex.yy.o
mv a.out cpp2html
lex.yy.o: lex.yy.c
gcc -g -DDEBUG lex.yy.c
lex.yy.c:
flex cppscanner.l
cpp2html.o: cpp2html.c
gcc -g -DDEBUG cpp2html.c
What am I doing wrong here? I get an error message saying:
collect2: error: ld returned 1 exit status
make: *** [cpp2html.o] Error 1
Your makefile does not build 'cpp2html' when invoked:
gcc -g -DDEBUG cpp2html.c
This is due tonight, so any suggestions would be greatly appreciated.
It's complaining about the following:
cpp2html.o: cpp2html.c
gcc -g -DDEBUG cpp2html.c
This line is trying to compile cpp2html.c to a.out.
Change that to
cpp2html.o: cpp2html.c
gcc -g -DDEBUG -c cpp2html.c
Do the same thing for the line that compiles lex.yy.c to lex.yy.o. The -c option tells gcc to only generate the object file and write it to the .o file.
You can take advantage of other options and some built-in variables. Here's a suggestion:
cpp2html: cpp2html.o lex.yy.o
gcc -g -DDEBUG -o $# $?
$# evaluates to the name of the target. $? evaluates to the list of dependencies (the .o files). The -o option tells gcc to write the resulting binary to the specified file name instead of a.out.
You can also take advantage of implicit rules:
%.o : %.c
gcc -g -DDEBUG -c $<
This will build any .c file to the corresponding .o file, so you don't need to repeat the same commands for cpp2html.c and lex.yy.c.
Edit
FWIW, here's how I'd structure the makefile (with annotations; assumes Gnu make):
# Variables used by implicit rules
CFLAGS=-g -DDEBUG -Wall -Werror # flags for gcc
LFLAGS= # flags for flex, currently none
LEX=flex # lexer
CC=gcc # C compiler
# Variables to make life easier
LSRCS=cppscanner.l # All of our flex source files
SRCS=cpp2html.c $(patsubst %.l,%.c,${LSRCS}) # All of our C source files
OBJS=$(patsubst %.c,%.o,${SRCS}) # All of our object files
TARGET=cpp2html # Final target name
${TARGET} : ${OBJS}
${CC} ${CFLAGS} -o $# $^ # Explicit rule to build target
# $# expands to target name
# $^ expands to list of all prerequisites
clean:
rm -rf *.o $(patsubst %.l,%.c,${LSRCS})
That's it. We're relying on implicit rules to build the .l file to a .c file, and to build the .c files to .o files. The implicit rules use the LEX, CC, LFLAGS and CFLAGS variables to run the right commands with the right options. We only need the single explicit rule to build our final executable.
The advantage of structuring a makefile like this is that you can add files to the project without having to add new rules.
I think all of the above is correct; my main box is shut down at the moment so I can't test it. Refer to the Gnu Make manual for more details.