1# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2# file Copyright.txt or https://cmake.org/licensing for details.
3
4#[=======================================================================[.rst:
5CheckPIESupported
6-----------------
7
8.. versionadded:: 3.14
9
10Check whether the linker supports Position Independent Code (PIE) or No
11Position Independent Code (NO_PIE) for executables.
12Use this to ensure that the :prop_tgt:`POSITION_INDEPENDENT_CODE` target
13property for executables will be honored at link time.
14
15.. command:: check_pie_supported
16
17  ::
18
19    check_pie_supported([OUTPUT_VARIABLE <output>]
20                        [LANGUAGES <lang>...])
21
22  Options are:
23
24  ``OUTPUT_VARIABLE <output>``
25    Set ``<output>`` variable with details about any error.
26  ``LANGUAGES <lang>...``
27    Check the linkers used for each of the specified languages.
28    Supported languages are ``C``, ``CXX``, and ``Fortran``.
29
30It makes no sense to use this module when :policy:`CMP0083` is set to ``OLD``,
31so the command will return an error in this case.  See policy :policy:`CMP0083`
32for details.
33
34Variables
35^^^^^^^^^
36
37For each language checked, two boolean cache variables are defined.
38
39 ``CMAKE_<lang>_LINK_PIE_SUPPORTED``
40   Set to ``YES`` if ``PIE`` is supported by the linker and ``NO`` otherwise.
41 ``CMAKE_<lang>_LINK_NO_PIE_SUPPORTED``
42   Set to ``YES`` if ``NO_PIE`` is supported by the linker and ``NO`` otherwise.
43
44Examples
45^^^^^^^^
46
47.. code-block:: cmake
48
49  check_pie_supported()
50  set_property(TARGET foo PROPERTY POSITION_INDEPENDENT_CODE TRUE)
51
52.. code-block:: cmake
53
54  # Retrieve any error message.
55  check_pie_supported(OUTPUT_VARIABLE output LANGUAGES C)
56  set_property(TARGET foo PROPERTY POSITION_INDEPENDENT_CODE TRUE)
57  if(NOT CMAKE_C_LINK_PIE_SUPPORTED)
58    message(WARNING "PIE is not supported at link time: ${output}.\n"
59                    "PIE link options will not be passed to linker.")
60  endif()
61
62#]=======================================================================]
63
64
65include (Internal/CMakeTryCompilerOrLinkerFlag)
66
67function (check_pie_supported)
68  cmake_policy(GET CMP0083 cmp0083)
69
70  if (NOT cmp0083)
71    message(FATAL_ERROR "check_pie_supported: Policy CMP0083 is not set")
72  endif()
73
74  if(cmp0083 STREQUAL "OLD")
75    message(FATAL_ERROR "check_pie_supported: Policy CMP0083 set to OLD")
76  endif()
77
78  set(optional)
79  set(one OUTPUT_VARIABLE)
80  set(multiple LANGUAGES)
81
82  cmake_parse_arguments(CHECK_PIE "${optional}" "${one}" "${multiple}" "${ARGN}")
83  if(CHECK_PIE_UNPARSED_ARGUMENTS)
84    message(FATAL_ERROR "check_pie_supported: Unparsed arguments: ${CHECK_PIE_UNPARSED_ARGUMENTS}")
85  endif()
86
87  if (CHECK_PIE_LANGUAGES)
88    set (unsupported_languages "${CHECK_PIE_LANGUAGES}")
89    list (REMOVE_ITEM unsupported_languages "C" "CXX" "Fortran")
90    if(unsupported_languages)
91      message(FATAL_ERROR "check_pie_supported: language(s) '${unsupported_languages}' not supported")
92    endif()
93  else()
94    # User did not set any languages, use defaults
95    get_property (enabled_languages GLOBAL PROPERTY ENABLED_LANGUAGES)
96    if (NOT enabled_languages)
97      return()
98    endif()
99
100    list (FILTER enabled_languages INCLUDE REGEX "^(C|CXX|Fortran)$")
101    if (NOT enabled_languages)
102      return()
103    endif()
104
105    set (CHECK_PIE_LANGUAGES ${enabled_languages})
106  endif()
107
108  set (outputs)
109
110  foreach(lang IN LISTS CHECK_PIE_LANGUAGES)
111    if(_CMAKE_${lang}_PIE_MAY_BE_SUPPORTED_BY_LINKER)
112      cmake_try_compiler_or_linker_flag(${lang}
113                                "${CMAKE_${lang}_LINK_OPTIONS_PIE}"
114                                CMAKE_${lang}_LINK_PIE_SUPPORTED
115                                OUTPUT_VARIABLE output)
116      if (NOT CMAKE_${lang}_LINK_PIE_SUPPORTED)
117        string (APPEND outputs "PIE (${lang}): ${output}\n")
118      endif()
119
120      cmake_try_compiler_or_linker_flag(${lang}
121                                "${CMAKE_${lang}_LINK_OPTIONS_NO_PIE}"
122                                CMAKE_${lang}_LINK_NO_PIE_SUPPORTED
123                                OUTPUT_VARIABLE output)
124      if (NOT CMAKE_${lang}_LINK_NO_PIE_SUPPORTED)
125        string (APPEND outputs "NO_PIE (${lang}): ${output}\n")
126      endif()
127    else()
128      # no support at link time. Set cache variables to NO
129      set(CMAKE_${lang}_LINK_PIE_SUPPORTED NO CACHE INTERNAL "PIE (${lang})")
130      set(CMAKE_${lang}_LINK_NO_PIE_SUPPORTED NO CACHE INTERNAL "NO_PIE (${lang})")
131      string (APPEND outputs "PIE and NO_PIE are not supported by linker for ${lang}")
132    endif()
133  endforeach()
134
135  if (CHECK_PIE_OUTPUT_VARIABLE)
136    set (${CHECK_PIE_OUTPUT_VARIABLE} "${outputs}" PARENT_SCOPE)
137  endif()
138endfunction()
139