Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to capture CMake command line arguments?

Tags:

cmake

I want to record the arguments passed to cmake in my generated scripts. E.g., "my-config.in" will be processed by cmake, it has definition like this:

config="@CMAKE_ARGS@"

After cmake, my-config will contain a line something like this:

config="-DLINUX -DUSE_FOO=y -DCMAKE_INSTALL_PREFIX=/usr"

I tried CMAKE_ARGS, CMAKE_OPTIONS, but failed. No documents mention this. :-(

like image 476
Vivodo Avatar asked Apr 18 '12 08:04

Vivodo


People also ask

How do you pass command line arguments in CMake?

So we can understand that you want to set command line for CMake project in the Properties page, right? Create CMake project-> change generator from Ninja to VS 16 2019 generators to generator . sln project in cache folder->open the . sln project->open Properties page to set the command line that you want.

What is CMake command line?

The “cmake” executable is the CMake command-line interface. It may be used to configure projects in scripts. Project configuration settings may be specified on the command line with the -D option. CMake is a cross-platform build system generator.

What does Add_subdirectory do in CMake?

Add a subdirectory to the build. Adds a subdirectory to the build. The source_dir specifies the directory in which the source CMakeLists.

How use CMake command line Linux?

Running CMake from the command line From the command line, cmake can be run as an interactive question and answer session or as a non-interactive program. To run in interactive mode, just pass the option “-i” to cmake. This will cause cmake to ask you to enter a value for each value in the cache file for the project.


3 Answers

I don't know of any variable which provides this information, but you can generate it yourself (with a few provisos).

Any -D arguments passed to CMake are added to the cache file CMakeCache.txt in the build directory and are reapplied during subsequent invocations without having to be specified on the command line again.

So in your example, if you first execute CMake as

cmake ../.. -DCMAKE_INSTALL_PREFIX:PATH=/usr

then you will find that subsequently running simply

cmake .

will still have CMAKE_INSTALL_PREFIX set to /usr


If what you're looking for from CMAKE_ARGS is the full list of variables defined on the command line from every invocation of CMake then the following should do the trick:

get_cmake_property(CACHE_VARS CACHE_VARIABLES)
foreach(CACHE_VAR ${CACHE_VARS})
  get_property(CACHE_VAR_HELPSTRING CACHE ${CACHE_VAR} PROPERTY HELPSTRING)
  if(CACHE_VAR_HELPSTRING STREQUAL "No help, variable specified on the command line.")
    get_property(CACHE_VAR_TYPE CACHE ${CACHE_VAR} PROPERTY TYPE)
    if(CACHE_VAR_TYPE STREQUAL "UNINITIALIZED")
      set(CACHE_VAR_TYPE)
    else()
      set(CACHE_VAR_TYPE :${CACHE_VAR_TYPE})
    endif()
    set(CMAKE_ARGS "${CMAKE_ARGS} -D${CACHE_VAR}${CACHE_VAR_TYPE}=\"${${CACHE_VAR}}\"")
  endif()
endforeach()
message("CMAKE_ARGS: ${CMAKE_ARGS}")

This is a bit fragile as it depends on the fact that each variable which has been set via the command line has the phrase "No help, variable specified on the command line." specified as its HELPSTRING property. If CMake changes this default HELPSTRING, you'd have to update the if statement accordingly.


If this isn't what you want CMAKE_ARGS to show, but instead only the arguments from the current execution, then I don't think there's a way to do that short of hacking CMake's source code! However, I expect this isn't what you want since all the previous command line arguments are effectively re-applied every time.

like image 195
Fraser Avatar answered Oct 05 '22 18:10

Fraser


One way to store CMake command line arguments, is to have a wrapper script called ~/bin/cmake (***1) , which does 2 things:

  • create ./cmake_call.sh that stores the command line arguments
  • call the real cmake executable with the command line arguments

~/bin/cmake # code is shown below

#!/usr/bin/env bash

#
# Place this file into this location: ~/bin/cmake
# (with executable rights)
#
# This is a wrapper for cmake!
# * It calls cmake -- see last line of the script
# It also:
# * Creates a file cmake_call.sh in the current directory (build-directory)
#   which stores the cmake-call with all it's cmake-flags etc.
#   (It also stores successive calls to cmake, so that you have a trace of all your cmake calls)
#
# You can simply reinvoke the last cmake commandline with: ./cmake_call.sh  !!!!!!!!!!
#
# cmake_call.sh is not created
#   when cmake is called without any flags,
#   or when it is called with flags such as --help, -E, -P, etc. (refer to NON_STORE_ARGUMENTS -- you might need to modify it to suit your needs)

SCRIPT_PATH=$(readlink -f "$BASH_SOURCE")
SCRIPT_DIR=$(dirname "$SCRIPT_PATH")

#http://stackoverflow.com/a/13864829
if [ -z ${SUDO_USER+x} ]; then
    # var SUDO_USER is unset
    user=$USER
else
    user=$SUDO_USER
fi


#http://stackoverflow.com/a/34621068
path_append ()  { path_remove $1 $2; export $1="${!1}:$2"; }
path_prepend()  { path_remove $1 $2; export $1="$2:${!1}"; }
path_remove ()  { export $1="`echo -n ${!1} | awk -v RS=: -v ORS=: '$1 != "'$2'"' | sed 's/:$//'`"; }

path_remove PATH ~/bin # when calling cmake (at the bottom of this script), do not invoke this script again!




# when called with no arguments, don't create cmake_call.sh
if [[ -z "$@" ]]; then
   cmake "$@"
   exit
fi


# variable NON_STORE_ARGUMENTS stores flags which, if any are present, cause cmake_call.sh to NOT be created
read -r -d '' NON_STORE_ARGUMENTS <<'EOF'
-E
--build
#-N
-P
--graphviz
--system-information
--debug-trycompile
#--debug-output
--help
-help
-usage
-h
-H
--version
-version
/V
--help-full
--help-manual
--help-manual-list
--help-command
--help-command-list
--help-commands
--help-module
--help-module-list
--help-modules
--help-policy
--help-policy-list
--help-policies
--help-property
--help-property-list
--help-properties
--help-variable
--help-variable-list
--help-variables
EOF

NON_STORE_ARGUMENTS=$(echo "$NON_STORE_ARGUMENTS" | head -c -1 `# remove last newline` | sed "s/^/^/g" `#begin every line with ^` | tr '\n' '|')

#echo "$NON_STORE_ARGUMENTS" ## for debug purposes

## store all the args
ARGS_STR=
for arg in "$@"; do
    if cat <<< "$arg" | grep -E -- "$NON_STORE_ARGUMENTS" &> /dev/null; then  # don't use   echo "$arg"  ....
                                                                              # since echo "-E"    does not do what you want here,
                                                                              # but   cat <<< "-E" does what you want (print minus E)
        # do not create cmake_call.sh
        cmake "$@"
        exit
    fi

    # concatenate to ARGS_STR
    ARGS_STR="${ARGS_STR}$(echo -n " \"$arg\"" | sed "s,\($(pwd)\)\(\([/ \t,:;'\"].*\)\?\)$,\$(pwd)\2,g")"
    #                                             replace $(pwd) followed by
    #                                                                  /             or
    #                                                                   whitespace   or
    #                                                                      ,         or
    #                                                                       :        or
    #                                                                        ;       or
    #                                                                         '      or
    #                                                                           "
    #                                                                    or nothing
    #                                             with \$(pwd)
done









if [[ ! -e $(pwd)/cmake_call.sh ]]; then
echo    "#!/usr/bin/env bash" >  $(pwd)/cmake_call.sh

# escaping:
# note in the HEREDOC below, \\ means \ in the output!!
#                            \$ means $ in the output!!
#                            \` means ` in the output!!
cat <<EOF                     >>  $(pwd)/cmake_call.sh


#http://stackoverflow.com/a/34621068
path_remove ()  { export \$1="\`echo -n \${!1} | awk -v RS=: -v ORS=: '\$1 != "'\$2'"' | sed 's/:\$//'\`"; }

path_remove PATH ~/bin # when calling cmake (at the bottom of this script), do not invoke ~/bin/cmake but real cmake!


EOF
else
    # remove bottom 2 lines from cmake_call.sh
    sed -i '$ d' $(pwd)/cmake_call.sh
    sed -i '$ d' $(pwd)/cmake_call.sh
fi


echo "ARGS='${ARGS_STR}'" >> $(pwd)/cmake_call.sh
echo "echo cmake \"\$ARGS\""  >> $(pwd)/cmake_call.sh
echo "eval cmake \"\$ARGS\""  >> $(pwd)/cmake_call.sh
#echo "eval which cmake"       >> $(pwd)/cmake_call.sh

chmod +x     $(pwd)/cmake_call.sh
chown $user: $(pwd)/cmake_call.sh

cmake "$@"

Usage:

mkdir build
cd    build
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$(pwd)/install ..

This will create cmake_call.sh with the following content:

#!/usr/bin/env bash


#http://stackoverflow.com/a/34621068
path_remove ()  { export $1="`echo -n ${!1} | awk -v RS=: -v ORS=: '$1 != "'$2'"' | sed 's/:$//'`"; }

path_remove PATH ~/bin # when calling cmake (at the bottom of this script), do not invoke ~/bin/cmake but real cmake!


ARGS=' "-DCMAKE_BUILD_TYPE=Debug" "-DCMAKE_INSTALL_PREFIX=$(pwd)/install" ".."'
echo cmake "$ARGS"
eval cmake "$ARGS"

The 3rd last line stores the cmake arguments. You can now reinvoke the exact command-line that you used by simply calling:

./cmake_call.sh

Footnotes:

(***1) ~/bin/cmake is usually in the PATH because of ~/.profile. When creating ~/bin/cmake the very 1st time, it might be necessary to log out and back in, so that .profile sees ~/bin.

like image 25
ajneu Avatar answered Oct 05 '22 17:10

ajneu


A very Linux specific way of achieving the same objective:

if(${CMAKE_SYSTEM_NAME} STREQUAL Linux)
    file(STRINGS /proc/self/status _cmake_process_status)

    # Grab the PID of the parent process
    string(REGEX MATCH "PPid:[ \t]*([0-9]*)" _ ${_cmake_process_status})

    # Grab the absolute path of the parent process
    file(READ_SYMLINK /proc/${CMAKE_MATCH_1}/exe _cmake_parent_process_path)

    # Compute CMake arguments only if CMake was not invoked by the native build
    # system, to avoid dropping user specified options on re-triggers.
    if(NOT ${_cmake_parent_process_path} STREQUAL ${CMAKE_MAKE_PROGRAM})
        execute_process(COMMAND bash -c "tr '\\0' ' ' < /proc/$PPID/cmdline"
                        OUTPUT_VARIABLE _cmake_args)

        string(STRIP "${_cmake_args}" _cmake_args)

        set(CMAKE_ARGS "${_cmake_args}"
            CACHE STRING "CMake command line args (set by end user)" FORCE)
    endif()
    message(STATUS "User Specified CMake Arguments: ${CMAKE_ARGS}")
endif()
like image 1
Nehal J Wani Avatar answered Oct 05 '22 17:10

Nehal J Wani