How to pass arbitrary compiler CFLAGS with Scons from the command line without custom code? - scons

Is there a way to write something like:
scons CFLAGS='-fsanitize=address -fsanitize=thread'
which would just work with a minimal script:
env = Environment()
env.Program(target='main.out', source=['main.c'])
without changing that script?
I know how to do it by modifying the script with AddOption + env.Append(CCFLAGS but I'm wondering it it is possible without changing the code to explicitly support it.

I ended up going with:
env = Environment()
env.Append(CCFLAGS='-Werror')
env.Append(CCFLAGS=ARGUMENTS.get('CCFLAGS', ''))
env.Program(target='main.out', source=['main.c'])
which can be used as:
scons CCFLAGS='-Wall -pedantic'
and will compile as:
gcc -o main.o -c -Werror -Wall -pedantic main.c
You likely want to keep the env.Append(CCFLAGS=ARGUMENTS.get('CCFLAGS', '')) line as the very last change made to CCFLAGS, since this will allow overriding the defaults on the command line: GCC tends to just use the last value seen as the actual one.
TODO: how to make it work with Variables? This would be nicer since we can get more error checking and the help message generation:
variables = Variables(None, ARGUMENTS)
variables.Add('CCFLAGS', 'my help', '')
env = Environment(variables)
env.Append(CCFLAGS='$CCFLAGS')
env.Append(CCFLAGS=['-Werror'])
env.Program(
source=['main.c'],
target='main.out',
)
Help(variables.GenerateHelpText(env))
but this fails due to bad quoting:
gcc -o main.o -c "-Wall -pedantic" -Werror main.c

This is not possible by design (without explicitly changing the build scripts). From the answer to #1 of the most-frequently-asked questions in our FAQ:
SCons does not automatically propagate the external environment used
to execute 'scons' to the commands used to build target files. This is
so that builds will be guaranteed repeatable regardless of the
environment variables set at the time scons is invoked. This also
means that if the compiler or other commands that you want to use to
build your target files are not in standard system locations, SCons
will not find them unless you explicitly set the PATH to include those
locations.

Related

Understanding the role of automatic variables in make/makefile

I've recently started learning about make/makefiles and some of its associated concepts. I have thus far created some basic versions, with targets, dependencies, actions, etc. However, I have been unable to make sense of what automatic variables are in this context, their utility, or how to use them.
I've done a lot of research on this topic. However, I haven't been able to locate any satisfying material. It seems that the most reputable source of information is this GNU page. However, I still do not understand exactly what automatic variables are, what their utility is, or how I can practically use them in my makefiles.
I am interested to find out what they are, what their utility is, and how I can practically use them.
Automatic variables are simply variables for which the value is generated by Make, rather than having to be set explicitly.
If you take a look at existing Makefiles (pick your favorite open source project!), you'll find lots of practical examples to help you out. A common one looks something like this:
%.o: %.c
gcc -c -o $# $<
This is a pattern rule that says "to build a file named <something>.o, which depends on <something.c>, use the command gcc -c -o $# $<. $# is an automatic variable that evaluates to the target of the rule, and $< is an automatic variable which evaluates to the first prerequisite of the rule. These automatic variables are critical to this sort of pattern rule; they allow you to run make foo.o or make bar.o and have the appropriate values substituted into the command line. E.g., if you type make foo.o, Make will try to run:
gcc -c -o foo.o foo.c
Or consider this example from the git Makefile:
strip: $(PROGRAMS) git$X
$(STRIP) $(STRIP_OPTS) $^
This is used to strip symbol information from object files. It needs to operate on all of the prerequisites, so it uses $^, which evaluates to...a space-separated list of all the prerequisites.

Using AM_INIT_AUTOMAKE parameters and configure script command line parameters

Is it possible to use both command line parameters to the configure script, ala:
../configure CXXFLAGS=-O0 -g -foo -bar -bat
while simultaneously keeping any options that are passed to AM_INIT_AUTOMAKE in the configure.ac file? e.g.:
AM_INIT_AUTOMAKE([-Wall -Werror])
My desired result is that ALL of the above flags are passed along, e.g.:
-O0 -g -foo -bar -bat -Wall -Werror
It appears that specifying CXXFLAGS on the command line either ingores or overwrites what is passed into AM_INIT_AUTOMAKE. If anyone knows of a way to do the union of the two sets, that would be extremely helpful. Thx!
The -W warning categories options have nothing to do with C[XX]FLAGS. These are command line arguments used when invoking automake.
Conversely, the AM_INIT_AUTOMAKE options like -Wall don't affect the compiler flags. It's just the decision to use these switch names that is resulting in the confusion. They have a similar meaning, e.g., -Werror will treat automake warnings as errors, but are totally unrelated.
In short, you have to pass -Wall, etc., in C[XX]FLAGS to influence the compiler. And add -Wall to AM_INIT_AUTOMAKE to enable all warning categories when automake is invoked.

scons: changing compilation flags for a single source file

I have a fairly complex scons system with several subdirectories, with many libraries and executables.
Currently, every SConscript gets its own cloned environment, so I can easily change CFLAGS (or any other wariable) on a per-SConscript basis, but I'd like to change it per-target, and even per-object-file within a target.
I created a simple example SConscript and SConstruct to explain the problem, as follows.
SConstruct:
env = Environment()
env['CFLAGS'] = '-O2'
env.SConscript('SConscript', 'env')
SConscript:
Import('env')
env=env.Clone()
env.Program('foo', ['foo.c', 'bar.c'])
If I run scons, both foo.c and bar.c compile with -O2 flags. I could easily change flags SConscript-wide by just adding env['CFLAGS'] = '...' within the SConscript, but let's say that I want to compile foo.c with -O2, but bar.c with full debugging, -O0 -g. How do I do that (in the simplest possible way)?
The example uses gcc, but I'd like something that can be used with any compiler.
This happens frequently with performance-sensitive projects where compiling everything without optimization would result in unacceptable performance, but there is a need to debug one single file (or a subset of them).
The simplest one-liner answer is probably just to replace your Program line with this:
env.Program('foo', ['foo.c', env.Object('bar.c', CFLAGS='-g')])
because Program can take Object nodes as well as source files, and you can override any construction variable(s) in any builder (here, we override CFLAGS in the Object builder call). If you want to break out the Object into its own line for clarity:
debug_objs = env.Object('bar.c', CFLAGS='-g')
env.Program('foo', ['foo.c', debug_objs])
and of course taking that to the limit you get a system like Avatar33 showed above.
I suppose this is a bit harder in scons than it would be in make where you could just clean the required target and rebuilt with debug flags. Which would then just rebuild a specific object.
The solution to your particular project depends on it's size and how much effort the developer is prepared to put in.
So here's a rough solution where you specify source files on the command line that you want to be compiled with debug and no optimization, the rest will be compiled with -O2.
In your SConsctruct one additional line to get source files that we want to compile with debug from a command line option:
env = Environment()
env['CFLAGS'] = '-O2'
AddOption('--debug-targets', dest='debug-targets', type='string')
env.SConscript('SConscript', 'env')
And now in the SConscript file:
Import('env')
env=env.Clone()
debug_env = env.Clone()
debug_env['CFLAGS'] = '-g -O0'
normal_src = ['foo.c', 'bar.c']
debug_src = []
#Add src specified via the command line to the debug build
if GetOption('debug-targets'):
for x in GetOption('debug-targets').split(','):
if x in normal_src:
normal_src.remove(x)
debug_src.append(x)
normal_obj = env.Object(normal_src)
debug_obj = debug_env.Object(debug_src)
all_obj = normal_obj + debug_obj
env.Program('foo', all_obj)
Running our scons with out our debug-targets flag:
scons -Q
gcc -o bar.o -c -O2 bar.c
gcc -o foo.o -c -O2 foo.c
gcc -o foo foo.o bar.o
But now we want to compile bar.c with debug info:
scons -Q --debug-targets=bar.c
gcc -o bar.o -c -g -O0 bar.c
gcc -o foo foo.o bar.o
So that adds a bit of complexity to your build system, but if you don't need to specify debug targets from the command line like that, then the developer can obviously just cut and past sources from the normal_src list to debug_src.
There's probably many ways to improve and fine tune this for your specific environment

Simple makefile for C/C++ targets used with arm-linux-gcc

I would like to cross-compile a simple program for ARM architecture using the arm-linux-gcc suite of compilers [arm-linux-gcc (Buildroot 2011.08) 4.3.6]. I've attempted to use a simple makefile for compiling C code, and another simple makefile for compiling C++ code. For example, my makefile for C code is reproduced below, but it does not create an ELF binary for running on my embedded system. The host system is x64 GNU Linux.
Here is the listing of my very simple makefile for a C program:
CC=arm-linux-gcc
CFLAGS=-Wall
main: test.o
clean:
rm -f test test.o
The makefile reproduced above only creates an object file with extension .o, and does not create an ELF binary.
I've Googled for a good solution, but I can't seem to find one webpage showing example cross-compile ARM makefiles for both C and C++ programs. Perhaps an answer to this post could show such examples.
Have a look at the GNU make manual (info make), Section 10.2. It has a catalogue of the implicit rules, i.e. the rules where you don't need to explicitly state the commands. Like #GregHewgill thought, the "Linking a single object file" implicit rule builds N from N.o, but the name must match. Therefore, you can either name your executable like your object file, in which case
test:
or (more standard because it defines the all target)
all : test
completely suffice. You can also write out the rule explicitly, like Greg Hewgill also described. In this case, the standard rule is:
$(CC) $(LDFLAGS) N.o $(LOADLIBES) $(LDLIBS)
Include the LDFLAGS and LDLIBS in your Makefile, it makes life easier for users.
(sic: I think LOADLIBES is really LOADLIBS, and the author missed the -o).
Overall, I'd recommend autoconf and automake instead of hand-rolling makefiles. Gives you a bunch of Makefile features for very little work.
I tried your Makefile and changed the following:
test: test.o
It worked after this changed and created a binary called test. It seems that there is some implicit rule that knows how to link whatever if one of its dependencies is whatever.o.
Another way is to list the rule explicitly:
main: test.o
$(CC) -o $# $$
This uses the special macros $# (which means target) and $$ (which means dependencies).

Setting per-file flags with automake

Is there a way set flags on a per-file basis with automake?
In particular, if I have a c++ project and want to compile with -WAll all the files except one for which I want to disable a particular warning, what could I do?
I tried something like:
CXXFLAGS = -WAll ...
bin_PROGRAMS = test
test_SOURCES = main.cpp utility.cpp
utility_o_CXXFLAGS = $(CXXFLAGS) -Wno-unused-value
but it didn't work.
EDITED: removed reference to automake manual, which was actually misleading (thanks to Douglas Leeder).
You can't do this with automake... but can do with make =) Add following line to your Makefile.am:
utility.$(OBJEXT) : CXXFLAGS += -Wno-unused-value
See GNU Make documentation : Target-specific Variable Values for details.
Automake only supports per-target flags, while you want per-object flags. One way around is to create a small library that contains your object:
CXXFLAGS = -Wall ...
bin_PROGRAMS = test
test_SOURCES = main.cpp
test_LDADD = libutility.a
noinst_LIBRARIES = libutility.a
libutility_a_SOURCES = utility.cpp
libutility_a_CXXFLAGS = $(CXXFLAGS) -Wno-unused-value
You've got confused - that section is referring to options to automake itself.
It's a way of setting the automake command-line options:
-W CATEGORY
--warnings=category
Output warnings falling in category. category can be one of:
gnu
warnings related to the GNU Coding Standards (see Top).
obsolete
obsolete features or constructions
override
user redefinitions of Automake rules or variables
portability
portability issues (e.g., use of make features that are known to be not portable)
syntax
weird syntax, unused variables, typos
unsupported
unsupported or incomplete features
all
all the warnings
none
turn off all the warnings
error
treat warnings as errors
A category can be turned off by prefixing its name with ‘no-’.
For instance, -Wno-syntax will hide the
warnings about unused variables.
The categories output by default are ‘syntax’ and ‘unsupported’.
Additionally, ‘gnu’ and ‘portability’
are enabled in --gnu and --gnits
strictness.
The environment variable WARNINGS can contain a comma separated list of
categories to enable. It will be taken
into account before the command-line
switches, this way -Wnone will also
ignore any warning category enabled by
WARNINGS. This variable is also used
by other tools like autoconf; unknown
categories are ignored for this
reason.
The per-file listed in section 17 refers to per-Makefile not source file.
I'm not aware of any per-source file flag setting, but you can set the option for each result binary with:
binaryname_CXXFLAGS

Resources