Scons: how to create a file path by appending two nodes? - scons

I'm trying to write an sconstruct file that will install headers in a destination directory. The intended effect is:
cp include/a.h ../dest/a.h
cp include/b.h ../dest/b.h
Or just as good:
cp include/a.h ../dest/include/a.h
cp include/a.h ../dest/include/b.h
Here's what I have so far:
env = Environment()
for header in Glob("include/*.h"):
env.Command(Dir("../dest").Append(header), header, Copy("$TARGET", "$SOURCE"))
env.Alias("includes", Dir("../dest").Append(header));
This obviously doesn't work because there's no Append function. Glob returns Node objects, and a Dir is also a Node object. I can't figure out how I'm supposed to combine two Node objects into a longer path. Can anyone help?

You don't need to paste those paths together on your own (thanks for describing the actual problem that you're trying to solve). You're looking for the already provided Install() method. Please also check the User Guide, chap 11 "Installing Files in Other Directories: the Install Builder", but a concrete solution should look something like this (from the top of my head):
env = Environment()
includes = env.Install("../dest", Glob("include/*.h"))
env.Alias("includes", includes)
And if you should ever really need this
str(node)
will return the path of the node in question. ;)

Related

CMake: How to execute a command before make install?

This is the way I install the config files:
file(GLOB ConfigFiles ${CMAKE_CURRENT_SOURCE_DIR}/configs/*.xml
${CMAKE_CURRENT_SOURCE_DIR}/configs/*.xsd
${CMAKE_CURRENT_SOURCE_DIR}/configs/*.conf)
install(FILES ${ConfigFiles} DESTINATION ${INSTDIR})
But I need to convert one of the xml files before installing it. There is an executable that can do this job for me:
./Convertor a.xml a-converted.xml
How can I automatically convert the xml file before installing it? It should be a custom command or target that installing depends on it, I just don't know how to make the install command depend on it though. Any advice would be appreciated!
Take a look at the SCRIPT version of install:
The SCRIPT and CODE signature:
install([[SCRIPT <file>] [CODE <code>]] [...])
The SCRIPT form will invoke the given CMake script files during
installation. If the script file name is a relative path it will be
interpreted with respect to the current source directory. The CODE
form will invoke the given CMake code during installation. Code is
specified as a single argument inside a double-quoted string.
For example:
install(CODE "execute_process(\"./Convertor a.xml a-converted.xml\")")
install(FILES a-converted.xml DESTINATION ${INSTDIR})
Be sure to checkout the entry for execute_process in the manual. Also be aware that macro expansion inside the CODE parameter can be a bit tricky to get right. Check the generated cmake_install.cmake in your build directory where the generated code will be placed.
I think that your specific case would work better if you were to use a custom command and target like so:
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/a-converted.xml
COMMAND ./Convertor a.xml a-converted.xml
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Convertor
)
add_custom_target(run ALL
DEPENDS ${CMAKE_BINARY_DIR}/a-converted.xml
COMMENT "Generating a-converted.xml" VERBATIM
)
install(
FILES ${CMAKE_BINARY_DIR}/a-converted.xml
DESTINATION ${INSTDIR}
)
Note: I don't have all the details, so the directories are probably
not exactly what you'd want in your environment, although it's
a good idea to generate files in the ${CMAKE_BINARY_DIR} area.
That way you can be sure that the file a-converted.xml is built at the time you want to install it. Especially, these two rules make sure that if you make changes to the file, it gets recompiled.

Load SCons Tool via absolute path

I have a SCons Tool, which works when I put mytool.py and __init__.py under site_scons/site_tools/mytool.
Now I would like change it to rather be referenced via an absolute path from somewhere else.
So I called it via:
mytoolpath = '/tools/mytool'
env = Environment(tools=['mytool'], toolpath=mytoolpath)
and it excepts with EnvironmentError: No tool named 'mytool': not a Zip file:
mytool.py is located in /tools/mytool so I really do not understand where the problem is. Could someone shed some light.
Turns out this is one of the few places, where strings are not upconverted to lists.
So you have to invoke this via:
env = Environment(tools=['mytool'], toolpath=[mytoolpath])

SCons: Forcing SCons to duplicate files

I have a header-only library consisting of a folder hierarchy and a bunch of .hpp files I'd like to install. My problem is, that scons does not copy the folder into the build folder.
Here is what my directory layout looks like:
root
SConstruct
subdir
SConscript
the_lib
subdir_a
header_a.hpp
subdir_b
header_b.hpp
build
(...)
Here is what I do in subdir/SConscript:
all_headers = []
for root, dirnames, filenames in os.walk('.'):
for filename in fnmatch.filter(filenames, '*.hpp'):
fn = os.path.join(root, filename)
all_headers.append((fn, root))
for f, d in all_headers:
install.AddHeader( f, d )
I do this to get the filenames along with their relative paths and then, I use the installer I found in the scons wiki the other day.
Observation: all_headers remains empty because the the_lib folder does not get copied. I tired subdir_env.Dir('the_lib'), but did not change a thing.
After running the script, I have the_lib/SConscript in my build folder, but nothing else. Of course I can understand that my filesystem walk does nothing in that case.
Can anyone help me?
UPDATE
The only way out I found was to run a find -name "*.hpp" and paste the result into my SConscript. Works like a charm now, but since the library is an external one (and maybe some files are added or removed), I thought of a more generic solution without the need to know all headers by name.
The first thing I thought of was to use the SCons Install() builder, but that is to install actual SCons targets in different locations, and since these header files are not targets, that wont work.
So, in this case, you can use what is called the SCons Copy Factory.
if build is a VariantDir then you don't need to copy the file yourself, scons will do it if the header is used in any Builder.
If you want to a list of the files you can use env.Glob('*/*.hpp') (but wildcards won't traverse directories, so you need to know the depth)

How to force Scons output (exe, obj, lib & dll) to specific build directory?

I've been trying to get scons to output exe, obj, lib and dll files to a specific build directory.
My file structure looks like this:
/projectdir
/build
/bin
/obj
/source
/subdirs
/..
SConstruct
Basically, what I get now is my source directory is getting polluted with obj files. I'd rather have it all in one place.
The SConstruct file looks like this:
env.VariantDir('build', 'source', duplicate = 0)
env.Program('Hierarchy', source = ['source/sconstest.cpp', 'source/utils/IntUtil.cpp'])
The easiest way I've found is to use 2 files, a SConstruct file and a separate SConscript.
In the SConstruct you simply call the other file and specify the directory for the build output:
# content SConstruct
SConscript('main.scons', variant_dir='build', duplicate=0)
Then in 'main.scons' you do the meat of your build. You can forget about variant directories in this file.
# content of main.scons
env = Environment()
env.Program('Hierarchy',
source = ['source/sconstest.cpp', 'source/utils/IntUtil.cpp'])
It's not that tough to get VariantDir working using only one SConstruct file (for a small project), but it's very confusing as the configuration is different for the one-file and two-file use case.
Only SConstruct:
env = Environment()
env.VariantDir('build', 'src', duplicate=0)
files = Glob('build\*.c')
env.Program("build\program", files)
Notice how the source files are located in .\src but .\build is specified as the location. The output has to be also "prefixed" with .\build otherwise the compiled program will reside in the directory of the SConstruct file.
When you execute the script SCons will compile the *.c files from .\src and put the resulting objects to .\build.
No wonder they renamed BuildDir to VariantDir to try to avoid the confusion (without much success).
The VariantDir (also described in the user guide) tells scons to put generated files in a separate directory. In older versions of scons this function was named BuildDir.
You may also want to read up on avoiding duplicating the source directory (described both in the user guide and on the wiki).
I was using a two-file method like richq's answer, but although the final build products (libs, programs) were going into the right variant directory, the object files were still going to the source directory.
The solution turned out to be to glob the source files by relative path instead of absolute. I have no idea why.
My second scons file originally looked like this. Note globbing by absolute path - when I first wrote this I didn't realize paths would automatically be relative to the scons file.
import os, inspect
env = Environment()
packageDir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
src = Glob(os.path.join(packageDir, "src/*/*.c*"), strings=True, source=True)
env.Program('Foo', source = src)
And that resulted in *.obj ending up under src/ and the program under my variant dir. When I changed it to the following, the object files also went to the variant dir:
env = Environment()
src = Glob("src/*/*.c*", strings=True, source=True)
env.Program('Foo', source = src)
Using absolute paths is probably a noob mistake - I'm relatively new to both scons and Python - but I thought I'd share it in case anyone else has the same frustrating problem.

Where to put helper-scripts with GNU autoconf/automake?

I'm working on a project that will be distributed with GNU autoconf/automake, and I have a set of bash scripts which call awk scripts. I would like the bash scripts to end up in the $PATH, but not the awk scripts. How should I insert these into the project? Should they be put in with other binaries?
Also, is there a way to determine the final location of the file after installation? I presume that /usr/local/bin isn't always where the executables end up...
You can just list the scripts that you want to be installed in Makefile.am:
bin_SCRIPTS = foo bar
This will cause foo and bar to be installed during make install. To get the path to their final location, you can use #bindir# in foo.in and let configure build foo for you. For example, in configure.ac:
AC_CONFIG_FILES([foo bar])
and then in foo.in:
#!/bin/sh
prefix=#prefix#
exec_prefix=#exec_prefix#
bindir=#bindir#
echo bindir = $bindir
Keep in mind that the person running configure may specify any of --prefix, --exec_prefix, or
--bindir, and the installation may be redirected with a DESTDIR. Using the technique described here, DESTDIR will not be taken into account and the script will be installed in a location other than the path that it will echo. This is by design, and is the correct behavior, as usually a DESTDIR installation is used to create a tarball that will eventually be unpacked into the filesystem in such a way that the bindir in the script becomes valid.
Add something like this to Makefile.am
scriptsdir = $(prefix)/bin
scripts_DATA = awkscript1 awkscript2
In this case it will install awkscript in $(prefix)/bin (you can also use $(bindir)).
Note: Dont forget that the first should be named name + dir (scripts -> scriptsdir) and the second should be name + _DATA (scripts -> scripts_DATA).
Jonathan, in response to your additional question: if you want to replace the value of prefix at the time of build, you will need to:
rename your script 'myscript' to 'myscript.in'
add a rule to configure.ac to generate it at the bottom
use a macro I made called AS_AC_EXPAND
use it like this:
AS_AC_EXPAND(BINDIR, $bindir)
in your 'myscript.in', you can now use #BINDIR# and it will get expanded to the full path where the script will end up being installed.
Note that you shouldn't use PREFIX directly, any of the installation directories can potentially be changed so you really want to use the value passed to configure for bindir and expand that.
If the awk scripts won't go into the main bin directory (prefix/bin), then you need to place them in an appropriate sub-directory - probably of lib but possibly libexec or share (since the awk scripts are probably platform neutral).
Correct: software won't necessarily end up in /usr/local/bin; on my machine, /usr/local/bin is managed by MIS and all the software I install therefore goes under /usr/gnu/. I use: ./configure --prefix=/usr/gnu to get the software installed where I want it.
You can embed the value of PREFIX in the bash scripts -- effectively, you will 'compile' the scripts to include the install location. Be aware of problems during the build testing - you may need to locate the scripts relative to the current directory first and relative to PREFIX later.

Resources