cmake : Set environment variables from a script - linux

I have a script that sets all variables needed for the cross-compilation. Here is just part of it :
export CONFIG_SITE=~/workspace/eldk-5.4/powerpc/site-config-powerpc-linux
export CC="powerpc-linux-gcc -m32 -mhard-float --sysroot=~/workspace/eldk-5.4/powerpc/sysroots/powerpc-linux"
export CXX="powerpc-linux-g++ -m32 -mhard-float --sysroot=~/workspace/eldk-5.4/powerpc/sysroots/powerpc-linux"
export CPP="powerpc-linux-gcc -E -m32 -mhard-float --sysroot=~/workspace/eldk-5.4/powerpc/sysroots/powerpc-linux"
export AS="powerpc-linux-as "
export LD="powerpc-linux-ld --sysroot=~/workspace/eldk-5.4/powerpc/sysroots/powerpc-linux"
export GDB=powerpc-linux-gdb
If I do source environment-setup-powerpc-linux, all environment variables are imported into the current shell session, and I can compile my example.
Is it possible to import these variables in cmake? If yes, how?
A bit more details :
I am using ELDK v 5.4, and it's install script generates a script which sets all environment variables
I found this tutorial, which explains how to manually set for cross-compilation, but not how to use the script, which sets everything
if I call the script before setting cmake, all works fine, and I can cross-compile, but I'd like that cmake calls the script

Reading through the cmake quick start, you can specify variable on a command line:
cmake -DVARIABLE1=value1 -DVARIABLE2=value2 ...
Otherwise, set command in the cmake script is probably what you want, see the reference manual. To set the environment variable PATH, do:
set(ENV{PATH} "/home/martink")
To set normal variable, do:
set(variable "value")
Not sure which ones you have to set, probably the environment ones.
That said, setting environment variable prior to calling cmake is often the easiest solution to solve the problem. If you want a cross-platform way to do this that doesn't depend on the syntax of a specific shell to set environment variables, there is the cmake -E env command.

The only way to set a compiler and flags to do cross-compilation reliably with CMake is with a toolchain-file as done in the tutorial you have found.
When we faced the same issue you have (a toolkit which produces a script so set the compile-environment) we changed the toolkit in a way that it produces a toolchain-file along with the script.
In reality a cmake-toolchain-file does not change that often. The basic flags used for the target are fixed quite early in a project - normally. And with CMake's CMAKE_BUILD_TYPE you can switch between Debug and Release compilations without changing the toolchain-file.
If you have different targets to support, create different toolchain and use the out-of-source-build with CMake.
EDIT: One thing you could do is to invoke cmake with the -D-argument setting the variables you want to and having sourced your script before:
source environment-setup-powerpc-linux
cmake -DCMAKE_C_COMPILER=$CC -DCMAKE_CXX_COMPILER=$CXX etc
The result will be identical as to having used a toolchain-file.

Related

arm-none-eabi-gcc cross compilation with CLion [duplicate]

I'm using CLion IDE, Cmake and trying to write Hello world using CERN ROOT library.
CMakeLists.txt:
message(STATUS $ENV{ROOTSYS})
~/.bashrc:
export ROOTSYS="$HOME/tools/root-build/"
During build in CLion $ENV{ROOTSYS} is empty by some reason. But $ENV{PATH} returns correct $PATH.
What I did wrong?
Variables from .bashrc aren't passed.
Go to File -> Settings -> Build,Execution,Deployment
For Clion 2017.2+
For old Clion
Click Pass system and...
If you want to read environment variable in C++ runtime e.g. using std::getenv then
it won't work as we added environment variable for CMAKE not for runtime.
You can add such variable:
And then in your code:
std::filesystem::path getRootConfigPath()
{
// std::getenv can return nullptr and this is why we CAN'T assign it directly to std::string
const char* path = std::getenv("TEST_CONFIG_DIR");
gcpp::exception::fail_if_true(
path == nullptr, WHERE_IN_FILE, "No such environment variable: ${TEST_CONFIG_DIR}");
gcpp::exception::fail_if_true(std::string_view{path}.empty(),
WHERE_IN_FILE,
"Missing ${TEST_CONFIG_DIR} environment variable");
const std::filesystem::path testConfigDir{path};
gcpp::exception::fail_if_false(std::filesystem::exists(testConfigDir) &&
std::filesystem::is_directory(testConfigDir),
WHERE_IN_FILE,
"Invalid ${TEST_CONFIG_DIR} dir:" + testConfigDir.string());
return testConfigDir;
}
Source of gcpp::exception::fail_if_true
Other way to do this in more friendly way when running unit tests is add this variable to template.
So whenever you click:
Such variable will be there already.
From CLion developers FAQ:
Q: How to pass environment variables and parameters to CMake in CLion?
A: The best way is to use Preferences/Settings | Build, Execution, Deployment | CMake dialog.
As for .bashrc file, it is only used by bash. CLion doesn't need to use bash for run configuration process.
On Ubuntu 17.04, you can set a permanent environment variable by modifying
/etc/enviornment
[I assume you can do this in other versions of Linux, but I provide the version of the system that I am using.]
For example, I am compiling test cases that assume that ${GOOGLE_MOCK} has been set. I added the following to my /etc/environment file, and now I don't have to rewrite all of my CMakeLists.txt files:
GOOGLE_MOCK=/usr/local/src/googletest/googlemock
GOOGLE_TEST_HOME=/usr/local/src/googletest/googletest
Clion just became much more useable.
One thing you can check is the .gdbinit. Clion on Linux will invoke the gdb, which will read in the .gdbinit. I happen to have set environment LD_LIBRARY_PATH xxx in my .gdbinit file, which will override whatever you set LD_LIBRARY_PATH from shell, whether through direct export or through .bashrc, or from CLion environment variable panel.
Hope this helps.
Source your variables in .profile not .bashrc

Run script using Qt Creator like environment

I try to run a deployment script using KDE Neon. I have started the script manually from bash and I got an error qmake would be not found.
When trying to run qmake directly from the bash I get the following error:
$ qmake -v
qmake: could not exec '/usr/lib/x86_64-linux-gnu/qt4/bin/qmake': No such file or directory
That bin folder is almost empty and contains no qmake. In the path /usr/lib/x86_64-linux-gnu/qt5/bin/ a full Qt installation can be found but no qmake as well. /usr/bin/qmake obviously is a link that is pointing to the missing /usr/lib/x86_64-linux-gnu/qt4/bin/qmake.
qmake works fine when using Qt Creator. The binary used by Qt Creator is /home/<user>/Qt/5.10.0/gcc_64/bin/qmake.
Obviously the /usr/... Qt installations aren't complete. First I thought about how to complete the installations and how to switch from Qt4 to Qt5. However Qt Creator obviously is able to use its own /home/... located Qt environment anyway and I would like to use it too when running a script outside Qt Creator.
Is there a way I can start scripts from bash using the same Qt environment as Qt Creator (without modifying the OS configuration)?
QtCreator itself only modifies the environemt, i.e. it sets
export PATH="/home/<user>/Qt/5.10.0/gcc_64/bin:$PATH"
export QTDIR="/home/<user>/Qt/5.10.0/gcc_64"
This can be verified by checking the "Build Environment" section in the "Projects" tab. When checking the environment for the run configuration there is one more that is needed for the compiled applications to find the correct .so files (The build env. is used to run build tools. The run env. is used to run the compiled application).
export LD_LIBRARY_PATH="/home/<user>/Qt/5.10.0/gcc_64/lib:$LD_LIBRARY_PATH"
So in order to easily use qmake/... from the command line, just create a script that sets these (and possibly others you defined for your build in QtCreator) and source it before compiling and it should work.
source ~/qt-home-init.sh
qmake ...

How to manage the environment PATH used by the Android NDK build?

I use swig from my within Android.mk. I reference it directly relying on the $PATH variable to contain the path to it's executable.
The issue is that the $PATH variable is different depending on where the android-ndk tool is run from.
Background
In my Android.mk file:
# some stuff ...
#echo "$(PATH)"
swig # swig parameters here...
# more stuff ...
From the terminal, we see the system path includes the path to swig:
which swig
/usr/local/bin/swig
echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:
Issue
When the android-ndk is run from within Android Studio I get the following for PATH (note: it's missing the swig path):
/usr/bin:/bin:/usr/sbin:/sbin
This causes my build to fail because "swig" isn't recognised:
make: swig: Command not found
However, if I run the android-ndk directly from the terminal, then the PATH used is the same as my system path and the build works fine:
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:
Bad solution
Yes, I could add the path manually to my Android.mk file:
export PATH:=/usr/local/bin/:$(PATH)
However, I'd prefer not to define specific paths for my machine knowing that the whole team uses this file.
Question
I'd prefer the Android.mk file to use my systems PATH instead.
Any ideas how to do this?
Thanks!
The thing in my opinion is that environment variables may be different whether the process is launched from terminal or from the GUI (launchpad, spotlight, ...).
The former would use PATH as set in bashrc (or other shell related startup files) while the other will not.
I think you might find interesting information on how to change the environment variables for the launch deamon in the following question:
Setting environment variables in OS X?

Set linker search path for build in CMake

It seems this question has been asked very often before but none of the solutions seem to apply in my case.
I'm in a CMake/Linux environment and have to run an executable binary during the build step (protoc in particular).
This binary needs a library but it's not installed (and cannot be) in the in the standard directories like /usr, so the library cannot be found.
Unfortunately I cannot manipulate the protoc call because it's embedded in a 3rd party script.
I can now set LD_LIBRARY_PATH before every make or set it system wide but this is very inconvenient especially when it comes to IDEs in which the build takes place or distributed build scenarios with continuous build environments.
I tried to set LD_LIBRARY_PATH via
set(ENV{LD_LIBRARY_PATH} "/path/to/library/dir")
but this seems to have no effect during the build step.
So my question is: can I set a library search path in CMake which is used during the build?
Try this
SET(ENV{LD_LIBRARY_PATH} "/path/to/library/dir:$ENV{LD_LIBRARY_PATH}")
I also used this dirty trick to temporary change some environment variables:
LD_LIBRARY_PATH="/path/to/library/dir:$LD_LIBRARY_PATH" cmake ...
After execution of this line LD_LIBRARY_PATH is not changed in the current shell.
Also, I do not find it bad to change LD_LIBRARY_PATH before invoking cmake:
export LD_LIBRARY_PATH=...
It won't change anything system-wide, but it would be used for your current shell, current build process. The same holds for CI builds. You can save the variable and restore it after cmake invocation:
MY_LD=$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=...
cmake...
export LD_LIBRARY_PATH=$MY_LD
I have recently run into a somewhat similar problem.
My solution was to incorporate sourcing a file that set the appropriate environment into every command.
For example, this custom command:
add_custom_command(
OUTPUT some_output
COMMAND some_command
ARGS some_args
DEPENDS some_dependencies
COMMENT "Running some_command some_args to produce some_output"
)
Would become:
set(my_some_command_with_environment "source my_environment_script.sh && some_command")
add_custom_command(
OUTPUT some_output
COMMAND bash
ARGS -c "${my_some_command_with_environment} some_args"
DEPENDS some_dependencies
COMMENT "Running some_command some_args to produce some_output"
VERBATIM
)
Obviously, this has some disadvantages:
It relies on a bash shell being available.
It sources the environment script for every command invocation (performance issue) and you will have to change all invocations of commands that rely on that environment variables.
It changes the normal syntax of having the command follow COMMAND and the arguments follow ARGS, as now the 'real' command is part of the ARGS.
My CMake-Fu has proven insufficient to find a syntactically nicer way of doing this, but maybe somebody can comment a nicer way.
I had a similar issue for an executable provided by a third party library. The binary was linked against a library not provided by the distribution but the required library was included in the libs directory of the third party library.
So running LD_LIBRARY_PATH=/path/to/thirdparty/lib /path/to/thirdparty/bin/executable worked. But the package config script didn't set up the executable to search /path/to/thirdparty/lib for the runtime dependent so CMake would complain when CMake tried to run the executable.
I got around this by configuring a bootstrap script and replacing the IMPORTED_LOCATION property with the configured bootstrapping script.
_thirdpartyExe.in
#!/bin/bash
LD_LIBRARY_PATH=#_thirdpartyLibs# #_thirdpartyExe_LOCATION# "$#"
CMakeLists.txt
find_package(ThirdPartyLib)
get_target_property(_component ThirdPartyLib::component LOCATION)
get_filename_component(_thirdpartyLibs ${_component} DIRECTORY)
get_target_property(_thirdpartyExe_LOCATION ThirdPartyLib::exe IMPORTED_LOCATION)
configure_file(
${CMAKE_CURRENT_LIST_DIR} _thirdpartyExe.in
${CMAKE_BINARY_DIR}/thirdpartyExeWrapper #ONLY
)
set_target_properties(ThirdPartyLib::exe PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/thirdpartyExeWrapper)
Honestly I view this as a hack and temporary stop gap until I fix the third party library itself. But as far as I've tried this seems to work on all the IDE's I've thrown at it, Eclipse, VSCode, Ninja, QtCreator, ... etc

Run from clean login shell without credentials

So I've got an executable with conflicting dependencies with the build system it is running in. (AKA Xilinx doesn't play well with others). I'd love to run a cmake script as part of the build process, but it is depended on different dlls. I can try to figure out a version of cmake that matches the xylinx dlls. Xylinx redirects the dependencies to the different C++ runtimes by setting a bunch of environment variables.
Sanitize environment with command or bash script?
Will completely clean out all environments, but what i really want to do is run the script after the .bash_rc or .bash_profile is called. (cmake seems to require some of that to be set, as env -i cmake returns an error) I'd like to do it without having to require the login credentials. Is that possible?
You can pass environment variables directly in a command line, right before the executable name:
ENV_VAR=foo command -options ...
If you want to pass multiple variables just add them one after one:
ENV_VAR_FOO=foo ENV_VAR_BAR=bar command -options ...
In your case, you'll need to find out which environment variable cmake needs to access the libraries. Then pass this to the call, like:
LIB_PATH=/path/to/libs cmake -options ...

Resources