I'm trying to build a complex application with flex lexer using SCons. To build debug/release variants I'm using VariantDir().
The problem is that I can't force lex builder to generate lexer header in variant build directory. It isn't a big problem itself but it forces SCons to think that lexer is always out of date.
SConstruct contents:
VariantDir('build', '.', duplicate=0)
SConscript('build/SConscript')
SConscript contents:
env = Environment(LEXFLAGS='--header-file=scanner.h')
env.Program(target='scanner', source='scanner.l')
Full source to represent this problem is here: https://gist.github.com/g7r/4257f273715cadcf0d36
First scons.py --debug=explain run gives us this:
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: building associated VariantDir targets: build
scons: building `build/scanner.c' because it doesn't exist
flex --header-file=scanner.h -t scanner.l > build/scanner.c
scons: building `build/scanner.o' because it doesn't exist
gcc -o build/scanner.o -c build/scanner.c
scons: building `build/scanner' because it doesn't exist
gcc -o build/scanner build/scanner.o
scons: done building targets.
Second run gives this:
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: building associated VariantDir targets: build
scons: building `build/scanner.h' because it doesn't exist
flex --header-file=scanner.h -t scanner.l > build/scanner.c
scons: done building targets.
So second run rebuilds the lexer because scanner.h was generated in root directory instead of build directory.
Am I doing something wrong? Is there any way to do it right?
Consider combining the call to VariantDir() with the call to SConscript() which usually makes the paths in the build scripts more intuitive. Also, I suspect (unfortunately) the call to VariantDir() is confusing the SCons flex builder.
Here is an example:
SConscript('SConscript', variant_dir='build', duplicate=0)
Chapters 14 and 15 of the SCons user's guide give a good explanation of the SConscript() function.
For anyone hitting this old problem via a search - the problem isn't variant dirs per se, it's the use of the --header-file option and being in a subdirectory: SCons passes LEXFLAGS on as is when flex is called, but also extracts the header file name you asked for and adds it as a target - at which point it's subject to the SCons path interpretation rules, as in "relative to the directory of the SConscript", so flex generates the file one place via the issued command and SCons thinks it's a different place, thus the "always out of date". There's more on this topic at https://github.com/SCons/scons/issues/4154.
Related
I've got a simple SConstruct file as below:
import os,sys
env = Environment()
env.Command([], [], "echo abc")
I expected that this will output "abc", but in fact, nothing extra is printed, the screen output is:
$scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
scons: done building targets.
So how should I fix it? Thanks a lot.
Since you've called a builder with no source and no targets, it's never out of date, and so won't be run.
So you want it to run right away, in which case you should use Execute()
env = Environment()
env.Execute("echo abc")
or just
Execute("echo abc")
The latter yields
$ scons
scons: Reading SConscript files ...
echo abc
abc
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
scons: done building targets.
I have to merge one of my app's libs with the NVIDIA CUDA static lib using this horrific awful CMake code:
GET_TARGET_PROPERTY(OUTPUT_LIB ${LIBNAME} LOCATION)
add_custom_command (TARGET ${LIBNAME}
POST_BUILD
COMMAND mv ${OUTPUT_LIB} ${OUTPUT_LIB}.old
COMMAND echo "create ${OUTPUT_LIB}" > combineLibs.mri
COMMAND echo "addlib ${OUTPUT_LIB}.old" >> combineLibs.mri
COMMAND echo "addlib ${CUDA_LOCATION}" >> combineLibs.mri
COMMAND echo "save" >> combineLibs.mri
COMMAND echo "end" >> combineLibs.mri
COMMAND ar -M <combineLibs.mri
COMMAND rm ${OUTPUT_LIB}.old
COMMENT "Building merged library for ${LIBNAME} at ${OUTPUT_LIB}, including ${CUDA_LOCATION}"
)
target_link_libraries(${LIBNAME} -pthread -c)
This successfully produces a merged static library that has all the symbols in it. However, the NVIDIA CUDA static lib brought with it dependencies on libpthread and libc in the form of unresolved symbols. Now the merged library also has those unresolved symbols, and the target_link_libraries line doesn't seem to do what I seem to think it does, because the symbols don't get resolved at link-time. How do I get the merged static library to dynamically link against libpthread and libc?
The the target_link_libraries line does indeed not do what you think.
target_link_libraries(target,options) can have the desired effect of
adding the linker options options to the linkage of target only if target
is something that is produced by the linker. If no linkage happens in the
production of target then this directive will have no effect.
Your target is a static library. A static library - unlike a program, and unlike
a dynamic/shared library - is not produced by the linker. As your custom_command
in fact illustrates, a static library is produced by the GNU general purpose archiver,
ar. It is nothing but an archive of files which happen to be object files,
but as far as ar is concerned they might as well be the contents of your
Documents, Pictures and Music folders. Since no linkage is involved in the
production of a static library, nothing can be linked with a static library.
An ar archive can be used as a linker input in the linkage of something that
is produced by the linker - a program or a shared library. In that case the
linker will look into the archive to see if contains any object files it needs
to carry on the linkage. If it finds any, it will extract them from the archive
and link them into the program. The linkage will be exactly the same as if
you had listed the required object files in the linker commandline and not
mentioned the archive at all.
But if any of the object files that the linker extracts from an archive bring
with them undefined references, then to get them resolved you must link some
library or libraries that define those references in the linkage of the
program or shared library that you want the linker to produce - just as you
must do to resolve undefined references in any other object files you
input to the linkage.
So,
How do I get the merged static library to dynamically link against libpthread and libc?
You can't. It doesn't make sense. Any library dependencies of object files
in a static library can be satisfied only in the linkage of a program or shared library
that has acquired those dependencies by linking those object files.
Finally, -c is not a GCC linkage option that will have the effect of requesting
linkage of libc. It is not a linkage option at all. It is an option that
directs the GCC frontend not to invoke the linker. It is passed to GCC to
request compilation without linkage, and the perverse effect of including it in a
CMake target_link_libraries directive will be to stop any linkage of the
target from happening.
If you want to explicitly request linkage of libc, use -lc, following
the linker usage protocol that -lname requests linkage of libname.
Perhaps you inferred that -c requests linkage of libc from the assumption
that -pthread requests linkage of libpthread. In fact, -lpthread would
request linkage of libpthread. The option -pthread is a more abstract GCC
option, for both compilation and linkage, that means do the right things, for this platform, to link with the Posix Threads
library - which might entail passing -lpthead to the linker, and possibly not.
Thus -pthread is OK as an argument of target_link_libraries that will
have the effect of requesting Posix Threads linkage, but see
answers to cmake and libpthread
for CMake-proper ways of doing this.
I use Eclipse + ARM plugin to build my projects. When I needed to use in my project the StemWin library, I configured my IDE to use external library.
I set
Preferences -> C/C++ General -> Paths and Symbols
I added in "Library Paths" the link to my folder includes library.
I also added the name of my library in tab "Library".
I checked the settings in the compiler tab and I ascertained all should be good.
When I tried to build my project I got an error from linker:
cannot find -lMyLib.a Hello C/C++ Problem
I double checked the name of my library and link, all are correct. This is the output of my linker:
arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -L"C:\lib"
-T"C:\arm_toolchain\stm32_workspace\Hello\LinkerScript.ld" -Wl,
-Map=output.map -Wl,--gc-sections -o "Hello.elf" #"objects.list" -lMyLib.a
What should I do from here?
I faced the same problem before.
-l:STemWin526_CM4_GCC.a
-L"C:\Edu_Workspace\STM32F4\stm32f4_bsp_template\Drivers\Middlewares\ST\STemWin\Lib"
Above are my working settings.
With -l:<archive file name> the colon : is important for archive file linking.
And -L will contain library path.
Also for stemwin make sure to compile with hardware floating point
-mfloat-abi=hard -mfpu=fpv4-sp-d16
the convention for the -l option of the linker (say you give -lMyLib.a as a linker option) is to search for a library file with "lib" prepended to the given name and .a (or .so) appended, i.e. your command line searches for a file libMyLib.a.{a,so} which is probably not how it's named.
Either you rename your library according to this convention or give it to the linker command line omitting -l (provided your IDE allows you to do so).
Looks like the problem is in -lMyLib.a which means you're trying to link a static library as a dynamic one.
To link a static lib you have to use its path as with ordinary .o files: ... /path/to/MyLib.a
and the resulting command line should look something like
arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -L"C:\lib" -T"C:\arm_toolchain\stm32_workspace\Hello\LinkerScript.ld" -Wl,-Map=output.map -Wl,--gc-sections -o "Hello.elf" #"objects.list" /path/to/MyLib.a
UPDATE:
Although it might fix the issue, turns out it's not true:
-llibrary
-l library
...
Normally the files found this way are library files—archive files whose members are object files. The linker handles an archive file by scanning through it for members which define symbols that have so far been referenced but not defined. But if the file that is found is an ordinary object file, it is linked in the usual fashion. The only difference between using an -l option and specifying a file name is that -l surrounds library with ‘lib’ and ‘.a’ and searches several directories.
(https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html)
I have converted a static library project and an exe project to Scons project in ECLIPSE. These were building fine by Makefile.
But when I am building the static library project it gives an error as below -
=== Running SCons at 18/11/13 8:55 PM ====
Command line: /usr/bin/scons -u --jobs=8
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: building associated VariantDir targets: Debug
o Debug/add.o -c add.cpp
sh: o: not found
ar rc Debug/libadd.a Debug/add.o
ar: Debug/add.o: No such file or directory
scons: *** [Debug/libadd.a] Error 1
scons: building terminated because of errors.
Duration 301 ms.
1) my main objective is to a trigger a build once somehow (in the exe project) and both the project will be compiled in ECLIPSE. Is there a way to see and edit a SConscript in eclipse.
2) How do I do it? By manually writing SConscript and SConstruct file? :(
It looks from the SCons output like SCons can't find the compiler on your system. What OS are you using? And what version of SCons? (Type scons --version to see that.)
Something like this:
cmake --get-variable=MY_CMAKE_VARIABLE
The variable may exist in an included CMake file.
If you have an existing cache file, you can do:
grep MY_CMAKE_VARIABLE CMakeCache.txt
If you do not yet have a cache file and you want to see what options there are in a CMakeLists.txt file, you can do (in a different directory since this will write a cache file):
cmake -L /path/to/CMakeLists.txt | grep MY_CMAKE_VARIABLE
which will return to you something like
<VARIABLE>:<TYPE>=<VALUE>
If it is an advanced variable, add the -A flag to the same command and it will include advanced variables. Of course, if you only want the value, you can do:
cmake -L /path/to/CMakeLists.txt | grep MY_CMAKE_VARIABLE | cut -d "=" -f2
EDIT
For example, with a CMakeLists.txt that is:
cmake_minimum_required(VERSION 2.8)
project(test)
include(otherFile.txt)
set(MY_VAR "Testing" CACHE STRING "")
And where otherFile.txt is:
set(MY_OTHER_VAR "Hi" CACHE STRING "")
The command (run from another directory):
cmake -L ../cmaketest
Gives:
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/tgallagher/cmaketest-build
-- Cache values
CMAKE_BUILD_TYPE:STRING=
CMAKE_INSTALL_PREFIX:PATH=/usr/local
MY_OTHER_VAR:STRING=Hi
MY_VAR:STRING=Testing
So, it does show variables from other files. It should parse the entire build. The issue though is that it will not show any variables that are not marked with CACHE. And it will not show any that are cached INTERNAL, and will only show ADVANCED if -LA is used instead of -L.
If your variables are marked as INTERNAL or not CACHE'd at all, then there is no method within CMake to pull it out. But, non-CACHE'd variables are meant to be transient, so I'm not sure why you would need them outside of a build environment anyway.
Use:
cmake -LA -N /path/to/project
to get a listing of all cache values. The -N is important; it prevents cmake from trying to generate any build files, and just shows you what's in the cache.
If the variable you want is not something you're setting, but something from the defaults, you can use
cmake --system-information
And grep that. Note it does seem to take a second or two which seems kinda slow.
If for example you're trying to do this to configure your cmake vars in the first place, it avoids getting the cart out in front of the horse. :)
You can also pass this a file name. So you can try only generating it if it doesn't exist, and parse the file if it does (to save that 1-2 seconds).
If you need get non cached user variable but can't edit original cmake script, you may resort to a trick. Create new CMakeLists.txt file in another directory with the following content:
include(<Path to original CMakeLists.txt>)
message(STATUS "MY_VAR={${MY_VAR}}")
It is quite possible, cmake will made a lot of errors while running in new directory. Relative paths, if used in original script, is definitely a cause for such errors. But cmake will print last value assigned for your variable. Further, filter all errors and warnings using any well known text processor (assume UNIX familiar), for example:
cmake . | sed -n 's/-- MY_VAR={\(.*\)}/\1/p' 2>&1
I use this approach in projects maintenance scripts, it is reliably as long as original CMakeLists.txt has no syntax errors.
-D <var>:<type>=<value>:
When cmake is first run in an empty build tree, it creates a CMakeCache.txt file and populates it with customizable settings for the project. This option may be used to specify a setting that takes priority over the project's default value. The option may be repeated for as many cache entries as desired.