scons: overriding build options for one file - scons

Easy question but I don't know the answer.
Let's say I have a scons build where my CCFLAGS includes -O1. I have one file needsOptimization.cpp where I would like to override the -O1 with -O2 instead. How could I do this in scons?
update: this is what I ended up doing based on bialix's answer:
in my SConscript file:
Import('env');
env2 = env.Clone();
env2.Append(CCFLAGS=Split('-O2 --asm_listing'));
sourceFiles = ['main.cpp','pwm3phase.cpp'];
sourceFiles2 = ['serialencoder.cpp','uartTestObject.cpp'];
objectFiles = [];
objectFiles.append(env.Object(sourceFiles));
objectFiles.append(env2.Object(sourceFiles2));
...
previously this file was:
Import('env');
sourceFiles = ['main.cpp','pwm3phase.cpp','serialencoder.cpp','uartTestObject.cpp'];
objectFiles = env.Object(sourceFiles);
...

Use Object() builder for fine-grained control over compilation, and then pass these objects to Program() builder.
E.g. instead of:
env = Environment()
env.Program(target='foo', source=['foo.cpp', 'bar.cpp', 'needsOptimisation.cpp'])
You need to use following:
env = Environment()
env_o1 = env.Clone()
env_o1.Append(CCFLAGS = '-O1')
env_o2 = env.Clone()
env_o2.Append(CCFLAGS = '-O2')
# extend these lists if needed
SRC_O1 = ['foo.cpp', 'bar.cpp']
SRC_O2 = ['needsOptimisation.cpp']
obj_o1 = [env_o1.Object(i) for i in SRC_O1]
obj_o2 = [env_o2.Object(i) for i in SRC_O2]
env.Program(target='foo', source=obj_o1+obj_o2)
You can avoid creation of separate clone of env variable if you provide CCFLAGS='-O2' right in the Object() call:
obj_o2 = [env.Object(i, CCFLAGS=env['CCFLAGS'] + ['-O2']) for i in SRC_O2]

Avoiding creating of a separate env variable requires (ref: bialix's answer) needs something like this.
obj_o2 = env.Object(SRC_O2, CCFLAGS=env['CCFLAGS'] + ['-O2']);
If you just do this (or in the for loop like bialix does)
obj_o2 = env.Object(SRC_O2, CCFLAGS='-O2');
then you lose all the builtin flags.

Related

Internal Error: no cycle found in Scons Build

When I build my Sconstruct file, I am getting the below error.
scons: *** Found dependency cycle(s):
build/sselser/sselConfigArgs.h -> build/sselser/sselConfigArgs.h
Internal Error: no cycle found for node build/sselser/sselMain (<SCons.Node.FS.File instance at 0x9f61e8>) in state pending
Internal Error: no cycle found for node build/sselser/sselMain.o (<SCons.Node.FS.File instance at 0x9f2e68>) in state pending
File "/nfs/scons/scons-1.3.0/lib/scons-1.3.0/SCons/Taskmaster.py", line 1026, in cleanup
I guess this is due to dependency of sselMain in sselTransorm as the error occurs during the build of sselTransform directory.
Makefile in sselTransform:
UNIT_SUPPORT_FILES += ../sselser/sselMain intest ../../../make/Makenv
MDE_SUPPORT_FILES += ../sselser/sselMain intest ../../../make/Makenv
I need to add the same in Sconscript of sselTransform directory to resolve this issue.
How to resolve this issue?
Sconscript:
#Set CPPPATH, RPATH, DEFINES and CCFLAGS
env = Environment(CPPPATH =['.','../sselTransform','../sselSm','../sselSRC'],
RPATH = ['/l-n/app/colr/lib/infra/SunOS5.10/WS12.0'],CPPDEFINES = ['THREADSAFE','_RWSTD_SOLARIS_THREADS','_SVID_GETTO
D','DEBUG','sun5'],CCFLAGS = ['library=rwtools7_std','features=no%tmplife','-pta','-mt','-xdebugformat=stabs','-g0','-xildoff'])
env['CXX']=CXX
Src = Split('sselManager.C PromoNotifyMgr.C ')
env.StaticLibrary('libSselser-g0.a',Src)
Src1 = Split('sselMain.C sselManager.o PromoNotifyMgr.o ')
env.Program('sselMain',Src1)
configfile = 'sselConfigArgs.h'
CONFIG_PATH = '../../build/include/'
CONFIG=CONFIG_PATH+configfile
env.Command(CONFIG,configfile,
[Copy('$TARGET', '$SOURCE'),
Chmod('$TARGET', 0444)])
Sconstruct:
SConscript('src/ssel/sselser/SConscript',variant_dir='build/sselser',duplicate=0,exports='env')
Try this?
Notes:
I'm saving the build objects for your two source files and using those in both the program and static library.
I've added the target dir you're copying the header file to earlier in the CPPPATH.
You could have skipped the variables configfile, CONFIG_PATH, CONFIG and just used the strings in your Command.
You are using a VERY old version of SCons. If you're limited to python 2.7 please try using SCons 3.0.1? If you're not and can use Python 3.6, then try using SCons 4.3.0.
#Set CPPPATH, RPATH, DEFINES and CCFLAGS
env = Environment(
CPPPATH =['.','../include','../sselTransform','../sselSm','../sselSRC'],
RPATH = ['/l-n/app/colr/lib/infra/SunOS5.10/WS12.0'],
CPPDEFINES = ['THREADSAFE','_RWSTD_SOLARIS_THREADS','_SVID_GETTOD','DEBUG','sun5'],
CCFLAGS = ['library=rwtools7_std','features=no%tmplife','-pta','-mt','-xdebugformat=stabs','-g0','-xildoff'])
env['CXX']=CXX
Src = ['sselManager.C','PromoNotifyMgr.C']
objects = []
for s in Src:
objects.extend(env.StaticObject(s))
env.StaticLibrary('Sselser-g0',objects)
Src1 = ['sselMain.C'] + objects
env.Program('sselMain', Src1)
configfile = 'sselConfigArgs.h'
CONFIG_PATH = '../include/'
CONFIG=CONFIG_PATH+configfile
env.Command(CONFIG, configfile,
[Copy('$TARGET', '$SOURCE'),
Chmod('$TARGET', 0444)])

How can SCons detect changes in SConstruct script?

I want build files based on variables that are assigned in the SConstruct file. As in this example:
import os
env = Environment(ENV = os.environ)
def text_file_maker(target, source, env):
with open(str(target[0]), "w") as text_file:
text_file.write(env['my_text'])
return 0
env.Append( BUILDERS = {'Make_text' : Builder(action = text_file_maker)})
env.Make_text(target = 'sour.txt',
source = None,
my_text = 'lemon')
env.Make_text(target = 'sweet.txt',
source = None,
my_text = 'apple')
Running this script generates two text files with the content 'lemon' and 'apple'. If I'm running the script again, SCons correctly detects that the targets exist:
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
scons: done building targets.
Now if I'm changing one target, e.g:
env.Make_text(target = 'sweet.txt',
source = None,
my_text = 'orange')
and run the script, I'll get scons: '.' is up to date. and the content 'orange' is not written to sweet.txt.
I could specify a dependency:
Depends(['sweet.txt', 'sour.txt'], 'SConstruct')
but that forces a rebuild for any change in my SConstruct script.
How can I make SCons recognise changes in the SConstruct script that affects the targets?
This could be a very simple question, sorry if I missed something obvious.
You're example very closely resembles the answer which is in the manpage (Search for "Action Objects"):
https://scons.org/doc/production/HTML/scons-man.html
The third and succeeding arguments, if present, may either be a construction variable or a list of construction variables whose values will be included in the signature of the Action when deciding whether a target should be rebuilt because the action changed. The variables may also be specified by a varlist= keyword parameter; if both are present, they are combined. This is necessary whenever you want a target to be rebuilt when a specific construction variable changes. This is not often needed for a string action, as the expanded variables will normally be part of the command line, but may be needed if a Python function action uses the value of a construction variable when generating the command line.
def build_it(target, source, env):
# build the target from the 'XXX' construction variable
open(target[0], 'w').write(env['XXX'])
return 0
# Use positional arguments.
a = Action(build_it, '$STRINGIT', ['XXX'])
# Alternatively, use a keyword argument.
a = Action(build_it, varlist=['XXX'])
So in your case change:
env.Append( BUILDERS = {'Make_text' : Builder(action = text_file_maker)})
To
tfb_action = Action(text_file_maker, varlist=['my_text'])
env.Append( BUILDERS = {'Make_text' : Builder(action = tfb_action)})

Library builder not triggering on modified file in SCons

I have to build a target using a two steps compilation.
The first step: .c -> .asm
The second step: .asm -> .o
I am creating a library from some .o files.
My implementation is the following:
The first step:
c_to_asm_builder = SCons.Builder.Builder(action = SCons.Defaults.CAction,
emitter = {},
suffix = '.asm',
src_suffix = ['.c','.cpp'],
src_builder = '',
source_scanner = SCons.Tool.CScanner
)
env['Builders']['CTOASM'] = c_to_asm_builder
The second step:
suffixesASM = ['.asm', '.s']
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
for suffix in suffixesASM:
static_obj.add_action(suffix, SCons.Defaults.ASAction)
I am then calling the builders as follows:
env.CTOASM(['file1.c', 'file2.c', 'file3.c'], CFLAGS = '-flag')
env.Object(['file1.asm', 'file2.asm', 'file3.asm'], ASFLAGS = '-flag')
I am creating a library like this:
env.Library('name', ['file1.o', 'file2.o'])
Everything works fine for the compilation.
The problem appers when:
I change file1.c content. I expect file1.c to pass trough these steps:
file1.c -> file1.asm -> file1.o and then name.a library to be recreated.
What happens:
Only c_to_asm_builder is retriggered by the change (file1.c -> file1.asm). The Object builder (file1.asm -> file1.o) is not retriggered and also the Library builder and Program builder are not retriggered.
I don't know what I am missing. I know that for a single step compilation that I configured in another project the Object builder and Library builder are somehow aware of each other.
How to make Library and Program builder aware of Object and CTOASM builders ?
You are not specifying an emitter, so either write one, or explicitly list your expected targets...
asm = env.CTOASM(['file1.asm', 'file2.asm', 'file3.asm'],
['file1.c', 'file2.c', 'file3.c'], CFLAGS = '-flag')
obj = env.Object(['file1.o', 'file2.o', 'file3.o'], asm, ASFLAGS = '-flag')
lib = env.Library('name', obj)
Here is a good reference on how to add an emitter to your builder.
https://bitbucket.org/scons/scons/wiki/ToolsForFools
Just scroll down to "Using Emitters".

Scons -u option doesn't work?

Seems -u doesn't work on for me ( I am using scons-2.3.6).
To simplify the context, you can imagine my project structure like,
+root
+project
- bar.vcxproj (generated vs project)
-SConstruct
-bar.c
Inside SConstruct, I have put code like:
env_base = Environment()
...
env_base.StaticLibrary(target = 'bar', source = ['bar.c'])
...
If I execute command "scons" in root folder, everything works perfectly.
But If I execute command "scons -u" in project folder, scons can find my SConstruct up in root folder, but no file get compiled.
BTW : The reason for me to execute "scons -u" in project folder is because I want to put the generated vsproj in projet folder and use BuildCommandLine to compile the project.
I guess I didn't use "-u" correctly, what will be the elegant solution for my situation?
1st edit:
As bdbaddog asked, I have put the SConstruct here:
def BuildConfig(env, config):
env.Append(CCFLAGS = '/W 4')
env.Append(CCFLAGS = '/WX')
if config == "debug":
env.Append(CCFLAGS = '/DEBUG')
#env.Append(CCFLAGS = '-Zi /Fd${TARGET}.pdb')
env.Append(CCFLAGS = '/Z7')
elif config == "release":
pass
env_base = Environment()
lib = env_base.StaticLibrary(target = 'bar', source = ['bar.c'])
opts=Variables()
opts.Add('target', 'Compile Target (debug/release).', "debug")
# there is more in my project....
opts.Update(env_base) # update environment
# here I want to use my own command to build the project, so it can support different build option that is defined by me.
env_base['MSVSBUILDCOM'] = "scons -u target=$(Configuration)"
target = env_base["target"]
BuildConfig(env_base, env_base['target'])
env_base.MSVSProject(target = "project\\bar" + env_base['MSVSPROJECTSUFFIX'],
srcs = ["..\\bar.c"],
incs = [],
localincs = "",
resources = "",
misc = "",
buildtarget = lib,
variant = ['debug'],
auto_build_solution=0)
SCons only builds files under the current directory by default.
If you you wanted to only build files in a certain directory (for which there are rules that build the targets there), you can invoke SCons as follows:
scons the_target_directory_I_want_to_build
Though this may cause sources for targets in that directory to also be built.

SCONS: How do I carry on an action on a target in place

Let's say I want to strip all the debug symbols in the shared libraries that I build whiling keeping the original file name.
I tried to add an command in the method:
def mySharedLibrary(self, *args, **kwargs):
# do some common work for every shared library like add a soname or append some lib files to LIBS parameter
target = SharedLibary(*args, **kwargs)
target = env.Command(target,target, "objcopy --strip-debug ${SOURCE}")
return target
I get this error: two different method was given to the same target,
I guess it's because the two targets returned by env.Command and SharedLibrary are exactly the same name.
Any ideas to do this?
Thanks in advance!
I had the same problem and got the same error. What I had to do was to create an intermediate target/library. The intermediate and final targets each had their own library name, so SCons doesnt get confused.
You could probably do something like the following:
env.SharedLibrary(target = 'namePreStrip', source = 'yourSource')
env.Command(target = 'name', source = 'namePreStrip', 'objcopy...')
I used objcopy to build a library out of several libraries. Here's the actual source code I implemented:
#
# Build an object file out of several other source files, objects, and libraries
# Optionally execute objcopy on the resulting library, depending if objcopyFlags
# has been populated
#
# env - SCons Environment used to build, Mandatory arg
# target - resulting library name, without LIBPREFIX and LIBSUFFIX, ej 'nsp2p',
# Mandatory arg
# sourceFiles - list of '.cc' files that will be compiled and included in the
# resulting lib, Optional arg
# objects - list of already compiled object files to be included in resulting lib,
# Optional arg
# libraries - list of libraries to be included in resulting lib, Optional arg
# objcopyFlags - list of flags to pass to objcopy command. objcopy will only
# be executed if this list is populated, Optional arg
#
# One of [sourceFiles, objects, or libraries] must be specified, else nothing
# will be performed
#
# Not using a custom builder because I dont like the way SCons prints the
# entire command each time its called, even if its not going to actually
# build anything AND I need more method args than provided by custom builders
#
def buildWholeArchive(self, env, target, sourceFiles, objects, libraries, objcopyFlags):
if len(sourceFiles) == 0 and len(objects) == 0 and len(libraries) == 0:
print "Incorrect use of buildWholeArchive, at least one of [sourceFiles | objects | librarires] must be specified, no build action will be performed"
return None
# Compile each source file
objNodes = []
if len(sourceFiles) > 0:
objNodes = env.Object(source = sourceFiles)
cmdList = []
cmdList.append(env['CXX'])
cmdList.append('-nostdlib -r -o $TARGET -Wl,--whole-archive')
for obj in objNodes:
cmdList.append(env.File(obj).abspath)
for obj in objects:
cmdList.append(env.File(obj).abspath)
for lib in libraries:
cmdList.append(lib)
cmdList.append('-Wl,--no-whole-archive')
cmd = ' '.join(cmdList)
libTarget = '%s%s%s' % (env['LIBPREFIX'], target, env['LIBSUFFIX'])
if len(objcopyFlags) > 0:
# First create the library, then run objcopy on it
objTarget = '%s%s_preObjcopy%s' % (env['LIBPREFIX'], target, env['LIBSUFFIX'])
preObjcopyTarget = env.Command(target = objTarget, source = [], action = cmd)
env.Depends(preObjcopyTarget, [objNodes, sourceFiles, objects, libraries])
objCmdList = [env['OBJCOPY']]
objCmdList.extend(objcopyFlags)
objCmdList.append('$SOURCE $TARGET')
objcopyCmd = ' '.join(objCmdList)
archiveTarget = env.Command(target = libTarget, source = preObjcopyTarget, action = objcopyCmd)
else:
# Just create the library
archiveTarget = env.Command(target = libTarget, source = [], action = cmd)
env.Depends(archiveTarget, [objNodes, sourceFiles, objects, libraries])
return archiveTarget
And here is how I called it:
sourceFiles = ['file1.cc', 'file2.cc']
libSource = []
if 'OcteonArchitecture' in env:
libSource.append(lib1)
libSource.append(lib2)
libSource.append(lib3)
objcopy = []
if 'OcteonArchitecture' in env:
objcopy.extend([
'--redefine-sym calloc=ns_calloc',
'--redefine-sym free=ns_free',
'--redefine-sym malloc=ns_malloc',
'--redefine-sym realloc=ns_realloc'])
archiveTarget = clonedEnv.buildWholeArchive(target = libName,
sourceFiles = sourceFiles,
objects = [],
libraries = libSource,
objcopyFlags = objcopy)
env.Alias('libMyLib', archiveTarget)

Resources