I have an executable made by linking several .o files, using a somewhat involved linker script. If a particular environment flag is set, I'd like there to be another section involved. So, something like:
/* withfoo.ld */
SECTIONS {
.foo 0x80000000 : {
_start_foo = .;
. += 0xfff;
_end_foo = .;
}
}
and then compile with:
if [ $FOO_DEFINED ]; then
gcc -Wl,-T normal.ld $(OBJS) foo.o foo.ld
else
gcc -Wl,-T normal.ld $(OBJS)
fi
where I'd like to not to have to modify normal.ld. normal.ld has a SECTIONS definition within it. So that gives me two SECTIONS... I'm getting the warning:
/usr/bin/ld: warning: foo.ld contains output sections; did you forget -T?
Is there an actual problem or is this just a warning? Also is there a proper way to do this that I'm not aware of?
GNU linker scripts and LD do not support conditional compile. You can't use two linker scripts at once and have it work as expected. You have to pre-process the linker script with some other type of macro language like m4 or even just use the GNU C Preprocessor cpp.
Not sure if this is a type of solution you are looking for. The C preprocessor cpp can process the text of any kind of file (even non-C files) and will blindly parse for pre-processor directives and macros just like a C file. You can also pass C style defines through the cpp command line just like you can with GCC. You create a linker script with all the C pre-processor directives and generate specific linker scripts based on that. For example create a special linker script like this:
linker.ld.pp :
SECTIONS {
#ifdef FOO
.foo 0x80000000 : {
_start_foo = .;
. += 0xfff;
_end_foo = .;
}
#endif
}
This uses typical C style #if/#ifdef/#endif for conditional compilation. If you run it through the C pre-processor with a command like:
cpp -P -DFOO linker.ld.pp
You'd get output like this:
SECTIONS {
.foo 0x80000000 : {
_start_foo = .;
. += 0xfff;
_end_foo = .;
}
}
Not defining FOO would be done like this:
cpp -P linker.ld.pp
The output would be:
SECTIONS {
}
To redirect the pre-processor you can output to a linker script with:
cpp -P -DFOO linker.ld.pp >withfoo.ld
cpp -P linker.ld.pp >withoutfoo.ld
You can specify multiple -D directives on the CPP command line to define multiple pre-processor symbols if you wish. The idea of this method is that you have a single linker script but you create a shell script to generate the necessary specialized scripts that are required.
Other Observations
Usually you specify linker scripts on the GCC command line with the -T option:
gcc -T withfoo.ld $(OBJS) foo.o
Related
I would like to understand how the preprocessor inlines includes into the code in Fortran. With C, it's pretty simple:
Test.c:
#include <stdio.h>
int main(void) {
return 0;
}
Then I compile using:
gcc -E test.c
Then it displays the content generated by the C preprocessor, as expected.
Now assume I have this Fortran code:
Test.f:
program test
include "mpif.h"
call mpi_init
call mpi_finalize
end
Then I run:
gfortran -E -cpp test.f // For some reason I need -cpp when using -E in Fortran
But I won't have the expected result, which is the generated include embedded into the code.
Instead, I have this:
# 1 "test.f"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.f"
program test
include 'mpif.h'
call mpi_init
call mpi_finalize
end
What am I doing wrong here?
Fortran has its own include directive which must not be confused with the preprocessor directive #include. As far as I understand it, the included code is not embedded into the master file, but the compiler instead continues to compile from the include file, and returns to the master file at the end of that file. From here:
The INCLUDE statement directs the compiler to stop reading statements
from the current file and read statements in an included file or text
module.
Also, included files are not preprocessed further, while #included ones are.
Note, that there is also a naming convention that enables the preprocessor only on files with capital suffixes *.F and *.F90. If you want to preprocess *.f or *.f90 files, you need to specify that in a compile option, e.g. -cpp for gfortran, and -fpp for ifort.
This question already has an answer here:
Conditional compilation in gfortran
(1 answer)
Closed 4 years ago.
I'm importing the following module (from an static library) in a file named initprogram.f90 this way:
use device_info
However I only want to include this library when this option in the Makefile is selected:
ifeq ($(strip $(WITH_GPU)),1)
(When WITH_GPU is equal to 1). If I'm not using a GPU, device_info.mod should not be available, since I don't need it. How can I do that?
Basically I want to get rid of this error:
Fatal Error: Can't open module file 'device_info.mod' for reading at (1): No such file or directory
When compiling without the library where device_info.mod is defined.
You probably need:
A preprocessor to hide or not the use device_info declaration in your Fortran source file, depending on an option you pass to it. Do you have a preprocessor in your Fortran compilation chain? If yes do you know how to pass it options from the command line and how to use them in your source file to hide or not parts of your code?
Pass the right option to the compiler chain from your Makefile.
Let's assume you do have a preprocessor and it has an #ifdef - #endif macro. Let's also assume your compiler chain takes options -D MACRO=VALUE from the command line. And let's assume the syntax of your compiler command looks like:
<compiler-name> <options> <source-file> -o <binary-output-file>
Just edit your source file and add:
#ifdef WITH_GPU
use device_info
#endif
And then, edit your Makefile:
COMPILER := <whatever-compiler-you-use>
COMPILER_FLAGS := <whatever-compiler-options-you-need-by-default>
OTHER_DEPENDENCIES := <whatever-default-dependencies>
ifeq ($(strip $(WITH_GPU)),1)
COMPILER_FLAGS += -D WITH_GPU=1
OTHER_DEPENDENCIES += device_info.mod
endif
initprogram.exe: initprogram.f90 $(OTHER_DEPENDENCIES)
$(COMPILER) $(COMPILER_FLAGS) $< -o $#
(the $< and $# make automatic variables expand respectively to the first prerequisite (initprogram.f90) and the target (initprogram.exe) of the rule).
I am forking an existing project on Github that I want to make some changes to. One thing I want to do is add an extra file. This file should be in one of the libraries that the Makefile.am generates. The problem is that the file I want to add is a .c file, while everything else in the project is .cpp.
The library that should contain the file is used like this in the makefile:
MYLIBRARY=path/mylibrary.a
...
path_mylibrary_a_CPPFLAGS = $(AM_CPPFLAGS)
path_mylibrary_a_CXXFLAGS = $(AM_CXXFLAGS)
path_mylibrary_a_SOURCES = \
path/cppfile1.cpp \
path/cppfile1.h \
path/cppfile2.cpp \
path/cppfile2.h \
path/cppfile3.cpp \
path/cppfile3.h
...
mybinary_LDADD = $(MYLIBRARY)
Simply adding the path/cfile.c and path/cfile.h to the list of sources gives me the following error:
CXXLD mybinary
/usr/bin/ld: path/mylibrary.a(path_mylibrary_a-cfile.o): relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
path/mylibrary.a: error adding symbols: Bad value
What can I do so the Makefile.am will compile the c file in a project that otherwise is built in c++?
The canonical way to solve this problem is to have a convenience library in which you compile your c code:
path_mylibrary_a_LDADD = cfile.la
noinst_LTLIBRARIES = cfile.la
cfile_la_CPPFLAGS = -std=c11
cfile_la_SOURCES = cfile.c
For more information refer to this answer.
I presume your cfile.h has the structure
#ifndef MYLIBRARY_CFILE_H
#define MYLIBRARY_CFILE_H
#ifdef __cplusplus
extern "C" {
#endif
/* your C declarations here */
#ifdef __cplusplus
}
#endif
#endif /* MYLIBRARY_CFILE_H */
Also, as a general tip: If you want to add a few source files to a library or program in a Makefile.am file, just add the lines
path_mylibrary_a_SOURCES += cfile.c cfile.h
which makes for a very clean patch which only adds one line, does not touch other lines, etc.
Also, I concur with https://stackoverflow.com/users/440558/some-programmer-dude on the path_mylibrary_a_CFLAGS which you probably need to add as well.
I've been attempting to make a folder for each architecture my code can support. In this folder are platform specific files to include. I include them as follows:
#define STR(x) #x
#define ASSTR(x) STR(x)
#include ASSTR(ARCHITECTURE/sizes.h)
My compilation line in make looks like this:
gcc -o $# -c $< -DARCHITECTURE=i386
Which works, until I define ARCHITECTURE to be i386. When this happens, it looks for 1/sizes.h, so I assume it's already defined somewhere.
I believe the C preprocessor (cpp), which is called by gcc, defines i386 (for i386 systems). You can find out what it defines like so:
touch foo.h; cpp -dM foo.h; rm foo.h
This method is described by the cpp man page, under -d, with the character M (so, -dM):
Instead of the normal output, generate a list of #define directives for all the macros defined during the execution of the preprocessor, including predefined macros. This gives you a way of finding out what is predefined in your version of the preprocessor. Assuming you have no file foo.h, the command
touch foo.h; cpp -dM foo.h
will show all the predefined macros.
I'm trying to use the sprintf() function. Therefore I have to include the stdio.h in my C project. If I compile the project without including the stdio.h in my makefile, the compiler generates the error that sprintf() is a unknown function. Including the stdio.h to the makefile generates the error that there is "no rule to make target."
The makefile template gives the options as follows:
NAME = test
CC = arm-none-eabi-gcc
LD = arm-none-eabi-ld -v
AR = arm-none-eabi-ar
AS = arm-none-eabi-as
CP = arm-none-eabi-objcopy
OD = arm-none-eabi-objdump
CFLAGS = -I./ -c -fno-common -O0 -g -mcpu=cortex-m3 -mthumb
AFLAGS = -ahls -mapcs-32 -o crt.o
ASFLAGS = -Wa,-gstabs
LFLAGS = -Tlinkerscript_rom.cmd -nostartfiles
CPFLAGS = -Obinary
ODFLAGS = -S
I hope that you can help me out, because I have no desire to rewrite every standard function.
Sven
Makefiles don't read include files. The C preprocessor reads include files, before the resulting file is compiled by the compiler. You should include the header in your C file. Just add:
#include <stdio.h>
Somewhere close to the top, before any function definitions etc.
This will show a declaration of the function to the compiler, which will remove the warning.
Just include stdio.h at the top of your c file
#include <stdio.h>
The only reason to put a .h file in your makefile is so that the files dependent upon your header will be recompiled if anything in the header is changed. Needless to say, this is most commonly with header files you have written.
If there is an error after including stdio.h, you have a broken tool chain. If you update your question to indicate your platform, we may be able to help you fix it :)