Is there a way to have scons perform an action that only depends on the existence of the target?
More specifically, I'd like to download a file using wget, but only if it isn't already there. So ideally I'd have some Builder that takes the url as a source and the local filename as the target and then calls something like this:
wget $SOURCE_URL -O $TARGET
Any suggestions?
You can use the SCons Command() builder, as follows:
source_url = 'put url here'
Command(target='local_file_name', source=None, action='wget %s -O $TARGET'%(source_url))
SCons will parse the action string replacing '$TARGET' with the target passed to the Command() builder.
Related
I have a pretty big file structure of a project which I need to convert into a multiplatform cmake project. Now it seams that cmake requires ever single cpp file be added individually to the executable. But is there a script that automates this? That snoopes through the file structure and just adds every source file automatically? Since the project will probably get a lot more source files and I probably wont be able to manually add every single one.
You could use execute_process() with a cmake -P script that uses globbing to recursively scan for source files which writes to an included file in your CMakeLists.txt i.e. something like:
"CMakeLists.txt":
execute_process(COMMAND ${CMAKE_COMMAND}
-D "RDIR=${CMAKE_CURRENT_SOURCE_DIR}"
-P "scansources.cmake"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
include("sources.cmake")
add_executable(myexe ${sources})
"scansources.cmake" (generates "sources.cmake"):
file(GLOB_RECURSE sourcelist
*.c
*.cc
*.cpp
*.cxx)
string(REGEX REPLACE "${RDIR}/" "" relative_sources "${sourcelist}")
string(REPLACE ";" "\n" sources_string "${relative_sources}")
set(sources_string "set(sources\n${sources_string})")
file(WRITE sources.cmake "${sources_string}")
The reason why this works is because execute_process() occurs at configure time.
You could, of course, generate sources.cmake via some other tool or IDE then you wouldn't need scansources.cmake or execute_process().
I currently use a small program to process Qt form (.ui) files and automatically generate classes which have a common base class and use virtual functions to access the form elements.
On windows, I run this tool as a custom build step on the ui form file. The only argument to the tool is the input filename.
To clarify, on Windows, Qt runs uic on the .ui file, creating a ui_filename.h file. I need to run my tool on that file.
How can/should I do this on linux? Ideally I'd build it into the .pro file, but I'm happy to edit the Makefile as well.
I'm not awesome at writing Makefiles so this may be very simple. I am happy to write the command manually for each ui_ or *.ui file but ideally it would happen automatically for all .ui files.
It is not needed to write Makefiles manually. Makefiles that call custom external tool can be generated by qmake from the project file .pro.
It is needed to create a custom target using QMAKE_EXTRA_TARGETS. Then the main target should be set as denendent on that custom target (custom target name should be added to PRE_TARGETDEPS), for example How to modify the PATH variable in Qt Creator's project file (.pro)
The tool should run after generation of the form headers, so the custom target should depend on that file customtarget1.depends = ui_mainwindow.h:
customtarget1.target = form_scanner
customtarget1.commands = tool_win_bat_or_linux_shell.sh
customtarget1.depends = ui_mainwindow.h
QMAKE_EXTRA_TARGETS += customtarget1
PRE_TARGETDEPS += form_scanner
The above qmake commands create the following Makefile rules:
# the form header depends on mainwindow.ui
ui_mainwindow.h: ..\test\mainwindow.ui
<tab>#build command...
# form scanner depends on ui_mainwindow.h
form_scanner: ui_mainwindow.h
<tab>tool_win_bat_or_linux_shell.sh
# the final target depends on form scanner
$(DESTDIR_TARGET): form_scanner ui_mainwindowm.h $(OBJECTS)
If there are many forms it is possible to create many custom targets or create one target that depends on all form files:
for (form, FORMS) {
# autogenerated form headers are located in root of build directory
FILE_NAME = $$basename(form)
# prepend ui_ and replace ending .ui by .h
FORM_HEADERS += ui_$$replace(FILE_NAME, .ui$, .h)
}
customtarget1.target = form_scanner
customtarget1.commands = tool_win_bat_or_linux_shell.sh
customtarget1.depends = $$FORM_HEADERS
QMAKE_EXTRA_TARGETS += customtarget1
PRE_TARGETDEPS += form_scanner
So, the command tool_win_bat_or_linux_shell.sh is executed only when all form headers are generated.
It is also possible to run the shell script from the project directory $$PWD and pass as command line arguments the form header file names:
customtarget1.commands = $$PWD/tool_win_bat_or_linux_shell.sh $$FORM_HEADERS
Now that shell script can run some command for each form header tool_win_bat_or_linux_shell.sh:
# for each command line argument
for file in "$#"
do
echo "$file"
ls -l $file
done
I have several environments in my project.
When I use target target1 to build some binaries and shared libraries.
I want to use target target2 to build a binary and do everything that gets done for target1
I would like to achieve following,
Build and install program1 and library1 for command scons target1
Build and install program1, program2 and library1 for command scons target2
What is the right way achieve this?
The correct way to combine multiple targets under a symbolic name is the Alias() command. Check out the UserGuide at http://scons.org/doc/production/HTML/scons-user.html , especially Chapter 25. "Alias Targets". Note that an Alias is a target node itself, so you can combine multiple defined Aliases #1-#3 into a global Alias like:
alias1 = Alias("alias1", "target1")
alias2 = Alias("alias2", "target2")
alias3 = Alias("alias3", "target3")
Alias("all", [alias1, alias2, alias3])
, where you can also replace each "target" string with a reference to an actual file/dir node as it gets returned by a Builder...
I can use File('foo.bar').abspath to get the location of a file, but if I've got variant_dir set then the returned path will be in variant_dir rather than it's original location. If I have duplicate=0 set, then the file returned won't actually exist.
Obviously SCons knows where the original file is, as it's passed as an argument when the file's actually built (eg gcc -c -o variant/foo.o orig/foo.c).
Is there some sort of File('foo.bar').origpath that I can use?
If it came to it I could use os.path.join(Dir('#').abspath, 'orig') but that requires the SConscript to know which directory it's in, which is messy.
You can use srcnode(). To quote the man page:
The srcnode() method returns another
File or Dir object representing the
source path of the given File or Dir.
This will give you the absolute path in the source directory:
File('foo.bar').srcnode().abspath
I want to inject a "Cleanup" target which depends on a number of other targets finishing before it goes off and gzip's some log files. It's important that I not gzip early as this can cause some of the tools to fail.
How can I inject a cleanup target for Scons to execute?
e.g. I have targets foo and bar. I want to inject a new custom target called 'cleanup' that depends on foo and bar and runs after they're both done, without the user having to specify
% scons foo cleanup
I want them to type:
% scons foo
but have scons execute as though the user had typed
% scons foo cleanup
I've tried creating the cleanup target and appending to sys.argv, but it seems that scons has already processed sys.argv by the time it gets to my code so it doesn't process the 'cleanup' target that I manually append to sys.argv.
you shouldn't use _Add_Targets or undocumented features, you can just add your cleanup target to BUILD_TARGETS:
from SCons.Script import BUILD_TARGETS
BUILD_TARGETS.append('cleanup')
if you use this documented list of targets instead of undocumented functions, scons won't be confused when doing its bookkeeping. This comment block can be found in SCons/Script/__init__.py:
# BUILD_TARGETS can be modified in the SConscript files. If so, we
# want to treat the modified BUILD_TARGETS list as if they specified
# targets on the command line. To do that, though, we need to know if
# BUILD_TARGETS was modified through "official" APIs or by hand. We do
# this by updating two lists in parallel, the documented BUILD_TARGETS
# list, above, and this internal _build_plus_default targets list which
# should only have "official" API changes. Then Script/Main.py can
# compare these two afterwards to figure out if the user added their
# own targets to BUILD_TARGETS.
so I guess it is intended to change BUILD_TARGETS instead of calling internal helper functions
One way is to have the gzip tool depend on the output of the log files. For example, if we have this C file, 'hello.c':
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
And this SConstruct file:
#!/usr/bin/python
env = Environment()
hello = env.Program('hello', 'hello.c')
env.Default(hello)
env.Append(BUILDERS={'CreateLog':
Builder(action='$SOURCE.abspath > $TARGET', suffix='.log')})
log = env.CreateLog('hello', hello)
zipped_log = env.Zip('logs.zip', log)
env.Alias('cleanup', zipped_log)
Then running "scons cleanup" will run the needed steps in the correct order:
gcc -o hello.o -c hello.c
gcc -o hello hello.o
./hello > hello.log
zip(["logs.zip"], ["hello.log"])
This is not quite what you specified, but the only difference between this example and your requirement is that "cleanup" is the step that actually creates the zip file, so that is the step that you have to run. Its dependencies (running the program that generates the log, creating that program) are automatically calculated. You can now add the alias "foo" as follows to get the desired output:
env.Alias('foo', zipped_log)
In version 1.1.0.d20081104 of SCons, you can use the private internal SCons method:
SCons.Script._Add_Targets( [ 'MY_INJECTED_TARGET' ] )
If the user types:
% scons foo bar
The above code snippet will cause SCons to behave as though the user had typed:
% scons foo bar MY_INJECTED_TARGET