소스 검색

Merge topic 'pie-link-options'

c4b4d8b3a6 POSITION_INDEPENDENT_CODE: Manage link flags for executables
724a0346f7 POSITION_INDEPENDENT_CODE: Fix erroneous '-fPIE' flag for Sun Studio
023188ffb4 INTERFACE_POSITION_INDEPENDENT_CODE: add generator expressions support

Acked-by: Kitware Robot <[email protected]>
Merge-request: !2465
Craig Scott 7 년 전
부모
커밋
ddb967cca1
54개의 변경된 파일768개의 추가작업 그리고 30개의 파일을 삭제
  1. 1 0
      Help/manual/cmake-policies.7.rst
  2. 24 0
      Help/policy/CMP0083.rst
  3. 6 0
      Help/prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE.rst
  4. 5 0
      Help/release/dev/INTERFACE_POSITION_INDEPENDENT_CODE.rst
  5. 6 0
      Help/release/dev/link-option-PIE.rst
  6. 6 1
      Modules/CMakeCXXInformation.cmake
  7. 6 0
      Modules/CMakeFortranInformation.cmake
  8. 3 0
      Modules/Compiler/AppleClang-C.cmake
  9. 3 0
      Modules/Compiler/AppleClang-CXX.cmake
  10. 20 0
      Modules/Compiler/Clang.cmake
  11. 16 0
      Modules/Compiler/GNU.cmake
  12. 3 0
      Modules/Compiler/SunPro-C.cmake
  13. 3 0
      Modules/Compiler/SunPro-CXX.cmake
  14. 3 0
      Modules/Compiler/SunPro-Fortran.cmake
  15. 146 0
      Modules/Internal/CMakeCheckCompilerFlag.cmake
  16. 0 4
      Modules/Platform/Android/abi-common.cmake
  17. 2 0
      Modules/Platform/CYGWIN-GNU.cmake
  18. 1 0
      Modules/Platform/FreeBSD.cmake
  19. 2 0
      Modules/Platform/Fuchsia.cmake
  20. 4 0
      Modules/Platform/Linux-Intel.cmake
  21. 2 0
      Modules/Platform/Linux-PGI.cmake
  22. 1 0
      Modules/Platform/NetBSD.cmake
  23. 2 0
      Modules/Platform/SINIX.cmake
  24. 2 0
      Modules/Platform/UNIX_SV.cmake
  25. 2 0
      Modules/Platform/UnixWare.cmake
  26. 2 0
      Modules/Platform/Windows-GNU.cmake
  27. 12 0
      Source/cmGeneratorExpressionDAGChecker.cxx
  28. 1 0
      Source/cmGeneratorExpressionDAGChecker.h
  29. 2 1
      Source/cmGeneratorExpressionNode.cxx
  30. 150 21
      Source/cmGeneratorTarget.cxx
  31. 5 0
      Source/cmGeneratorTarget.h
  32. 23 0
      Source/cmGlobalXCodeGenerator.cxx
  33. 3 0
      Source/cmGlobalXCodeGenerator.h
  34. 32 0
      Source/cmLocalGenerator.cxx
  35. 4 0
      Source/cmLocalGenerator.h
  36. 3 0
      Source/cmMakefileTargetGenerator.cxx
  37. 5 2
      Source/cmPolicies.h
  38. 2 1
      Tests/RunCMake/CMakeLists.txt
  39. 22 0
      Tests/RunCMake/PositionIndependentCode/CMP0083-cmp0083_new-check.cmake
  40. 20 0
      Tests/RunCMake/PositionIndependentCode/CMP0083-cmp0083_old-check.cmake
  41. 45 0
      Tests/RunCMake/PositionIndependentCode/CMP0083.cmake
  42. 12 0
      Tests/RunCMake/PositionIndependentCode/CheckPIESupported.cmake
  43. 1 0
      Tests/RunCMake/PositionIndependentCode/Genex1-result.txt
  44. 3 0
      Tests/RunCMake/PositionIndependentCode/Genex1-stderr.txt
  45. 9 0
      Tests/RunCMake/PositionIndependentCode/Genex1.cmake
  46. 1 0
      Tests/RunCMake/PositionIndependentCode/Genex2-result.txt
  47. 3 0
      Tests/RunCMake/PositionIndependentCode/Genex2-stderr.txt
  48. 9 0
      Tests/RunCMake/PositionIndependentCode/Genex2.cmake
  49. 7 0
      Tests/RunCMake/PositionIndependentCode/PIE-pie_off-check.cmake
  50. 7 0
      Tests/RunCMake/PositionIndependentCode/PIE-pie_on-check.cmake
  51. 19 0
      Tests/RunCMake/PositionIndependentCode/PIE.cmake
  52. 32 0
      Tests/RunCMake/PositionIndependentCode/PIE_validator.cmake
  53. 64 0
      Tests/RunCMake/PositionIndependentCode/RunCMakeTest.cmake
  54. 1 0
      Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt

+ 1 - 0
Help/manual/cmake-policies.7.rst

@@ -57,6 +57,7 @@ Policies Introduced by CMake 3.14
 .. toctree::
    :maxdepth: 1
 
+   CMP0083: Add PIE options when linking executable. </policy/CMP0083>
    CMP0082: Install rules from add_subdirectory() are interleaved with those in caller. </policy/CMP0082>
 
 Policies Introduced by CMake 3.13

+ 24 - 0
Help/policy/CMP0083.rst

@@ -0,0 +1,24 @@
+CMP0083
+-------
+
+To control generation of Position Independent Executable (``PIE``) or not, some
+flags are required at link time.
+
+CMake 3.13 and lower did not add these link flags when
+:prop_tgt:`POSITION_INDEPENDENT_CODE` is set.
+
+The ``OLD`` behavior for this policy is to not manage ``PIE`` link flags. The
+``NEW`` behavior is to add link flags if :prop_tgt:`POSITION_INDEPENDENT_CODE`
+is set:
+
+* Set to ``TRUE``: flags to produce a position independent executable are
+  passed to the linker step. For example ``-pie`` for ``GCC``.
+* Set to ``FALSE``: flags not to produce a position independent executable are
+  passed to the linker step. For example ``-no-pie`` for ``GCC``.
+* Not set: no flags are passed to the linker step.
+
+This policy was introduced in CMake version 3.14.  CMake version
+|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
+the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+
+.. include:: DEPRECATED.txt

+ 6 - 0
Help/prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE.rst

@@ -14,3 +14,9 @@ undefined, then consumers will determine their
 :prop_tgt:`POSITION_INDEPENDENT_CODE` property by other means.  Consumers
 must ensure that the targets that they link to have a consistent
 requirement for their ``INTERFACE_POSITION_INDEPENDENT_CODE`` property.
+
+Contents of ``INTERFACE_POSITION_INDEPENDENT_CODE`` may use
+"generator expressions" with the syntax ``$<...>``.  See the
+:manual:`cmake-generator-expressions(7)` manual for available expressions.
+See the :manual:`cmake-buildsystem(7)` manual for more on defining buildsystem
+properties.

+ 5 - 0
Help/release/dev/INTERFACE_POSITION_INDEPENDENT_CODE.rst

@@ -0,0 +1,5 @@
+INTERFACE_POSITION_INDEPENDENT_CODE
+-----------------------------------
+
+* :prop_tgt:`INTERFACE_POSITION_INDEPENDENT_CODE` target property gains the
+  support of :manual:`generator expressions <cmake-generator-expressions(7)>`.

+ 6 - 0
Help/release/dev/link-option-PIE.rst

@@ -0,0 +1,6 @@
+link-option-PIE
+---------------
+
+* Required link options to manage Position Independent Executable are now
+  added when :prop_tgt:`POSITION_INDEPENDENT_CODE` is set. These flags are
+  controlled by policy :policy:`CMP0083`.

+ 6 - 1
Modules/CMakeCXXInformation.cmake

@@ -105,6 +105,12 @@ endif()
 if(NOT CMAKE_CXX_COMPILE_OPTIONS_PIE)
   set(CMAKE_CXX_COMPILE_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE})
 endif()
+if(NOT CMAKE_CXX_LINK_OPTIONS_PIE)
+  set(CMAKE_CXX_LINK_OPTIONS_PIE ${CMAKE_C_LINK_OPTIONS_PIE})
+endif()
+if(NOT CMAKE_CXX_LINK_OPTIONS_NO_PIE)
+  set(CMAKE_CXX_LINK_OPTIONS_NO_PIE ${CMAKE_C_LINK_OPTIONS_NO_PIE})
+endif()
 
 if(NOT CMAKE_CXX_COMPILE_OPTIONS_DLL)
   set(CMAKE_CXX_COMPILE_OPTIONS_DLL ${CMAKE_C_COMPILE_OPTIONS_DLL})
@@ -269,4 +275,3 @@ CMAKE_VERBOSE_MAKEFILE
 )
 
 set(CMAKE_CXX_INFORMATION_LOADED 1)
-

+ 6 - 0
Modules/CMakeFortranInformation.cmake

@@ -74,6 +74,12 @@ endif()
 if(NOT CMAKE_Fortran_COMPILE_OPTIONS_PIE)
   set(CMAKE_Fortran_COMPILE_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE})
 endif()
+if(NOT CMAKE_Fortran_LINK_OPTIONS_PIE)
+  set(CMAKE_Fortran_LINK_OPTIONS_PIE ${CMAKE_C_LINK_OPTIONS_PIE})
+endif()
+if(NOT CMAKE_Fortran_LINK_OPTIONS_NO_PIE)
+  set(CMAKE_Fortran_LINK_OPTIONS_NO_PIE ${CMAKE_C_LINK_OPTIONS_NO_PIE})
+endif()
 
 if(NOT CMAKE_Fortran_COMPILE_OPTIONS_DLL)
   set(CMAKE_Fortran_COMPILE_OPTIONS_DLL ${CMAKE_C_COMPILE_OPTIONS_DLL})

+ 3 - 0
Modules/Compiler/AppleClang-C.cmake

@@ -1,6 +1,9 @@
 include(Compiler/Clang)
 __compiler_clang(C)
 
+set(CMAKE_C_LINK_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE} -Xlinker -pie)
+set(CMAKE_C_LINK_OPTIONS_NO_PIE -Xlinker -no_pie)
+
 if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.0)
   set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90")
   set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu90")

+ 3 - 0
Modules/Compiler/AppleClang-CXX.cmake

@@ -1,6 +1,9 @@
 include(Compiler/Clang)
 __compiler_clang(CXX)
 
+set(CMAKE_CXX_LINK_OPTIONS_PIE ${CMAKE_CXX_COMPILE_OPTIONS_PIE} -Xlinker -pie)
+set(CMAKE_CXX_LINK_OPTIONS_NO_PIE -Xlinker -no_pie)
+
 if(NOT "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
   set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden")
 endif()

+ 20 - 0
Modules/Compiler/Clang.cmake

@@ -21,6 +21,26 @@ else()
   macro(__compiler_clang lang)
     __compiler_gnu(${lang})
     set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "-fPIE")
+    # Link options for PIE are already set in 'Compiler/GNU.cmake'
+    # but clang may require alternate syntax on some platforms
+    if (NOT CMAKE_${lang}_FLAG_PIE)
+      cmake_check_compiler_flag(${lang} "${CMAKE_${lang}_COMPILE_OPTIONS_PIE};-Xlinker;-pie"
+                                CMAKE_${lang}_FLAG_XLINKER_PIE)
+      if (CMAKE_${lang}_FLAG_XLINKER_PIE)
+        set(CMAKE_${lang}_LINK_OPTIONS_PIE ${CMAKE_${lang}_COMPILE_OPTIONS_PIE} "-Xlinker" "-pie")
+      else()
+        set(CMAKE_${lang}_LINK_OPTIONS_PIE "")
+      endif()
+    endif()
+    if (NOT CMAKE_${lang}_FLAG_NO_PIE)
+      cmake_check_compiler_flag(${lang} "-Xlinker;-no_pie"
+                                CMAKE_${lang}_FLAG_XLINKER_NO_PIE)
+      if (CMAKE_${lang}_FLAG_XLINKER_NO_PIE)
+        set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "-Xlinker" "-no_pie")
+      else()
+        set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "")
+      endif()
+    endif()
     set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ")
     set(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY "-fvisibility=")
     if(CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.4.0)

+ 16 - 0
Modules/Compiler/GNU.cmake

@@ -9,6 +9,7 @@ endif()
 set(__COMPILER_GNU 1)
 
 include(Compiler/CMakeCommonCompilerMacros)
+include(Internal/CMakeCheckCompilerFlag)
 
 macro(__compiler_gnu lang)
   # Feature flags.
@@ -16,6 +17,21 @@ macro(__compiler_gnu lang)
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "-fPIC")
   if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.4)
     set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "-fPIE")
+    # Support of PIE at link stage depends on various elements : platform, compiler, linker
+    # so the easiest way is to check if compiler supports these flags
+    cmake_check_compiler_flag(${lang} "${CMAKE_${lang}_COMPILE_OPTIONS_PIE};-pie"
+                              CMAKE_${lang}_FLAG_PIE)
+    if (CMAKE_${lang}_FLAG_PIE)
+      set(CMAKE_${lang}_LINK_OPTIONS_PIE ${CMAKE_${lang}_COMPILE_OPTIONS_PIE} "-pie")
+    else()
+      set(CMAKE_${lang}_LINK_OPTIONS_PIE "")
+    endif()
+    cmake_check_compiler_flag(${lang} "-no-pie" CMAKE_${lang}_FLAG_NO_PIE)
+    if (CMAKE_${lang}_FLAG_NO_PIE)
+      set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "-no-pie")
+    else()
+      set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "")
+    endif()
   endif()
   if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.0)
     set(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY "-fvisibility=")

+ 3 - 0
Modules/Compiler/SunPro-C.cmake

@@ -6,6 +6,9 @@ include(Compiler/SunPro)
 set(CMAKE_C_VERBOSE_FLAG "-#")
 
 set(CMAKE_C_COMPILE_OPTIONS_PIC -KPIC)
+set(CMAKE_C_COMPILE_OPTIONS_PIE "")
+set(CMAKE_C_LINK_OPTIONS_PIE "")
+set(CMAKE_C_LINK_OPTIONS_NO_PIE "")
 set(CMAKE_SHARED_LIBRARY_C_FLAGS "-KPIC")
 set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-G")
 set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-R")

+ 3 - 0
Modules/Compiler/SunPro-CXX.cmake

@@ -6,6 +6,9 @@ include(Compiler/SunPro)
 set(CMAKE_CXX_VERBOSE_FLAG "-v")
 
 set(CMAKE_CXX_COMPILE_OPTIONS_PIC -KPIC)
+set(CMAKE_CXX_COMPILE_OPTIONS_PIE "")
+set(CMAKE_CXX_LINK_OPTIONS_PIE "")
+set(CMAKE_CXX_LINK_OPTIONS_NO_PIE "")
 set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "-KPIC")
 set(CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS "-G")
 set(CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG "-R")

+ 3 - 0
Modules/Compiler/SunPro-Fortran.cmake

@@ -3,6 +3,9 @@ set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-fixed")
 set(CMAKE_Fortran_FORMAT_FREE_FLAG "-free")
 
 set(CMAKE_Fortran_COMPILE_OPTIONS_PIC "-KPIC")
+set(CMAKE_Fortran_COMPILE_OPTIONS_PIE "")
+set(CMAKE_Fortran_LINK_OPTIONS_PIE "")
+set(CMAKE_Fortran_LINK_OPTIONS_NO_PIE "")
 set(CMAKE_SHARED_LIBRARY_Fortran_FLAGS "-KPIC")
 set(CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS "-G")
 set(CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG "-R")

+ 146 - 0
Modules/Internal/CMakeCheckCompilerFlag.cmake

@@ -0,0 +1,146 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=[
+
+NOTE: This function is used internally by CMake. Projects should not include
+      this file directly.
+
+The cmake_check_compiler_flag() function can be used to compile and link a
+source file to check whether a specific compiler or linker flag is supported.
+The function does not use the try_compile() command so as to avoid infinite
+recursion.  It may not work for all platforms or toolchains, the caller is
+responsible for ensuring it is only called in valid situations.
+
+Parameters:
+  lang   - Language to check.
+  flag   - The flag to add to the compile/link command line.
+  result - Boolean output variable.  It will be stored in the cache as an
+           internal variable and if true, will cause future tests that assign
+           to that variable to be bypassed.
+
+Optional parameters:
+  SRC_EXT         - Overrides the extension of the source file used for the
+                    check.  Defaults are 'c' (C), 'cxx' (CXX), 'F' (Fortran).
+  COMMAND_PATTERN - Pattern to be used for the command line. The default is
+                    '<FLAG> -o <OUTPUT> <SOURCE>'
+  FAIL_REGEX      - List of additional regular expressions that, if matched by
+                    the output, give a failed result for the check.  A common
+                    set of regular expressions will be included in addition to
+                    those given by FAIL_REGEX.
+
+#]=]
+
+include_guard(GLOBAL)
+include(CMakeCheckCompilerFlagCommonPatterns)
+
+function(CMAKE_CHECK_COMPILER_FLAG lang flag result)
+  # Cache results between runs similar to check_<lang>_source_compiles()
+  if(DEFINED ${result})
+    return()
+  endif()
+
+  set(comment "Is the '${flag}' option(s) supported")
+  string(REPLACE ";" " " comment "${comment}")
+
+  if (NOT lang MATCHES "^(C|CXX|Fortran|ASM)$")
+    # other possible languages are not supported
+    # log message to keep trace of this problem...
+    file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+      "Function 'CMAKE_CHECK_COMPILER_FLAG' called with unsupported language: ${lang}\n")
+    set(${result} FALSE CACHE INTERNAL ${comment})
+    return()
+  endif()
+  if (lang STREQUAL "ASM")
+    # assume ASM compiler is a multi-language compiler, so supports C language as well
+    set(check_lang C)
+  else()
+    set(check_lang ${lang})
+  endif()
+
+  cmake_parse_arguments(CCCF "" "SRC_EXT;COMMAND_PATTERN" "FAIL_REGEX" ${ARGN})
+
+  if (NOT CCCF_COMMAND_PATTERN)
+    set (CCCF_COMMAND_PATTERN "<FLAG> -o <OUTPUT> <SOURCE>")
+  endif()
+
+  list (APPEND CCCF_FAIL_REGEX "argument unused during compilation") # clang
+  if (check_lang STREQUAL "C")
+    list(APPEND CCCF_FAIL_REGEX
+      "command line option .* is valid for .* but not for C") # GNU
+  elseif(check_lang STREQUAL "CXX")
+    list(APPEND CCCF_FAIL_REGEX
+      "command line option .* is valid for .* but not for C\\+\\+") # GNU
+  elseif(check_lang STREQUAL "Fortran")
+    list(APPEND CCCF_FAIL_REGEX
+      "command line option .* is valid for .* but not for Fortran") # GNU
+  endif()
+
+  # Add patterns for common errors
+  check_compiler_flag_common_patterns(COMPILER_FLAG_COMMON_PATTERNS)
+  foreach(arg IN LISTS COMPILER_FLAG_COMMON_PATTERNS)
+    if(arg MATCHES "^FAIL_REGEX$")
+      continue()
+    endif()
+    list(APPEND CCCF_FAIL_REGEX "${arg}")
+  endforeach()
+
+  if(NOT CCCF_SRC_EXT)
+    if (check_lang STREQUAL "C")
+      set(CCCF_SRC_EXT c)
+    elseif(check_lang STREQUAL "CXX")
+      set(CCCF_SRC_EXT cxx)
+    elseif(check_lang STREQUAL "Fortran")
+      set(CCCF_SRC_EXT F)
+    endif()
+  endif()
+
+  # Compute the directory in which to run the test.
+  set(COMPILER_FLAG_DIR "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp")
+  # Compute source and output files.
+  set(COMPILER_FLAG_SRC
+    "${COMPILER_FLAG_DIR}/CompilerFlag${lang}.${CCCF_SRC_EXT}")
+  if(check_lang STREQUAL "Fortran")
+    file(WRITE "${COMPILER_FLAG_SRC}"
+      "      program simple\n      end program simple\n")
+  else()
+    file(WRITE "${COMPILER_FLAG_SRC}" "int main (void)\n{ return 0; }\n")
+  endif()
+  get_filename_component(COMPILER_FLAG_EXE "${COMPILER_FLAG_SRC}" NAME_WE)
+  string(APPEND COMPILER_FLAG_EXE "${CMAKE_EXECUTABLE_SUFFIX}")
+
+  # Build command line
+  separate_arguments(CCCF_COMMAND_PATTERN UNIX_COMMAND
+    "${CCCF_COMMAND_PATTERN}")
+  list(TRANSFORM CCCF_COMMAND_PATTERN REPLACE "<SOURCE>" "${COMPILER_FLAG_SRC}")
+  list(TRANSFORM CCCF_COMMAND_PATTERN REPLACE "<OUTPUT>" "${COMPILER_FLAG_EXE}")
+  list(TRANSFORM CCCF_COMMAND_PATTERN REPLACE "<FLAG>" "${flag}")
+
+  execute_process(
+    COMMAND "${CMAKE_COMMAND}" -E env LC_ALL=C LC_MESSAGES=C LANG=C
+            "${CMAKE_${lang}_COMPILER}" ${CCCF_COMMAND_PATTERN}
+    WORKING_DIRECTORY "${COMPILER_FLAG_DIR}"
+    OUTPUT_VARIABLE COMPILER_FLAG_OUTPUT
+    ERROR_VARIABLE COMPILER_FLAG_OUTPUT
+    RESULT_VARIABLE COMPILER_FLAG_RESULT)
+
+  # Record result in the cache so we can avoid re-testing every CMake run
+  if (COMPILER_FLAG_RESULT)
+    set(${result} FALSE CACHE INTERNAL ${comment})
+  else()
+    foreach(regex IN LISTS CCCF_FAIL_REGEX)
+      if(COMPILER_FLAG_OUTPUT MATCHES "${regex}")
+        set(${result} FALSE CACHE INTERNAL ${comment})
+      endif()
+    endforeach()
+  endif()
+  if (DEFINED ${result})
+    file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+        "Determining if the ${flag} option "
+        "is supported for ${lang} language failed with the following output:\n"
+        "${COMPILER_FLAG_OUTPUT}\n")
+    return()
+  endif()
+
+  set(${result} TRUE CACHE INTERNAL ${comment})
+endfunction()

+ 0 - 4
Modules/Platform/Android/abi-common.cmake

@@ -12,10 +12,6 @@ if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE
   set(CMAKE_POSITION_INDEPENDENT_CODE ON)
 endif()
 
-if(CMAKE_POSITION_INDEPENDENT_CODE)
-  string(APPEND _ANDROID_ABI_INIT_EXE_LDFLAGS " -fPIE -pie")
-endif()
-
 string(APPEND _ANDROID_ABI_INIT_EXE_LDFLAGS " -Wl,--gc-sections")
 
 if(NOT _ANDROID_ABI_INIT_EXE_LDFLAGS_NO_nocopyreloc)

+ 2 - 0
Modules/Platform/CYGWIN-GNU.cmake

@@ -27,6 +27,8 @@ macro(__cygwin_compiler_gnu lang)
    # No -fPIC on cygwin
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "")
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "")
+  set(CMAKE_${lang}_LINK_OPTIONS_PIE "")
+  set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "")
   set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "")
 
   # Initialize C link type selection flags.  These flags are used when

+ 1 - 0
Modules/Platform/FreeBSD.cmake

@@ -1,6 +1,7 @@
 set(CMAKE_DL_LIBS "")
 set(CMAKE_C_COMPILE_OPTIONS_PIC "-fPIC")
 set(CMAKE_C_COMPILE_OPTIONS_PIE "-fPIE")
+# PIE link options are managed in Compiler/<compiler>.cmake file
 set(CMAKE_SHARED_LIBRARY_C_FLAGS "-fPIC")            # -pic
 set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared")       # -shared
 set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")         # +s, flag for exe link to use shared lib

+ 2 - 0
Modules/Platform/Fuchsia.cmake

@@ -3,6 +3,8 @@ set(FUCHSIA 1)
 set(CMAKE_DL_LIBS "")
 set(CMAKE_C_COMPILE_OPTIONS_PIC "-fPIC")
 set(CMAKE_C_COMPILE_OPTIONS_PIE "-fPIE")
+set(CMAKE_C_LINK_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE} "-pie")
+set(CMAKE_C_LINK_OPTIONS_NO_PIE "-no-pie")
 set(CMAKE_SHARED_LIBRARY_C_FLAGS "-fPIC")
 set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared")
 set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,")

+ 4 - 0
Modules/Platform/Linux-Intel.cmake

@@ -23,6 +23,10 @@ endif()
 macro(__linux_compiler_intel lang)
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "-fPIC")
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "-fPIE")
+  if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 13.0)
+    set(CMAKE_${lang}_LINK_OPTIONS_PIE ${CMAKE_${lang}_COMPILE_OPTIONS_PIE} "-pie")
+    set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "-no-pie")
+  endif()
   set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "-fPIC")
   set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared")
 

+ 2 - 0
Modules/Platform/Linux-PGI.cmake

@@ -12,6 +12,8 @@ macro(__linux_compiler_pgi lang)
   # Shared library compile and link flags.
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "-fPIC")
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "")
+  set(CMAKE_${lang}_LINK_OPTIONS_PIE "")
+  set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "")
   set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "-fPIC")
   set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared")
   set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS " ")

+ 1 - 0
Modules/Platform/NetBSD.cmake

@@ -1,6 +1,7 @@
 set(CMAKE_DL_LIBS "")
 set(CMAKE_C_COMPILE_OPTIONS_PIC "-fPIC")
 set(CMAKE_C_COMPILE_OPTIONS_PIE "-fPIE")
+# PIE link options are managed in Compiler/<compiler>.cmake file
 set(CMAKE_SHARED_LIBRARY_C_FLAGS "-fPIC")            # -pic
 set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared")       # -shared
 set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")         # +s, flag for exe link to use shared lib

+ 2 - 0
Modules/Platform/SINIX.cmake

@@ -1,4 +1,6 @@
 set(CMAKE_C_COMPILE_OPTIONS_PIC -K PIC)
 set(CMAKE_C_COMPILE_OPTIONS_PIE "")
+set(CMAKE_C_LINK_OPTIONS_PIE "")
+set(CMAKE_C_LINK_OPTIONS_NO_PIE "")
 set(CMAKE_SHARED_LIBRARY_C_FLAGS "-K PIC")
 include(Platform/UnixPaths)

+ 2 - 0
Modules/Platform/UNIX_SV.cmake

@@ -1,5 +1,7 @@
 set(CMAKE_C_COMPILE_OPTIONS_PIC -K PIC)
 set(CMAKE_C_COMPILE_OPTIONS_PIE "")
+set(CMAKE_C_LINK_OPTIONS_PIE "")
+set(CMAKE_C_LINK_OPTIONS_NO_PIE "")
 set(CMAKE_SHARED_LIBRARY_C_FLAGS "-K PIC")
 set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-Wl,-Bexport")
 include(Platform/UnixPaths)

+ 2 - 0
Modules/Platform/UnixWare.cmake

@@ -1,5 +1,7 @@
 set(CMAKE_C_COMPILE_OPTIONS_PIC -K PIC)
 set(CMAKE_C_COMPILE_OPTIONS_PIE "")
+set(CMAKE_C_LINK_OPTIONS_PIE "")
+set(CMAKE_C_LINK_OPTIONS_NO_PIE "")
 set(CMAKE_SHARED_LIBRARY_C_FLAGS "-K PIC")
 set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-Wl,-Bexport")
 include(Platform/UnixPaths)

+ 2 - 0
Modules/Platform/Windows-GNU.cmake

@@ -72,6 +72,8 @@ macro(__windows_compiler_gnu lang)
   # No -fPIC on Windows
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "")
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "")
+  set(CMAKE_${lang}_LINK_OPTIONS_PIE "")
+  set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "")
   set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "")
 
   set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS ${__WINDOWS_GNU_LD_RESPONSE})

+ 12 - 0
Source/cmGeneratorExpressionDAGChecker.cxx

@@ -166,6 +166,18 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingGenexExpression()
   return top->Property == "TARGET_GENEX_EVAL" || top->Property == "GENEX_EVAL";
 }
 
+bool cmGeneratorExpressionDAGChecker::EvaluatingPICExpression()
+{
+  const cmGeneratorExpressionDAGChecker* top = this;
+  const cmGeneratorExpressionDAGChecker* parent = this->Parent;
+  while (parent) {
+    top = parent;
+    parent = parent->Parent;
+  }
+
+  return top->Property == "INTERFACE_POSITION_INDEPENDENT_CODE";
+}
+
 bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
   cmGeneratorTarget const* tgt)
 {

+ 1 - 0
Source/cmGeneratorExpressionDAGChecker.h

@@ -66,6 +66,7 @@ struct cmGeneratorExpressionDAGChecker
                    const std::string& expr);
 
   bool EvaluatingGenexExpression();
+  bool EvaluatingPICExpression();
   bool EvaluatingLinkLibraries(cmGeneratorTarget const* tgt = nullptr);
 
 #define DECLARE_TRANSITIVE_PROPERTY_METHOD(METHOD) bool METHOD() const;

+ 2 - 1
Source/cmGeneratorExpressionNode.cxx

@@ -1225,7 +1225,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
     const char* prop = target->GetProperty(propertyName);
 
     if (dagCheckerParent) {
-      if (dagCheckerParent->EvaluatingGenexExpression()) {
+      if (dagCheckerParent->EvaluatingGenexExpression() ||
+          dagCheckerParent->EvaluatingPICExpression()) {
         // No check required.
       } else if (dagCheckerParent->EvaluatingLinkLibraries()) {
 #define TRANSITIVE_PROPERTY_COMPARE(PROPERTY)                                 \

+ 150 - 21
Source/cmGeneratorTarget.cxx

@@ -495,6 +495,36 @@ const char* cmGeneratorTarget::GetFeature(const std::string& feature,
   return this->LocalGenerator->GetFeature(feature, config);
 }
 
+const char* cmGeneratorTarget::GetLinkPIEProperty(
+  const std::string& config) const
+{
+  static std::string PICValue;
+
+  PICValue = this->GetLinkInterfaceDependentStringAsBoolProperty(
+    "POSITION_INDEPENDENT_CODE", config);
+
+  if (PICValue == "(unset)") {
+    // POSITION_INDEPENDENT_CODE is not set
+    return nullptr;
+  }
+
+  switch (this->GetPolicyStatusCMP0083()) {
+    case cmPolicies::WARN: {
+      std::ostringstream e;
+      e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0083);
+      this->LocalGenerator->IssueMessage(cmake::AUTHOR_WARNING, e.str());
+      CM_FALLTHROUGH;
+    }
+    case cmPolicies::OLD:
+      return nullptr;
+    default:
+      // nothing to do
+      break;
+  }
+
+  return PICValue.c_str();
+}
+
 bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang,
                                      std::string const& config) const
 {
@@ -4237,6 +4267,29 @@ void cmGeneratorTarget::CheckPropertyCompatibility(
   }
 }
 
+template <typename PropertyType>
+std::string valueAsString(PropertyType);
+template <>
+std::string valueAsString<bool>(bool value)
+{
+  return value ? "TRUE" : "FALSE";
+}
+template <>
+std::string valueAsString<const char*>(const char* value)
+{
+  return value ? value : "(unset)";
+}
+template <>
+std::string valueAsString<std::string>(std::string value)
+{
+  return value;
+}
+template <>
+std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/)
+{
+  return "(unset)";
+}
+
 std::string compatibilityType(CompatibleType t)
 {
   switch (t) {
@@ -4268,34 +4321,49 @@ std::string compatibilityAgree(CompatibleType t, bool dominant)
 }
 
 template <typename PropertyType>
-PropertyType getTypedProperty(cmGeneratorTarget const* tgt,
-                              const std::string& prop);
+PropertyType getTypedProperty(
+  cmGeneratorTarget const* tgt, const std::string& prop,
+  cmGeneratorExpressionInterpreter* genexInterpreter = nullptr);
 
 template <>
 bool getTypedProperty<bool>(cmGeneratorTarget const* tgt,
-                            const std::string& prop)
+                            const std::string& prop,
+                            cmGeneratorExpressionInterpreter* genexInterpreter)
 {
-  return tgt->GetPropertyAsBool(prop);
-}
+  if (genexInterpreter == nullptr) {
+    return tgt->GetPropertyAsBool(prop);
+  }
 
-template <>
-const char* getTypedProperty<const char*>(cmGeneratorTarget const* tgt,
-                                          const std::string& prop)
-{
-  return tgt->GetProperty(prop);
+  const char* value = tgt->GetProperty(prop);
+  return cmSystemTools::IsOn(genexInterpreter->Evaluate(value, prop));
 }
 
-template <typename PropertyType>
-std::string valueAsString(PropertyType);
 template <>
-std::string valueAsString<bool>(bool value)
+const char* getTypedProperty<const char*>(
+  cmGeneratorTarget const* tgt, const std::string& prop,
+  cmGeneratorExpressionInterpreter* genexInterpreter)
 {
-  return value ? "TRUE" : "FALSE";
+  const char* value = tgt->GetProperty(prop);
+
+  if (genexInterpreter == nullptr) {
+    return value;
+  }
+
+  return genexInterpreter->Evaluate(value, prop).c_str();
 }
+
 template <>
-std::string valueAsString<const char*>(const char* value)
+std::string getTypedProperty<std::string>(
+  cmGeneratorTarget const* tgt, const std::string& prop,
+  cmGeneratorExpressionInterpreter* genexInterpreter)
 {
-  return value ? value : "(unset)";
+  const char* value = tgt->GetProperty(prop);
+
+  if (genexInterpreter == nullptr) {
+    return valueAsString(value);
+  }
+
+  return genexInterpreter->Evaluate(value, prop);
 }
 
 template <typename PropertyType>
@@ -4310,6 +4378,12 @@ const char* impliedValue<const char*>(const char* /*unused*/)
 {
   return "";
 }
+template <>
+std::string impliedValue<std::string>(
+  std::string /*unused*/) // NOLINT(clang-tidy)
+{
+  return std::string();
+}
 
 template <typename PropertyType>
 std::pair<bool, PropertyType> consistentProperty(PropertyType lhs,
@@ -4330,6 +4404,13 @@ std::pair<bool, const char*> consistentStringProperty(const char* lhs,
   return std::make_pair(b, b ? lhs : nullptr);
 }
 
+std::pair<bool, std::string> consistentStringProperty(const std::string& lhs,
+                                                      const std::string& rhs)
+{
+  const bool b = lhs == rhs;
+  return std::make_pair(b, b ? lhs : valueAsString(nullptr));
+}
+
 std::pair<bool, const char*> consistentNumberProperty(const char* lhs,
                                                       const char* rhs,
                                                       CompatibleType t)
@@ -4372,9 +4453,10 @@ std::pair<bool, const char*> consistentProperty(const char* lhs,
   const char* const null_ptr = nullptr;
 
   switch (t) {
-    case BoolType:
-      assert(false && "consistentProperty for strings called with BoolType");
-      return std::pair<bool, const char*>(false, null_ptr);
+    case BoolType: {
+      bool same = cmSystemTools::IsOn(lhs) == cmSystemTools::IsOn(rhs);
+      return std::make_pair(same, same ? lhs : nullptr);
+    }
     case StringType:
       return consistentStringProperty(lhs, rhs);
     case NumberMinType:
@@ -4385,6 +4467,40 @@ std::pair<bool, const char*> consistentProperty(const char* lhs,
   return std::pair<bool, const char*>(false, null_ptr);
 }
 
+std::pair<bool, std::string> consistentProperty(const std::string& lhs,
+                                                const std::string& rhs,
+                                                CompatibleType t)
+{
+  const std::string null_ptr = valueAsString(nullptr);
+
+  if (lhs == null_ptr && rhs == null_ptr) {
+    return std::make_pair(true, lhs);
+  }
+  if (lhs == null_ptr) {
+    return std::make_pair(true, rhs);
+  }
+  if (rhs == null_ptr) {
+    return std::make_pair(true, lhs);
+  }
+
+  switch (t) {
+    case BoolType: {
+      bool same = cmSystemTools::IsOn(lhs) == cmSystemTools::IsOn(rhs);
+      return std::make_pair(same, same ? lhs : null_ptr);
+    }
+    case StringType:
+      return consistentStringProperty(lhs, rhs);
+    case NumberMinType:
+    case NumberMaxType: {
+      auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t);
+      return std::make_pair(
+        value.first, value.first ? std::string(value.second) : null_ptr);
+    }
+  }
+  assert(false && "Unreachable!");
+  return std::pair<bool, std::string>(false, null_ptr);
+}
+
 template <typename PropertyType>
 PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt,
                                                  const std::string& p,
@@ -4394,6 +4510,7 @@ PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt,
                                                  PropertyType* /*unused*/)
 {
   PropertyType propContent = getTypedProperty<PropertyType>(tgt, p);
+
   std::vector<std::string> headPropKeys = tgt->GetPropertyKeys();
   const bool explicitlySet =
     std::find(headPropKeys.begin(), headPropKeys.end(), p) !=
@@ -4423,6 +4540,11 @@ PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt,
   }
 
   std::string interfaceProperty = "INTERFACE_" + p;
+  std::unique_ptr<cmGeneratorExpressionInterpreter> genexInterpreter(
+    p == "POSITION_INDEPENDENT_CODE" ? new cmGeneratorExpressionInterpreter(
+                                         tgt->GetLocalGenerator(), config, tgt)
+                                     : nullptr);
+
   for (cmGeneratorTarget const* theTarget : deps) {
     // An error should be reported if one dependency
     // has INTERFACE_POSITION_INDEPENDENT_CODE ON and the other
@@ -4434,8 +4556,8 @@ PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt,
 
     const bool ifaceIsSet = std::find(propKeys.begin(), propKeys.end(),
                                       interfaceProperty) != propKeys.end();
-    PropertyType ifacePropContent =
-      getTypedProperty<PropertyType>(theTarget, interfaceProperty);
+    PropertyType ifacePropContent = getTypedProperty<PropertyType>(
+      theTarget, interfaceProperty, genexInterpreter.get());
 
     std::string reportEntry;
     if (ifaceIsSet) {
@@ -4533,6 +4655,13 @@ bool cmGeneratorTarget::GetLinkInterfaceDependentBoolProperty(
                                                    BoolType, nullptr);
 }
 
+std::string cmGeneratorTarget::GetLinkInterfaceDependentStringAsBoolProperty(
+  const std::string& p, const std::string& config) const
+{
+  return checkInterfacePropertyCompatibility<std::string>(
+    this, p, config, "FALSE", BoolType, nullptr);
+}
+
 const char* cmGeneratorTarget::GetLinkInterfaceDependentStringProperty(
   const std::string& p, const std::string& config) const
 {

+ 5 - 0
Source/cmGeneratorTarget.h

@@ -173,6 +173,8 @@ public:
   const char* GetFeature(const std::string& feature,
                          const std::string& config) const;
 
+  const char* GetLinkPIEProperty(const std::string& config) const;
+
   bool IsIPOEnabled(std::string const& lang, std::string const& config) const;
 
   bool IsLinkInterfaceDependentBoolProperty(const std::string& p,
@@ -789,6 +791,9 @@ private:
   cmHeadToLinkInterfaceMap& GetHeadToLinkInterfaceUsageRequirementsMap(
     std::string const& config) const;
 
+  std::string GetLinkInterfaceDependentStringAsBoolProperty(
+    const std::string& p, const std::string& config) const;
+
   // Cache import information from properties for each configuration.
   struct ImportInfo
   {

+ 23 - 0
Source/cmGlobalXCodeGenerator.cxx

@@ -1755,6 +1755,26 @@ void cmGlobalXCodeGenerator::CreateCustomRulesMakefile(
   }
 }
 
+void cmGlobalXCodeGenerator::AddPositionIndependentLinkAttribute(
+  cmGeneratorTarget* target, cmXCodeObject* buildSettings,
+  const std::string& configName)
+{
+  // For now, only EXECUTABLE is concerned
+  if (target->GetType() != cmStateEnums::EXECUTABLE) {
+    return;
+  }
+
+  const char* PICValue = target->GetLinkPIEProperty(configName);
+  if (PICValue == nullptr) {
+    // POSITION_INDEPENDENT_CODE is not set
+    return;
+  }
+
+  buildSettings->AddAttribute(
+    "LD_NO_PIE",
+    this->CreateString(cmSystemTools::IsOn(PICValue) ? "NO" : "YES"));
+}
+
 void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
                                                  cmXCodeObject* buildSettings,
                                                  const std::string& configName)
@@ -1806,6 +1826,9 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
     buildSettings->AddAttribute("LLVM_LTO", this->CreateString(ltoValue));
   }
 
+  // Handle PIE linker configuration
+  this->AddPositionIndependentLinkAttribute(gtgt, buildSettings, configName);
+
   // Add define flags
   this->CurrentLocalGenerator->AppendFlags(
     defFlags, this->CurrentMakefile->GetDefineFlags());

+ 3 - 0
Source/cmGlobalXCodeGenerator.h

@@ -171,6 +171,9 @@ private:
                                    const std::string& configName);
   cmXCodeObject* CreateUtilityTarget(cmGeneratorTarget* gtgt);
   void AddDependAndLinkInformation(cmXCodeObject* target);
+  void AddPositionIndependentLinkAttribute(cmGeneratorTarget* target,
+                                           cmXCodeObject* buildSettings,
+                                           const std::string& configName);
   void CreateBuildSettings(cmGeneratorTarget* gtgt,
                            cmXCodeObject* buildSettings,
                            const std::string& buildType);

+ 32 - 0
Source/cmLocalGenerator.cxx

@@ -1201,6 +1201,8 @@ void cmLocalGenerator::GetTargetFlags(
       break;
   }
 
+  this->AppendPositionIndependentLinkerFlags(linkFlags, target, config,
+                                             linkLanguage);
   this->AppendIPOLinkerFlags(linkFlags, target, config, linkLanguage);
 }
 
@@ -2027,6 +2029,36 @@ void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags,
   }
 }
 
+void cmLocalGenerator::AppendPositionIndependentLinkerFlags(
+  std::string& flags, cmGeneratorTarget* target, const std::string& config,
+  const std::string& lang)
+{
+  // For now, only EXECUTABLE is concerned
+  if (target->GetType() != cmStateEnums::EXECUTABLE) {
+    return;
+  }
+
+  const char* PICValue = target->GetLinkPIEProperty(config);
+  if (PICValue == nullptr) {
+    // POSITION_INDEPENDENT_CODE is not set
+    return;
+  }
+
+  std::string name = "CMAKE_" + lang + "_LINK_OPTIONS_";
+  name += cmSystemTools::IsOn(PICValue) ? "PIE" : "NO_PIE";
+
+  auto pieFlags = this->Makefile->GetSafeDefinition(name);
+  if (pieFlags.empty()) {
+    return;
+  }
+
+  std::vector<std::string> flagsList;
+  cmSystemTools::ExpandListArgument(pieFlags, flagsList);
+  for (const auto& flag : flagsList) {
+    this->AppendFlagEscape(flags, flag);
+  }
+}
+
 void cmLocalGenerator::AppendCompileOptions(std::string& options,
                                             const char* options_list,
                                             const char* regex) const

+ 4 - 0
Source/cmLocalGenerator.h

@@ -126,6 +126,10 @@ public:
   void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target,
                             const std::string& config,
                             const std::string& lang);
+  void AppendPositionIndependentLinkerFlags(std::string& flags,
+                                            cmGeneratorTarget* target,
+                                            const std::string& config,
+                                            const std::string& lang);
   ///! Get the include flags for the current makefile and language
   std::string GetIncludeFlags(const std::vector<std::string>& includes,
                               cmGeneratorTarget* target,

+ 3 - 0
Source/cmMakefileTargetGenerator.cxx

@@ -97,6 +97,9 @@ void cmMakefileTargetGenerator::GetTargetLinkFlags(
   this->GeneratorTarget->GetLinkOptions(opts, this->ConfigName, linkLanguage);
   // LINK_OPTIONS are escaped.
   this->LocalGenerator->AppendCompileOptions(flags, opts);
+
+  this->LocalGenerator->AppendPositionIndependentLinkerFlags(
+    flags, this->GeneratorTarget, this->ConfigName, linkLanguage);
 }
 
 void cmMakefileTargetGenerator::CreateRuleFile()

+ 5 - 2
Source/cmPolicies.h

@@ -244,7 +244,9 @@ class cmMakefile;
   SELECT(POLICY, CMP0082,                                                     \
          "Install rules from add_subdirectory() are interleaved with those "  \
          "in caller.",                                                        \
-         3, 14, 0, cmPolicies::WARN)
+         3, 14, 0, cmPolicies::WARN)                                          \
+  SELECT(POLICY, CMP0083, "Add PIE options when linking executable.", 3, 14,  \
+         0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
@@ -271,7 +273,8 @@ class cmMakefile;
   F(CMP0069)                                                                  \
   F(CMP0073)                                                                  \
   F(CMP0076)                                                                  \
-  F(CMP0081)
+  F(CMP0081)                                                                  \
+  F(CMP0083)
 
 /** \class cmPolicies
  * \brief Handles changes in CMake behavior and policies

+ 2 - 1
Tests/RunCMake/CMakeLists.txt

@@ -190,7 +190,8 @@ add_RunCMake_test(PolicyScope)
 add_RunCMake_test(WriteCompilerDetectionHeader)
 add_RunCMake_test(SourceProperties)
 if(NOT WIN32)
-  add_RunCMake_test(PositionIndependentCode)
+  add_RunCMake_test(PositionIndependentCode -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
+                                            -DCMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID})
 endif()
 if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
   add_RunCMake_test(VisibilityPreset)

+ 22 - 0
Tests/RunCMake/PositionIndependentCode/CMP0083-cmp0083_new-check.cmake

@@ -0,0 +1,22 @@
+
+include ("${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/CMP0083_config.cmake")
+
+
+# retrieve default type of executable
+check_executable ("${cmp0083_ref}" ref)
+
+if (ref STREQUAL "PIE")
+  # check no_pie executable is really no position independent
+  check_executable ("${cmp0083_new_no_pie}" new_no_pie)
+  if (NOT new_no_pie STREQUAL "NO_PIE")
+    set (RunCMake_TEST_FAILED "CMP0083(NEW) do not produce expected executable.")
+  endif()
+elseif (ref STREQUAL "NO_PIE")
+  # check pie executable is really position independent
+  check_executable ("${cmp0083_new_pie}" new_pie)
+  if (NOT new_pie MATCHES "PIE")
+    set (RunCMake_TEST_FAILED "CMP0083(NEW) do not produce expected executable.")
+  endif()
+else()
+  set (RunCMake_TEST_FAILED "CMP0083(NEW) unexpected result.")
+endif()

+ 20 - 0
Tests/RunCMake/PositionIndependentCode/CMP0083-cmp0083_old-check.cmake

@@ -0,0 +1,20 @@
+
+include ("${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/CMP0083_config.cmake")
+
+
+# retrieve default type of executable
+check_executable ("${cmp0083_ref}" ref)
+
+# POSITION_INDEPENDENT_CODE must not have influence on executable
+# pie and no_pie executable must have same type as reference
+check_executable ("${cmp0083_old_pie}" old_pie)
+if (NOT old_pie STREQUAL ref)
+  set (RunCMake_TEST_FAILED "CMP0083(OLD) do not produce expected executable.")
+  return()
+endif()
+
+check_executable ("${cmp0083_old_no_pie}" old_no_pie)
+if (NOT old_no_pie STREQUAL ref)
+  set (RunCMake_TEST_FAILED "CMP0083(OLD) do not produce expected executable.")
+  return()
+endif()

+ 45 - 0
Tests/RunCMake/PositionIndependentCode/CMP0083.cmake

@@ -0,0 +1,45 @@
+
+# create reference to detect default : PIE or not
+add_executable (cmp0083_ref main.cpp)
+
+
+set (CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+cmake_policy(SET CMP0083 NEW)
+add_executable (cmp0083_new_pie main.cpp)
+
+
+cmake_policy(SET CMP0083 OLD)
+add_executable (cmp0083_old_pie main.cpp)
+
+
+set (CMAKE_POSITION_INDEPENDENT_CODE OFF)
+
+cmake_policy(SET CMP0083 NEW)
+add_executable (cmp0083_new_no_pie main.cpp)
+
+
+cmake_policy(SET CMP0083 OLD)
+add_executable (cmp0083_old_no_pie main.cpp)
+
+# high-level targets
+add_custom_target(cmp0083_new)
+add_dependencies(cmp0083_new cmp0083_ref cmp0083_new_pie cmp0083_new_no_pie)
+
+# high-level targets
+add_custom_target(cmp0083_old)
+add_dependencies(cmp0083_old cmp0083_ref cmp0083_old_pie cmp0083_old_no_pie)
+
+
+# generate file holding paths to executables
+file (GENERATE OUTPUT "${CMAKE_BINARY_DIR}/$<CONFIG>/CMP0083_config.cmake"
+               CONTENT
+[==[
+include ("${RunCMake_TEST_SOURCE_DIR}/PIE_validator.cmake")
+
+set (cmp0083_ref "$<TARGET_FILE:cmp0083_ref>")
+set (cmp0083_new_pie "$<TARGET_FILE:cmp0083_new_pie>")
+set (cmp0083_old_pie "$<TARGET_FILE:cmp0083_old_pie>")
+set (cmp0083_new_no_pie "$<TARGET_FILE:cmp0083_new_no_pie>")
+set (cmp0083_old_no_pie "$<TARGET_FILE:cmp0083_old_no_pie>")
+]==])

+ 12 - 0
Tests/RunCMake/PositionIndependentCode/CheckPIESupported.cmake

@@ -0,0 +1,12 @@
+
+if (CMAKE_CXX_LINK_OPTIONS_PIE)
+  file(WRITE "${PIE_SUPPORTED}" "\nset(PIE_SUPPORTED TRUE)\n")
+else()
+  file(WRITE "${PIE_SUPPORTED}" "\nset(PIE_SUPPORTED FALSE)\n")
+endif()
+
+if (CMAKE_CXX_LINK_OPTIONS_NO_PIE)
+  file(APPEND "${PIE_SUPPORTED}" "\nset(NO_PIE_SUPPORTED TRUE)\n")
+else()
+  file(APPEND "${PIE_SUPPORTED}" "\nset(NO_PIE_SUPPORTED FALSE)\n")
+endif()

+ 1 - 0
Tests/RunCMake/PositionIndependentCode/Genex1-result.txt

@@ -0,0 +1 @@
+1

+ 3 - 0
Tests/RunCMake/PositionIndependentCode/Genex1-stderr.txt

@@ -0,0 +1,3 @@
+CMake Error: Property POSITION_INDEPENDENT_CODE on target "conflict1" does
+not match the INTERFACE_POSITION_INDEPENDENT_CODE property requirement
+of dependency "genex_pic".

+ 9 - 0
Tests/RunCMake/PositionIndependentCode/Genex1.cmake

@@ -0,0 +1,9 @@
+
+add_library(genex_pic UNKNOWN IMPORTED)
+# PIC is ON if sibling target is a library, OFF if it is an executable
+set_property(TARGET genex_pic PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE $<NOT:$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>>)
+
+
+add_library(conflict1 STATIC main.cpp)
+set_property(TARGET conflict1 PROPERTY POSITION_INDEPENDENT_CODE OFF)
+target_link_libraries(conflict1 PRIVATE genex_pic)

+ 1 - 0
Tests/RunCMake/PositionIndependentCode/Genex2-result.txt

@@ -0,0 +1 @@
+1

+ 3 - 0
Tests/RunCMake/PositionIndependentCode/Genex2-stderr.txt

@@ -0,0 +1,3 @@
+CMake Error: Property POSITION_INDEPENDENT_CODE on target "conflict2" does
+not match the INTERFACE_POSITION_INDEPENDENT_CODE property requirement
+of dependency "genex_pic".

+ 9 - 0
Tests/RunCMake/PositionIndependentCode/Genex2.cmake

@@ -0,0 +1,9 @@
+
+add_library(genex_pic UNKNOWN IMPORTED)
+# PIC is ON if sibling target is a library, OFF if it is an executable
+set_property(TARGET genex_pic PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE $<NOT:$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>>)
+
+
+add_executable(conflict2 main.cpp)
+set_property(TARGET conflict2 PROPERTY POSITION_INDEPENDENT_CODE ON)
+target_link_libraries(conflict2 PRIVATE genex_pic)

+ 7 - 0
Tests/RunCMake/PositionIndependentCode/PIE-pie_off-check.cmake

@@ -0,0 +1,7 @@
+
+include ("${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/PIE_config.cmake")
+
+check_executable ("${pie_off}" status)
+if (NOT status STREQUAL "NO_PIE")
+  set (RunCMake_TEST_FAILED "Executable is NOT 'no PIE' (${status}).")
+endif()

+ 7 - 0
Tests/RunCMake/PositionIndependentCode/PIE-pie_on-check.cmake

@@ -0,0 +1,7 @@
+
+include ("${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/PIE_config.cmake")
+
+check_executable ("${pie_on}" status)
+if (NOT status STREQUAL "PIE")
+  set (RunCMake_TEST_FAILED "Executable is NOT 'PIE' (${status}).")
+endif()

+ 19 - 0
Tests/RunCMake/PositionIndependentCode/PIE.cmake

@@ -0,0 +1,19 @@
+
+cmake_policy(SET CMP0083 NEW)
+
+add_executable (pie_on main.cpp)
+set_property(TARGET pie_on PROPERTY POSITION_INDEPENDENT_CODE ON)
+
+add_executable (pie_off main.cpp)
+set_property(TARGET pie_off PROPERTY POSITION_INDEPENDENT_CODE OFF)
+
+
+# generate file holding paths to executables
+file (GENERATE OUTPUT "${CMAKE_BINARY_DIR}/$<CONFIG>/PIE_config.cmake"
+               CONTENT
+[==[
+include ("${RunCMake_TEST_SOURCE_DIR}/PIE_validator.cmake")
+
+set (pie_on "$<TARGET_FILE:pie_on>")
+set (pie_off "$<TARGET_FILE:pie_off>")
+]==])

+ 32 - 0
Tests/RunCMake/PositionIndependentCode/PIE_validator.cmake

@@ -0,0 +1,32 @@
+
+include_guard(GLOBAL)
+
+function (CHECK_EXECUTABLE executable result)
+  set (${result} "UNKNOWN" PARENT_SCOPE)
+
+  if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+    set (tool otool -hv)
+  else()
+    set (tool "${CMAKE_COMMAND}" -E env LANG=C LC_ALL=C readelf -lW)
+  endif()
+
+  execute_process(COMMAND ${tool} "${executable}"
+                  OUTPUT_VARIABLE output
+                  ERROR_VARIABLE output)
+
+  if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+    if (output MATCHES "( |\t)PIE( |\n|$)")
+      set (${result} "PIE" PARENT_SCOPE)
+    else()
+      set (${result} "NO_PIE" PARENT_SCOPE)
+    endif()
+  else()
+    if (output MATCHES "Elf file type is DYN")
+      set (${result} "PIE" PARENT_SCOPE)
+    elseif (output MATCHES "Elf file type is EXEC")
+      set (${result} "NO_PIE" PARENT_SCOPE)
+    else()
+      message(SEND_ERROR "Did not find a known file type")
+    endif()
+  endif()
+endfunction()

+ 64 - 0
Tests/RunCMake/PositionIndependentCode/RunCMakeTest.cmake

@@ -7,3 +7,67 @@ run_cmake(Conflict4)
 run_cmake(Conflict5)
 run_cmake(Conflict6)
 run_cmake(Debug)
+run_cmake(Genex1)
+run_cmake(Genex2)
+
+set(RunCMake_TEST_OPTIONS "-DPIE_SUPPORTED=${RunCMake_BINARY_DIR}/PIESupported.cmake")
+run_cmake(CheckPIESupported)
+include ("${RunCMake_BINARY_DIR}/PIESupported.cmake" OPTIONAL)
+
+if (PIE_SUPPORTED OR NO_PIE_SUPPORTED)
+  if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|(Free|Net|Open)BSD)$")
+    # try to locate readelf needed for validation
+    find_program (READELF NAMES readelf)
+  endif()
+  if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+    # try to locate otool needed for validation
+    find_program (OTOOL NAMES otool)
+  endif()
+
+  if ((READELF OR OTOOL) AND
+      (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
+        OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
+        OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang"))
+    macro(run_cmake_target test subtest)
+      set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+      set(RunCMake_TEST_NO_CLEAN 1)
+      set(RunCMake_TEST_CONFIG Release)
+      run_cmake_command(${test}-${subtest} ${CMAKE_COMMAND} --build . --config Release --target ${subtest} ${ARGN})
+
+      unset(RunCMake_TEST_BINARY_DIR)
+      unset(RunCMake_TEST_NO_CLEAN)
+    endmacro()
+
+    set(RunCMake_TEST_SOURCE_DIR "${RunCMake_SOURCE_DIR}")
+    set(RunCMake_TEST_OUTPUT_MERGE TRUE)
+    if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+      set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+    endif()
+
+    run_cmake(PIE)
+    if (PIE_SUPPORTED)
+      run_cmake_target(PIE pie_on)
+    endif()
+    if (NO_PIE_SUPPORTED)
+      run_cmake_target(PIE pie_off)
+    endif()
+
+    run_cmake(CMP0083)
+    run_cmake_target(CMP0083 cmp0083_ref)
+
+    # retrieve default mode
+    include("${RunCMake_SOURCE_DIR}/PIE_validator.cmake")
+    include("${RunCMake_BINARY_DIR}/CMP0083-build/Release/CMP0083_config.cmake")
+    check_executable("${cmp0083_ref}" cmp0083_ref_mode)
+
+    if ((cmp0083_ref_mode STREQUAL "PIE" AND NO_PIE_SUPPORTED)
+        OR (cmp0083_ref_mode STREQUAL "NO_PIE" AND PIE_SUPPORTED))
+      run_cmake_target(CMP0083 cmp0083_new)
+    endif()
+    run_cmake_target(CMP0083 cmp0083_old)
+
+    unset(RunCMake_TEST_SOURCE_DIR)
+    unset(RunCMake_TEST_OPTIONS)
+    unset(RunCMake_TEST_OUTPUT_MERGE)
+  endif()
+endif()

+ 1 - 0
Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt

@@ -26,6 +26,7 @@
    \* CMP0073
    \* CMP0076
    \* CMP0081
+   \* CMP0083
 
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)