66

I have a .so library target created by add_library, and need to pass an absolute path to this library to an external script. Now I have ${LIBRARY_OUTPUT_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}LangShared${CMAKE_SHARED_LIBRARY_SUFFIX} for that (LIBRARY_OUTPUT_PATH is defined in my CMakeLists.txt). This looks like hard-coding to me, because it will break as soon as the target is renamed or some of its properties are changed. Is there a way to get an absolute path to add_library's output?

Dmitry Risenberg
  • 2,321
  • 2
  • 18
  • 22

3 Answers3

82

You should use a generator expression for this.

From the docs for add_custom_command and the docs for generator expressions:

Arguments to COMMAND may use "generator expressions"...

Generator expressions are evaluted during build system generation to produce information specific to each build configuration.

In this case, assuming your library target is called "MyLib", the generator expression representing the full path to the built library would be:

$<TARGET_FILE:MyLib>
Fraser
  • 74,704
  • 20
  • 238
  • 215
  • 1
    This is very frustrating because none of the `add_custom_[...]` commands allow you to invoke macros - they can only run external programs. – Nathan Osman Feb 01 '15 at 06:19
  • 3
    Yeah, to invoke a macro you'd need to add it to a standalone CMake script file and invoke CMake itself from the command using the `-P` arg. The downside (apart from being overly complex) is that the macro in the script file doesn't have access to any of the current variables in the main CMake process - you need to pass them too when invoking CMake. So for example `add_custom_command(... COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -P"path/to/script_with_macro.cmake")` – Fraser Feb 01 '15 at 09:58
  • 1
    Or, if used in a cmake-macro, `$` . – Patrick B. Mar 07 '16 at 10:19
  • 1
    Generator expressions only work as arguments to a few functions related to the "generation" step. If you want this during the "configuration" step, generator expressions don't work. See this question: https://stackoverflow.com/questions/56470020/cmake-how-to-get-target-location-for-install-rule – John Zwinck Jun 07 '19 at 04:29
29

Try:

get_property(fancy_lib_location TARGET fancy_lib PROPERTY LOCATION)
message (STATUS "fancy_lib_location == ${fancy_lib_location}")

Where fancy_lib is the target created with add_library (fancy_lib SHARED ...).

I found that works directly with Makefile generators, but there is more work to be done for Visual Studio generators since the value of fancy_lib_location is not what you would expect:

  1. fancy_lib_location will contain an embedded reference to a Visual-Studio-specific $(OutDir) reference that you will have to replace with the value of the CMAKE_BUILD_TYPE CMake variable (which resolves to something like Debug, or Release).
  2. At least for CMake 2.8.1, and at least on Visual Studio targets, and if you have set the CMAKE_DEBUG_POSTFIX variable, then it will not be included in the value (which may or may not be a bug, I don't know).
bgoodr
  • 2,744
  • 1
  • 30
  • 51
  • 2
    For point 1, you have available the per-configuration versions of `LOCATION`, i.e. `LOCATION_DEBUG`, `LOCATION_RELEASE`, etc. This resolves the path fully. For point 2, this seems to be fixed as at v2.8.10 – Fraser Apr 08 '13 at 19:06
  • @Fraser in fact `LOCATION_${CMAKE_BUILD_TYPE}` seems to cover all cases (i.e. even when `${CMAKE_BUILD_TYPE}` is "", it correctly returns the path to the release target). – Ose Apr 30 '13 at 09:33
  • 3
    @Ose Unfortunately, ${CMAKE_BUILD_TYPE} is only supported in make-based generators, so your solution is likely to break when using an IDE like Visual Studio. – ComicSansMS May 08 '13 at 11:40
  • 13
    Policy [CMP0026](http://www.cmake.org/cmake/help/v3.0/policy/CMP0026.htm) in Cmake 3.0 and later) disallows using the LOCATION property in this manner. The generator expression method is preferred. – Mike DeSimone Feb 18 '15 at 21:33
  • @MikeDeSimone The problem with generators is that you can't use variables in the generator name. `$` produces that as literal text. – Qix - MONICA WAS MISTREATED Feb 24 '19 at 06:12
  • @Qix [According to this link, you can use variables.](https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#string-valued-generator-expressions) They give an example: `$<$:-I$>` I'm wondering if you hit a bug or if this is a new feature. – Mike DeSimone Jun 15 '19 at 03:12
  • Seeng as how those are all generator values, I would imagine you can use generators within generators (a generator is `$<...>` as opposed to `${...}`, which is a variable). – Qix - MONICA WAS MISTREATED Jun 16 '19 at 15:52
  • 1
    with cmake v3.15.5, this gives following error: The LOCATION property may not be read from target. Use the target name directly with add_custom_command, or use the generator expression $, as appropriate. – harish Nov 26 '19 at 07:49
  • 2
    @harish I've not used cmake in years and so my answer may now be badly out of date, and thus now "stale". – bgoodr Nov 27 '19 at 01:25
1

To expand on the answer by @bgooddr, here is a CMake function to get the location of a target:

function(get_fancy_lib_location)
    set(options)
    set(multiValueArgs LIB)
    set(oneValueArgs LOCATION)
    cmake_parse_arguments(get_fancy_lib_location "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
    message (STATUS "fancy_lib  == ${get_fancy_lib_location_LIB}")

    get_property(fancy_lib_location TARGET "${get_fancy_lib_location_LIB}" PROPERTY LOCATION)
    message (STATUS "fancy_lib_location == ${fancy_lib_location}")

    set(${get_fancy_lib_location_LOCATION} ${fancy_lib_location})
endfunction()
Blue7
  • 1,750
  • 4
  • 31
  • 55