Common Sconstruct file like Makefile.common - scons

Is there a way to have a Sconstruct.common at the top level directory that has all the compiler/linker options that are common to individual subdirectories and have separate Sconstruct files within each individual subdirectory with custom options that specify additional arguments to the compiler/linker options ?
(Similiar to having Makefile.common in the top-level dir and individual Makefile in subdirs including Makefile.common and adding extra args using variables)
thanks,
Prakash

Yes, Scons is usually organized with a top-level SConstruct file that sets up common build commands (in an Environment) and SConscript files in sub-directories of the project that build local artifacts (objects, libraries, executables, etc). In the top-level SConstruct, you list the SConscript files that you want to include using the SConscript command. In your SConscript files you can either override certain environment variables as needed in a particular build command, or you can Clone your common build environment and override variables that way.
I highly recommend reading through the Scons User's Guide.
Also, Scons is python, so you can also import python code for build or deploy actions that might be common across projects.

Related

SCons: When adding a Node to the LIBS variable, how do I make it use just the file without the directory?

I have SCons code in which I am using SConscripts to build different directories separately. In my Src directory, my SConscript builds a shared library, and then returns the resulting Node as the Python variable libMyLibrary. I typically use the install option to copy this library to a directory that is on my system's LD_LIBRARY_PATH (I'm using OpenSUSE).
So far, so good. Now, in another directory, Src/Test, another SConscript imports libMyLibrary and builds some Programs using code like this:
env.Program('myProgram', 'myProgram.cpp', LIBS=[env['LIBS'], libMyLibrary])
The program then gets installed to my local bin folder. This code does track the library dependency and build the program, but the problem is that since the library is in a sub-directory (Src), that sub-directory gets included in the linker command. Here is an abbreviated example of the linker command that SCons generates:
g++ -o Src/Test/myProgram Src/Test/myProgram.o Src/libMyLibrary.so
I believe this happens because the Node,libMyLibrary, is essentially a path. The problem is that when I try to run the program, it is not looking for libMyLibrary.so in my library folder, but rather Src/libMyLibrary.so, and of course it doesn't find it.
I do NOT want the libraries I build to be installed in sub-directories of my install folder.
I already add the Src folder to LIBPATH, so SCons adds the -LSrc option to the linker command, but that doesn't solve the problem. My preference would be that when I add a Node, the path should automatically get parsed out to add the appropriate -L and -l options.
I know that I can get around this problem by adding the string 'MyLibrary' to the LIBS variable instead of the libMyLibrary Node, but then I have to explicitly tell SCons that each Program Depends() on libMyLibrary. It seems very inefficient to short-circuit SCons's built-in dependency tracking this way. Does anyone know the correct, SCons-y way to do this?
I'm referring to your latest comment: It looks to me as if this is not really a SCons problem, but more a general linker question (XY problem). Are you perhaps simply searching for RPATH? Please also check this old SO question: scons executable + shared library in project directory

Building relative to src/ directory with SCons

I have an app with the following (I would have thought quite common) directory hierarchy:
/src
subdir1/ # Subdirs with more source files.
more.c
SConscript
foo.c # Source files.
foo.h
SConscript
/other # Other top-level directories with no source code.
/stuff # However, there are other assets I may want to build.
README # Other top-level files.
SConstruct
The problem is that when I run scons from the top-level directory, it calls gcc from that directory without cding into src, like this:
gcc -o src/foo.o src/foo.c
This is problematic for several reasons:
Within my program, I #include files giving the path relative to the src directory. For example, more.c could include foo.h with #include "foo.h". This fails because GCC is run from the parent directory. I don't want to change my includes to #include "src/foo.h".
I use the __FILE__ special macro for things like logging. When built from the top-level directory, GCC puts "src/" at the front of all filenames, since that was the path it was given to compile. This may seem picky, but I don't want that, because I think of my source tree as being relative to the src directory.
(Edit: I should add that obviously #1 can be fixed by adding -Isrc as a flag to GCC, but this seems like more hacks around the main issue.)
How can I make SCons cd into the src directory before calling gcc?
I don't want to get rid of the src directory and move everything up, because there are lots of other (non-code) files at the top level.
I don't want SCons to cd into every subdirectory. It should just cd into src and then build all files in the hierarchy from there.
I could solve this by moving SConscript inside the src directory and running it from there, perhaps using a Makefile at the top level. But this seems quite hacky, and I also do want to use SCons to build (non-code) assets in other directories than src.
I've read that you can make a custom Builder and make it change directories. However, I do not want to write a whole new Builder for C/C++. Is there a way to modify the behaviour of an existing builder without writing one from scratch? Also, on one forum, someone said that changing directories from within a Builder would break parallel builds since it would change the directory that other tasks are building from. Is this true?
This behavior is just how SCons works, and its not possible to avoid/change. I've been looking for some supporting documentation in its favor, and havent found any. Its just something Ive become used to.
As you mention, the include paths are simple to fix. The harder part is the __FILE__ macro. I hadnt ever noticed this until you mentioned it. Unfortunately, I think the only way around this is to strip the path in the logger, which is a rather ugly fix.
You can add src to your env['CPPPATH'] which will fix the include paths.
env.Append(CPPPATH=[Dir('src')])
However, this doesn't solve the problem with __FILE__.

SCons handle conflicting options in multiple SConstruct files (OptionConflictError)

I have a project set up with several individual Sconstruct files and one toplevel SConstruct file.
project/SConstruct -- toplevel SConstruct file
project/binary1/SConstruct -- lower level SConstructs
project/binary2/SConstruct
project/binary3/src/SConstruct
I want to be able to call the individual SConstruct files with options. So each SConstruct can be called like this:
scons install --prefix=/usr/local/bin
and they have a section for that option in the SConstruct file:
AddOption('--prefix',
dest='prefix',
type='string',
nargs=1,
action='store',
metavar='DIR',
default=prefix,
help='installation prefix')
Also, in the toplevel SConstruct file, I would like to be able to call all of the lower level SConstruct files, so I added this to the toplevel SConstruct:
SConscript(binary1/SConstruct)
SConscript(binary2/SConstruct)
SConscript(binary3/src/SConstruct)
However, if I try to do this, I will get an OptionConflictError on binary2/SConstruct because the --prefix option is already defined (in binary1/SConstruct):
OptionConflictError: option --prefix: conflicting option string(s): --prefix:
Is there a way to get around this OptionConflictError?
I know I can surround the call to AddOption() with a try block, but are there better ways? Can I add a conflict_handler? Can I check if the --prefix option already exists?
Can I organize things better? I need the individual SConstruct files unfortunately, so I can't reorganize too much.
I think it would be better to do this by defining SConscript files in the sub-directories: binary1, binary2, and binary3.
I answered a similar question recently and proposed how to organize the SConsctruct and SConscript files, I think that answer would help you:
Real Hierarchical Builds with SCons?
This way you could define the --prefix option in the SConstruct files, and from the root SConstruct file, call the subdirectory SConscript files, thus avoiding the afore-mentioned error.

SCons Output in Build directory

I'm trying to modify my SCons files so that they put the generated files into a build directory. Initially I though VariantDir could be an option but judging from all I read and the examples it does not do what I want.
Is there any easy way to force SCons to put the output in a certain directory without having to rewrite all the sources and scripts?
After struggling with VariantDir for a while (it wasn't doing anything at all), I ended up using variant_dir parameter in the top level SConscript call, which causes all downstream build outputs end up in a parallel 'build' tree:
SConscript(['subdirs/SConscript'], variant_dir='build', duplicate=0)
My build structure is a hierarchy of SConscripts in subdirs/sub-subdirs, etc. With this call the outputs end up in build/sub-subdirs at the same level as they would in the source.
This eats up one level, though (subdirs), and using "../build" does not help. The solution is to have a SConscript file at the same level as SConstruct and call SConscript(['SConscript'], variant_dir='build', duplicate=0)
See also Force Scons output (exe, obj, lib & dll) to specific build directory - it has a similar answer
Using VariantDir with duplicate=0 should work.
Facing similar frustration, I added a site_scons that added replacement builders (e.g. "Exe" instead of "Program") and specified an emitter for that builder that replaced the path portion with the build directory. This requires the use of the alternate builder throughout your SConscripts though.
Alternatively you could try to subclass Environment and rewrite the main targets to use target rewrites. Then you specify your Environment as the default (modifying Scons.Script.DefaultEnvironment or something like that). This approach kept the SConscripts static but got very messy and requires more maintenance over time as scons internals change.
You might use Install or InstallAs on target output. It works for me.
lib = env.SharedLibrary(target = "some_target", source = sources);
env.InstallAs( target = "folder/output_name.ext", source = lib );

Multiple locations within a folder hierarchy to run SCons from

So far, I've only seen examples of running SCons in the same folder as the single SConstruct file resides. Let's say my project structure is like:
src/*.(cpp|h)
tools/mytool/*.(cpp|h)
What I'd like is to be able to run 'scons' at the root and also inside tools/mytool. The latter compiles only mytool. Is this possible with SCons?
I assume it involves creating another SConstruct file. I've made another one: tools/mytool/SConstruct
I made it contain only:
SConscript('../../SConstruct')
and I was thinking of doing Import('env mytoolTarget') and calling Default(mytoolTarget), but running it with just the above runs in the current directory instead of from the root, so the include paths are broken.
What's the correct way to do this?
You can use the -u option to do this. From any subdirectory, scons -u will search upwards in the directory tree for an SConstruct file.

Resources