SCons : Need an explanation why Install not being call with scons -u - scons

There is something that I don`t understand. See following SConscript :
Import('common_env')
import os
#Grab a copy of the top environment (the one sent by the SConstruct file)
common_env = common_env.Clone()
#Because this component is compiled in both win32 and win64.
if (common_env['ENV']['CONFIG'] == "win32") or (common_env['ENV']['CONFIG'] == "win64"):
#Grabs the library name, the name should look like libpath_of_current_component-(debug/opt)
libName = common_env.libName()
common_env.USE_BOOST()
#Grab all the sources in current dir.
sources = Glob('*.cpp')
#Creates the library
myLib = common_env.Library(libName, sources)
#Install (copy) the library in LINK/lib/winX
common_env.Install('#/LINK/lib/' + common_env['ENV']['CONFIG'], myLib)
If this script is invoke from his sub-folder, I have the following output :
scons: Entering directory `C:\svn\products\faa_mx\scons-test-speed3'
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: building associated VariantDir targets: build\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64
cl /Fobuild\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64\ThinProtocol.obj /c sr\speech\ASRNetworkLayer\ThinProtocol\ThinProtocol.cpp /TP /nologo -Od -D_DEBUG -RTC1 -MDd -Z7 -DBOOST_FILESYSTEM_VERSION=2 -DWIN32 -D_WIN32 -DWINDOWS -D_MBCS -DNOMINMAX -D_MSC_VER=1600 -D_WIN32_WINNT=0x0501 -D_CRT_SECURE_NO_WARNINGS -W3 -nologo -GS -GR -EHa -wd4290 -wd4996 -wd4995 -TP -DBOOST_FILESYSTEM_VERSION=2 /Iinterface\asom\api /Ifwk\simulation_fwk\utils /ILINK\include /Ibuild\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64 /Isr\speech\ASRNetworkLayer\ThinProtocol /IC:\svn\3rdParty\3rdPartyPackages\boost-1.47.0_vs2010_x64\include /Z7
ThinProtocol.cpp
cl /Fobuild\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64\ThinProtocolMessageDefinitions.obj /c sr\speech\ASRNetworkLayer\ThinProtocol\ThinProtocolMessageDefinitions.cpp /TP /nologo -Od -D_DEBUG -RTC1 -MDd -Z7 -DBOOST_FILESYSTEM_VERSION=2 -DWIN32 -D_WIN32 -DWINDOWS -D_MBCS -DNOMINMAX -D_MSC_VER=1600 -D_WIN32_WINNT=0x0501 -D_CRT_SECURE_NO_WARNINGS -W3 -nologo -GS -GR -EHa -wd4290 -wd4996 -wd4995 -TP -DBOOST_FILESYSTEM_VERSION=2 /Iinterface\asom\api /Ifwk\simulation_fwk\utils /ILINK\include /Ibuild\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64 /Isr\speech\ASRNetworkLayer\ThinProtocol /IC:\svn\3rdParty\3rdPartyPackages\boost-1.47.0_vs2010_x64\include /Z7
ThinProtocolMessageDefinitions.cpp
cl /Fobuild\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64\ThinProtocolMessaging.obj /c sr\speech\ASRNetworkLayer\ThinProtocol\ThinProtocolMessaging.cpp /TP /nologo -Od -D_DEBUG -RTC1 -MDd -Z7 -DBOOST_FILESYSTEM_VERSION=2 -DWIN32 -D_WIN32 -DWINDOWS -D_MBCS -DNOMINMAX -D_MSC_VER=1600 -D_WIN32_WINNT=0x0501 -D_CRT_SECURE_NO_WARNINGS -W3 -nologo -GS -GR -EHa -wd4290 -wd4996 -wd4995 -TP -DBOOST_FILESYSTEM_VERSION=2 /Iinterface\asom\api /Ifwk\simulation_fwk\utils /ILINK\include /Ibuild\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64 /Isr\speech\ASRNetworkLayer\ThinProtocol /IC:\svn\3rdParty\3rdPartyPackages\boost-1.47.0_vs2010_x64\include /Z7
ThinProtocolMessaging.cpp
lib /nologo /OUT:build\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64\win64\libsr_speech_ASRNetworkLayer_ThinProtocol-debug.lib build\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64\ThinProtocol.obj build\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64\ThinProtocolMessageDefinitions.obj build\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64\ThinProtocolMessaging.obj
scons: `sr\speech\ASRNetworkLayer\ThinProtocol' is up to date.
scons: done building targets.
As you can see, Install is not being call by scons at all.
If instead of calling scons -u from a sub-folder I call scons from the root, then, I have this :
Install file: "build\debug\sr\speech\ASRNetworkLayer\ThinProtocol\win64\libsr_speech_ASRNetworkLayer_ThinProtocol-debug.lib" as "LINK\lib\win64\libsr_speech_ASRNetworkLayer_ThinProtocol-debug.lib"
My question is : Why this difference? Is it because scons build system, in case of scons -u, knows that nobody needs the .lib, so Install not being called?
Thx!

The default set of files to be built is from the current working directory down.
You can see info on this in the manpage: http://scons.org/doc/production/HTML/scons-man.html
A subset of a hierarchical tree may be built by remaining at the
top-level directory (where the SConstruct file lives) and specifying
the subdirectory as the target to be built:
scons src/subdir
or by changing directory and invoking scons with the -u option, which
traverses up the directory hierarchy until it finds the SConstruct
file, and then builds targets relatively to the current subdirectory:
cd src/subdir
scons -u .
See also this section:
-u, --up, --search-up
Walks up the directory structure until an SConstruct , Sconstruct or sconstruct file is found, and uses that as the top of the directory
tree. If no targets are specified on the command line, only targets at
or below the current directory will be built.
-U
Works exactly the same way as the -u option except for the way default targets are handled. When this option is used and no targets
are specified on the command line, all default targets that are
defined in the SConscript(s) in the current directory are built,
regardless of what directory the resultant targets end up in.
So you can either make that install a Default() target, or specify it as a dependency of some other target in the current working directory.

Related

CMakeList file to generate LLVM bitcode file from C source file

I am trying to generate LLVM bytecode file from a C source file (hello.c) using CMake. And below is my CMakeLists file.
###### CMakelists.txt ############
cmake_minimum_required(VERSION 2.8.9)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_C_FLAGS "-emit-llvm")
project (hello)
add_executable(hello hello.c)
I am new to CMake and not sure if this is the right way. I could not find any rules to make *.bc in the generated MakeFile
. Please correct me here. I also tried "-save-temps"
Considering this for a single .c file. It would be really helpful if you could give me some hints on generating the same for a complete C project.
I think what you ultimately want is to be able to build a C-program
project with CMake and clang in which source files are compiled to LLVM bitcode
and the executable is linked from the bitcode files.
With CMake, asking clang to to link bitcode files means asking it to link in LTO mode,
with the -flto linkage option.
And you can get clang to compile to LLVM bitcode with the -flto compilation
option, or with the -emit-llvm option.
For illustration here is a Hello World project comprising two source files and one header:
$ ls -R
.:
CMakeLists.txt hello.c hello.h main.c
Here is the:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2)
project (hello)
set(CMAKE_C_COMPILER clang)
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-flto")
add_executable(hello main.c hello.c)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
#target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)
It will work equally well with:
#target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -emit-llvm)
Make a build directory for CMake and go there:
$ mkdir build
$ cd build
Generate the build system:
$ cmake ..
Build:
$ make
Scanning dependencies of target hello
[ 33%] Building C object CMakeFiles/hello.dir/main.c.o
[ 66%] Building C object CMakeFiles/hello.dir/hello.c.o
[100%] Linking C executable hello
[100%] Built target hello
You will not find any *.bc targets in the Makefiles, nor any *.bc files
generated:
$ egrep -r '.*\.bc'; echo Done
Done
$ find -name '*.bc'; echo Done
Done
because the compilation option -flto or -emit-llvm results in an output
file:
CMakeFiles/hello.dir/main.c.o
CMakeFiles/hello.dir/hello.c.o
that adheres to the usual CMake naming convention but is in fact not an object file
but an LLVM bitcode file, as you see:
$ file $(find -name '*.o')
./CMakeFiles/hello.dir/hello.c.o: LLVM IR bitcode
./CMakeFiles/hello.dir/main.c.o: LLVM IR bitcode
The program does the usual thing:
$ ./hello
Hello World!
Later
When I try " make hello.o " it should generate the object file right?
the cmd executes successfully but, could not find the generated object file. Am I doing it right?
You are doing it in one way that is right, though not the only way that is right, but
your expectations are wrong. Look again at:
$ file $(find -name '*.o')
./CMakeFiles/hello.dir/hello.c.o: LLVM IR bitcode
./CMakeFiles/hello.dir/main.c.o: LLVM IR bitcode
You can see there that the .o files that are made from hello.c and main.c
by the CMake-generated makefile are not called hello.o and main.o but hello.c.o
and main.c.o. CMake prefers a compiled filename to preserve the extension of the
source file, and append .o. That is a fairly common practice. So if you wanted
to use the makefile to compile hello.c, the most obviously right way would be
make hello.c.o.
Let's see what actually happens. In my CMake build directory:
$ make VERBOSE=1 hello.c.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
make[1]: 'CMakeFiles/hello.dir/hello.c.o' is up to date.
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'
There was nothing to be done, because my hello.c.o was up to date. So I'll
delete it and repeat:
$ rm CMakeFiles/hello.dir/hello.c.o
$ make VERBOSE=1 hello.c.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
Building C object CMakeFiles/hello.dir/hello.c.o
clang -flto -o CMakeFiles/hello.dir/hello.c.o -c /home/imk/develop/so/scrap/hello.c
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'
Now it has been recompiled.
However, because many people - like you - would expect hello.o to be compiled
from hello.c, CMake helpfully defines hello.o as a .PHONY target
that depends on hello.c.o:
$ egrep -A3 'hello.o.*:.*hello.c.o' Makefile
hello.o: hello.c.o
.PHONY : hello.o
So in fact I can do:
$ rm CMakeFiles/hello.dir/hello.c.o
$ make VERBOSE=1 hello.o
make -f CMakeFiles/hello.dir/build.make CMakeFiles/hello.dir/hello.c.o
make[1]: Entering directory '/home/imk/develop/so/scrap/build'
Building C object CMakeFiles/hello.dir/hello.c.o
clang -flto -o CMakeFiles/hello.dir/hello.c.o -c /home/imk/develop/so/scrap/hello.c
make[1]: Leaving directory '/home/imk/develop/so/scrap/build'
make hello.o is another way of making hello.c.o
The problem is that using the -emit-llvm flag does not produce a final binary and stops the configuration tests that CMake performs once that flag is used in them.
Apart from what's already been written about using the LTO infrastructure, you have 3 (or 2 and a half) other alternatives.
One is to use Whole-Program LLVM and use the commands provided to extract the relevant bitcode parts.
The other is to go the manual way of setting up custom targets (see add_custom_target and add_custom_command) on your CMake binary targets, that will get triggered on changes and will reproduce the desired outcome as if executed manually on the command line each time.
Now, on this last point, I had a similar need so I created a CMake project that provides that functionality (llvm-ir-cmake-utils), but allows you to hook up those custom targets on existing ones as you please and see fit without having to rewrite everything from scratch each time.
There are examples in the repo, but in short, it allows you to attach custom targets on already existing CMake targets, e.g.
[...]
add_executable(qux ${SOURCES})
[...]
# this will create a bitcode generating target
# and allow it to depend on the initial target in order to detect source code changes
llvmir_attach_bc_target(qux_bc qux)
add_dependencies(qux_bc qux)
[...]
After make,
$>file CMakeFiles/hello.dir/hello.c.o
CMakeFiles/hello.dir/hello.c.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
if
set(CMAKE_C_FLAGS "-emit-llvm")
written before
project (hello)
In order to obtain IR bitcode, I wrote:
###### CMakelists.txt ############
cmake_minimum_required(VERSION 2.8.9)
project (hello)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_C_FLAGS "-flto")
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-flto")
add_executable(hello hello.c)
target_compile_options(hello PUBLIC ${CMAKE_C_FLAGS} -flto)
I worked several hours in order to have a Makefile working to compile from IR
code to native using lld, then with cmake it was much more faster.
Then reading at cmake generated Makefile, I was able to correct my Makefile:
clang -flto -flto <hello.c.o> ..
this worked but I do not know why -flto is written twice.
Thanl you very much for this post, showing clang as the centralized front end to various llvm provided commands.

scons check for available file without caching result

I'm trying to use scons functions to detect if a specific header file is available. Given a file main.cpp with a #include "fns.h". I've created this:
env = Environment(CXXFLAGS='-I.')
conf = Configure(env)
if not conf.CheckCXXHeader('fns.h'):
print 'please get fns.h'
Exit(1)
env.Program('main.cpp')
The first time I run it, it actually checks for the header, but after that it always uses the cached result
$ scons
scons: Reading SConscript files ...
Checking for C++ header file fns.h... no
please get fns.h
$ touch fns.h
$ scons
scons: Reading SConscript files ...
Checking for C++ header file fns.h... yes
scons: done reading SConscript files.
scons: Building targets ...
g++ -o main.o -c -I. main.cpp
g++ -o main main.o
scons: done building targets.
$ rm fns.h # delete it
$ scons # should check again here, but doesn't
scons: Reading SConscript files ...
Checking for C++ header file fns.h... (cached) yes
scons: done reading SConscript files.
scons: Building targets ...
g++ -o main.o -c -I. main.cpp
main.cpp:1:17: fatal error: fns.h: No such file or directory
#include "fns.h"
Please check the SCons manual (man scons), the command-line option you're looking for is "--config=force". You can also make it the default behavior for your build by setting the config option in an SConscript file:
SetOption('config', 'force')

How to modify a list of filenames in scons?

I use to use this simple Makefile to compile all .cc files in the current directory
SRCS:=$(wildcard *.cc)
OBJS:=$(SRCS:.cc=)
CXX := clang++
CXXFLAGS := -std=c++11 -g
all: $(OBJS)
I'm trying to translate this into an SConstruct file. I can use scons' Glob built-in to get the .cc file list but I don't know how to remove their suffix (like the OBJS := $(SRCS:.cc=) do). Of course I can write Python code to do the modification but does scons has built-in support for this kind of modification?
UPDATE:
My original SConstruct file (literally list all the .cc files)
env = Environment(CXX="clang++", CXXFLAGS=['-std=c++11', '-g'])
env.Program("1.1.cc")
env.Program("1.2.cc")
env.Program("1.3.cc")
env.Program("1.4.cc")
One version that works
import glob
env = Environment(CXX="clang++", CXXFLAGS=['-std=c++11', '-g'])
sources = glob.glob("./*.cc")
for s in sources:
env.Program(s)
Based on your response to my question in the comments above, it appears that you want SCons to automatically create a binary or object file based on the source file.
This can be done in SCons as follows:
env = Environment()
# This will build example.o
env.Object('example.cc')
# This will build main
env.Program('main.cc')
Here is the output from this build:
$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o example.o -c example.cc
g++ -o main.o -c main.cc
g++ -o main main.o
scons: done building targets.
If you need to build a binary with more than one source file, then you will need to specify the binary name, as follows:
Program(target = 'myBinary', source = ['main.cc', 'example.cc'])
This will create main.o and example.o, but cant know the name of the binary/program, so you have to specify it.
As for your example with glob, Scons has a built-in Glob() function, so you could do the following:
env = Environment(CXX="clang++", CXXFLAGS=['-std=c++11', '-g'])
sources = Glob("./*.cc")
for s in sources:
env.Program(s)
The SCons Glob() function is not recursive, so if you need to recursively list files, then you'll need to do it differently.

Scons: Generating version file only if target has changed

I have a requirement to generate version.cc file from SCons Script. This file should be generated only if any of the source file for a target has changed.
Suppose the SCons script has the following statements
#python function which generates version.cc in the same folder where libtest.a is generated. This will always generate a differnt version.cc because the version string contained inside that will have timestamp
GenerateVersionCode()
#target which uses version.cc
libtest = env.Library('test', ['a.cc', 'b.cc', 'version.cc'])
First time when I run the above code everything is fine. But when I run the same script again, the target 'test' will be rebuilt because of new version.cc which got generated.
My requirement is we should not generate new version of version.cc file if the file is already present and there are no changes in any of the sources (namely a.cc and b.cc in this example)
if not version_file_present:
GenerateVersionCode()
else
if no_changes_in_source:
GenerateVersionCode()
#target which uses version.cc which could be newly generated one or previous one
libtest = env.Library('test', ['a.cc', 'b.cc', 'version.cc'])
One related question on this site suggested something as follows
env.Command(target="version.c", source="version-in.c",
action=PythonFunctionToUpdateContents)
env.Program("foo", ["foo.c", "version.c"])
W.r.to the above suggestion I would want to know the contents of function PythonFunctionToUpdateContents which checks the change in source files since previous build.
As I understand it, you only want to generate version.cc if any of the source files change, and you only want to build the library if version.cc changes or if any of the library source files change. That is, consider version.cc as one of the source files for the library.
If this is the case, you could consider 2 sets of dependencies, both of which would be controlled by the SCons dependency checking.
Its not real clear what the version.cc generation consists of, but lets assume that the python function GenerateVersionCode() will do exactly that: generate version.cc, but wont have any dependency checking related logic.
Here is the SConscript code:
def GenerateVersionCode(env, target, source):
# fill in generation code here
# The version.cc checking
env.Command(target='version.cc',
source=['a.cc', 'b.cc'],
action=GenerateVersionCode)
# The library
env.Library(target='test', source=['version.cc', 'a.cc', 'b.cc'])
It shouldnt be necessary, but this could be taken one step further by explicitly setting a dependency from the Library target to the version.cc target with the SCons Depends() function.
Here is the output I get when I build, and instead of using the GenerateVersionCode() function, I use a simple shell script versionGen.sh, thus changing the call to Command() to this:
env.Command(target='version.cc',
source=['a.cc', 'b.cc'],
action='./versionGen.sh')
Here is the first build:
> scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o a.o -c a.cc
g++ -o b.o -c b.cc
./versionGen.sh
g++ -o version.o -c version.cc
ar rc libtest.a version.o a.o b.o
ranlib libtest.a
scons: done building targets.
Then, without having changed anything, I build again, and it does nothing:
> scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
scons: done building targets.
Then, I modify a.cc, and build again, and it generates a new version of version.cc:
> vi a.cc
> scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o a.o -c a.cc
./versionGen.sh
g++ -o version.o -c version.cc
ar rc libtest.a version.o a.o b.o
ranlib libtest.a
scons: done building targets.

scons generating incorrect include path directive in build

I've got a scons build using a simple, common directory setup:
project/
SConstruct
src/
file.cpp
SConscript
include/
namespace/
header.h
In file.cpp, I include header.h via #include "namespace/header.h" so what I want to do is simply add the include directory to the include path list. From the source (and SConscript) point of view, that path is "../include" but the build command always has an invalid path for the include in it. I've tried the following in the SConscript:
env.Append(CPPPATH = ["#include"])
env.Append(CPPPATH = [Dir("include")])
env.Append(CPPPATH = [os.getcwd() + os.sep + ".." + os.sep + "include"])
env.Append(CPPPATH = ["../include"])
env.Append(CPPPATH = ["#../include"])
none of which seem to work. The first four give "-Iinclude" while the last puts the include at the directory level above project! Here's the full SConscript
env = Environment()
import os
target_name = "device"
source_files = Split("""
file.cpp
""")
env.Append(CPPPATH = ["#include", os.environ.get("SYSTEMC_PATH"),
os.environ.get("SYSTEMC_TLM_PATH"), os.environ.get("BOOST_PATH")])
object_list = env.SharedObject(source = source_files)
targetobj = env.SharedLibrary(target = target_name, source = object_list )
Default(targetobj)
And the SConstruct is just:
import sys
SConscript('src/SConscript', variant_dir='Release-'+sys.platform, duplicate=0, exports={'MODE':'release'})
SConscript('src/SConscript', variant_dir='Debug-'+sys.platform, duplicate=0, exports={'MODE':'debug'})
I'm running scons from the directory where the SConstruct is located (i.e. the top level directory).
I've done some looking and according to the scons doco, the # is supposed to tell scons to generate the path from the current directory of the SConscript (which is the src directory) - I'm assuming this is instead of the SConstruct directory??? Further, I can't see any questions out there about this particular problem (on this site or via Google in general), usually I'm just hitting people asking for scons scripts for exactly the setup I've got already (which is to add "include" to the CPPPATH).
Any thoughts on where this is going awry?
'#' is relative to the top-level SConstruct, as per the SCons manual http://scons.org/doc/HTML/scons-user/x3240.html
The scripts you provide above build successfully when I recreate the tree you specify. Here's the working output:
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o Release-cygwin/file.os -c -Iinclude src/file.cpp
g++ -o Release-cygwin/device.dll -shared Release-cygwin/file.os
g++ -o Debug-cygwin/file.os -c -Iinclude src/file.cpp
g++ -o Debug-cygwin/device.dll -shared Debug-cygwin/file.os
scons: done building targets.

Resources