From b41ad3b399c591f8f35eaacf056ffd9016b9e0bc Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Wed, 8 Jun 2011 08:46:31 -0400
Subject: Teach find_(library|package) about Linux multiarch (#12037)

Implement support for multiarch as specified here:

  http://wiki.debian.org/Multiarch
  https://wiki.ubuntu.com/MultiarchSpec

Detect the <arch> part of <prefix>/lib/<arch> from the implicit library
search path from each compiler to set CMAKE_<lang>_LIBRARY_ARCHITECTURE.
Define CMAKE_LIBRARY_ARCHITECTURE using one of these values (they should
all be the same).  Teach the find_library and find_package commands to
search <prefix>/lib/<arch> whenever they would search <prefix>/lib.
---
 Modules/CMakeCCompiler.cmake.in         |  5 +++++
 Modules/CMakeCXXCompiler.cmake.in       |  5 +++++
 Modules/CMakeDetermineCompilerABI.cmake | 11 +++++++++++
 Modules/CMakeFortranCompiler.cmake.in   |  5 +++++
 Modules/Platform/Linux.cmake            |  3 +++
 Source/cmDocumentVariables.cxx          | 20 ++++++++++++++++++++
 Source/cmFindBase.cxx                   | 12 ++++++++++++
 Source/cmFindLibraryCommand.cxx         |  4 ++++
 Source/cmFindPackageCommand.cxx         | 24 ++++++++++++++++++------
 Source/cmFindPackageCommand.h           |  1 +
 Source/cmFindPathCommand.cxx            |  2 ++
 Source/cmFindProgramCommand.cxx         |  2 ++
 12 files changed, 88 insertions(+), 6 deletions(-)

diff --git a/Modules/CMakeCCompiler.cmake.in b/Modules/CMakeCCompiler.cmake.in
index 051df4c..04a5cec 100644
--- a/Modules/CMakeCCompiler.cmake.in
+++ b/Modules/CMakeCCompiler.cmake.in
@@ -28,6 +28,7 @@ SET(CMAKE_C_LINKER_PREFERENCE 10)
 # Save compiler ABI information.
 SET(CMAKE_C_SIZEOF_DATA_PTR "@CMAKE_C_SIZEOF_DATA_PTR@")
 SET(CMAKE_C_COMPILER_ABI "@CMAKE_C_COMPILER_ABI@")
+SET(CMAKE_C_LIBRARY_ARCHITECTURE "@CMAKE_C_LIBRARY_ARCHITECTURE@")
 
 IF(CMAKE_C_SIZEOF_DATA_PTR)
   SET(CMAKE_SIZEOF_VOID_P "${CMAKE_C_SIZEOF_DATA_PTR}")
@@ -37,6 +38,10 @@ IF(CMAKE_C_COMPILER_ABI)
   SET(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_C_COMPILER_ABI}")
 ENDIF(CMAKE_C_COMPILER_ABI)
 
+IF(CMAKE_C_LIBRARY_ARCHITECTURE)
+  SET(CMAKE_LIBRARY_ARCHITECTURE "@CMAKE_C_LIBRARY_ARCHITECTURE@")
+ENDIF()
+
 SET(CMAKE_C_HAS_ISYSROOT "@CMAKE_C_HAS_ISYSROOT@")
 @CMAKE_C_OSX_DEPLOYMENT_TARGET_FLAG_CODE@
 
diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in
index d7ae8d9..ea06526 100644
--- a/Modules/CMakeCXXCompiler.cmake.in
+++ b/Modules/CMakeCXXCompiler.cmake.in
@@ -29,6 +29,7 @@ SET(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1)
 # Save compiler ABI information.
 SET(CMAKE_CXX_SIZEOF_DATA_PTR "@CMAKE_CXX_SIZEOF_DATA_PTR@")
 SET(CMAKE_CXX_COMPILER_ABI "@CMAKE_CXX_COMPILER_ABI@")
+SET(CMAKE_CXX_LIBRARY_ARCHITECTURE "@CMAKE_CXX_LIBRARY_ARCHITECTURE@")
 
 IF(CMAKE_CXX_SIZEOF_DATA_PTR)
   SET(CMAKE_SIZEOF_VOID_P "${CMAKE_CXX_SIZEOF_DATA_PTR}")
@@ -38,6 +39,10 @@ IF(CMAKE_CXX_COMPILER_ABI)
   SET(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_CXX_COMPILER_ABI}")
 ENDIF(CMAKE_CXX_COMPILER_ABI)
 
+IF(CMAKE_CXX_LIBRARY_ARCHITECTURE)
+  SET(CMAKE_LIBRARY_ARCHITECTURE "@CMAKE_CXX_LIBRARY_ARCHITECTURE@")
+ENDIF()
+
 SET(CMAKE_CXX_HAS_ISYSROOT "@CMAKE_CXX_HAS_ISYSROOT@")
 @CMAKE_CXX_OSX_DEPLOYMENT_TARGET_FLAG_CODE@
 
diff --git a/Modules/CMakeDetermineCompilerABI.cmake b/Modules/CMakeDetermineCompilerABI.cmake
index 7f918e6..39d1f17 100644
--- a/Modules/CMakeDetermineCompilerABI.cmake
+++ b/Modules/CMakeDetermineCompilerABI.cmake
@@ -86,6 +86,17 @@ FUNCTION(CMAKE_DETERMINE_COMPILER_ABI lang src)
       SET(CMAKE_${lang}_IMPLICIT_LINK_LIBRARIES "${implicit_libs}" PARENT_SCOPE)
       SET(CMAKE_${lang}_IMPLICIT_LINK_DIRECTORIES "${implicit_dirs}" PARENT_SCOPE)
 
+      # Detect library architecture directory name.
+      IF(CMAKE_LIBRARY_ARCHITECTURE_REGEX)
+        FOREACH(dir ${implicit_dirs})
+          IF("${dir}" MATCHES "/lib/${CMAKE_LIBRARY_ARCHITECTURE_REGEX}$")
+            GET_FILENAME_COMPONENT(arch "${dir}" NAME)
+            SET(CMAKE_${lang}_LIBRARY_ARCHITECTURE "${arch}" PARENT_SCOPE)
+            BREAK()
+          ENDIF()
+        ENDFOREACH()
+      ENDIF()
+
     ELSE(CMAKE_DETERMINE_${lang}_ABI_COMPILED)
       MESSAGE(STATUS "Detecting ${lang} compiler ABI info - failed")
       FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
diff --git a/Modules/CMakeFortranCompiler.cmake.in b/Modules/CMakeFortranCompiler.cmake.in
index 146a6f2..53d9552 100644
--- a/Modules/CMakeFortranCompiler.cmake.in
+++ b/Modules/CMakeFortranCompiler.cmake.in
@@ -34,6 +34,7 @@ ENDIF(UNIX)
 # Save compiler ABI information.
 SET(CMAKE_Fortran_SIZEOF_DATA_PTR "@CMAKE_Fortran_SIZEOF_DATA_PTR@")
 SET(CMAKE_Fortran_COMPILER_ABI "@CMAKE_Fortran_COMPILER_ABI@")
+SET(CMAKE_Fortran_LIBRARY_ARCHITECTURE "@CMAKE_Fortran_LIBRARY_ARCHITECTURE@")
 
 IF(CMAKE_Fortran_SIZEOF_DATA_PTR AND NOT CMAKE_SIZEOF_VOID_P)
   SET(CMAKE_SIZEOF_VOID_P "${CMAKE_Fortran_SIZEOF_DATA_PTR}")
@@ -43,5 +44,9 @@ IF(CMAKE_Fortran_COMPILER_ABI)
   SET(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_Fortran_COMPILER_ABI}")
 ENDIF(CMAKE_Fortran_COMPILER_ABI)
 
+IF(CMAKE_Fortran_LIBRARY_ARCHITECTURE)
+  SET(CMAKE_LIBRARY_ARCHITECTURE "@CMAKE_Fortran_LIBRARY_ARCHITECTURE@")
+ENDIF()
+
 SET(CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES "@CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES@")
 SET(CMAKE_Fortran_IMPLICIT_LINK_DIRECTORIES "@CMAKE_Fortran_IMPLICIT_LINK_DIRECTORIES@")
diff --git a/Modules/Platform/Linux.cmake b/Modules/Platform/Linux.cmake
index 6490dd1..57d1b34 100644
--- a/Modules/Platform/Linux.cmake
+++ b/Modules/Platform/Linux.cmake
@@ -45,6 +45,9 @@ ELSE(DEFINED CMAKE_INSTALL_SO_NO_EXE)
   ENDIF(EXISTS "/etc/debian_version")
 ENDIF(DEFINED CMAKE_INSTALL_SO_NO_EXE)
 
+# Match multiarch library directory names.
+SET(CMAKE_LIBRARY_ARCHITECTURE_REGEX "[a-z0-9_]+-linux-gnu")
+
 INCLUDE(Platform/UnixPaths)
 
 # Debian has lib64 paths only for compatibility so they should not be
diff --git a/Source/cmDocumentVariables.cxx b/Source/cmDocumentVariables.cxx
index 994445d..ea25e60 100644
--- a/Source/cmDocumentVariables.cxx
+++ b/Source/cmDocumentVariables.cxx
@@ -822,6 +822,18 @@ void cmDocumentVariables::DefineVariables(cmake* cm)
      "set to the output of uname -r. On other "
      "systems this is set to major-minor version numbers.",false,
      "Variables That Describe the System");
+  cm->DefineProperty
+    ("CMAKE_LIBRARY_ARCHITECTURE", cmProperty::VARIABLE,
+     "Target architecture library directory name, if detected.",
+     "This is the value of CMAKE_<lang>_LIBRARY_ARCHITECTURE as "
+     "detected for one of the enabled languages.",false,
+     "Variables That Describe the System");
+  cm->DefineProperty
+    ("CMAKE_LIBRARY_ARCHITECTURE_REGEX", cmProperty::VARIABLE,
+     "Regex matching possible target architecture library directory names.",
+     "This is used to detect CMAKE_<lang>_LIBRARY_ARCHITECTURE from the "
+     "implicit linker search path by matching the <arch> name.",false,
+     "Variables That Describe the System");
 
   cm->DefineProperty
     ("CMAKE_HOST_SYSTEM", cmProperty::VARIABLE,
@@ -1360,6 +1372,14 @@ void cmDocumentVariables::DefineVariables(cmake* cm)
      "Variables for Languages");
 
   cm->DefineProperty
+    ("CMAKE_<LANG>_LIBRARY_ARCHITECTURE", cmProperty::VARIABLE,
+     "Target architecture library directory name detected for <lang>.",
+     "If the <lang> compiler passes to the linker an architecture-specific "
+     "system library search directory such as <prefix>/lib/<arch> this "
+     "variable contains the <arch> name if/as detected by CMake.",false,
+     "Variables for Languages");
+
+  cm->DefineProperty
     ("CMAKE_<LANG>_LINKER_PREFERENCE_PROPAGATES", cmProperty::VARIABLE,
      "True if CMAKE_<LANG>_LINKER_PREFERENCE propagates across targets.",
      "This is used when CMake selects a linker language for a target.  "
diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx
index d0fe99f..ce9deb1 100644
--- a/Source/cmFindBase.cxx
+++ b/Source/cmFindBase.cxx
@@ -72,12 +72,14 @@ void cmFindBase::GenerateDocumentation()
     "1. Search paths specified in cmake-specific cache variables.  "
     "These are intended to be used on the command line with a -DVAR=value.  "
     "This can be skipped if NO_CMAKE_PATH is passed.\n"
+    "XXX_EXTRA_PREFIX_ENTRY"
     "   <prefix>/XXX_SUBDIR for each <prefix> in CMAKE_PREFIX_PATH\n"
     "   CMAKE_XXX_PATH\n"
     "   CMAKE_XXX_MAC_PATH\n"
     "2. Search paths specified in cmake-specific environment variables.  "
     "These are intended to be set in the user's shell configuration.  "
     "This can be skipped if NO_CMAKE_ENVIRONMENT_PATH is passed.\n"
+    "XXX_EXTRA_PREFIX_ENTRY"
     "   <prefix>/XXX_SUBDIR for each <prefix> in CMAKE_PREFIX_PATH\n"
     "   CMAKE_XXX_PATH\n"
     "   CMAKE_XXX_MAC_PATH\n"
@@ -92,6 +94,7 @@ void cmFindBase::GenerateDocumentation()
     "5. Search cmake variables defined in the Platform files "
     "for the current system.  This can be skipped if NO_CMAKE_SYSTEM_PATH "
     "is passed.\n"
+    "XXX_EXTRA_PREFIX_ENTRY"
     "   <prefix>/XXX_SUBDIR for each <prefix> in CMAKE_SYSTEM_PREFIX_PATH\n"
     "   CMAKE_SYSTEM_XXX_PATH\n"
     "   CMAKE_SYSTEM_XXX_MAC_PATH\n"
@@ -346,6 +349,15 @@ void cmFindBase::AddPrefixPaths(std::vector<std::string> const& in_paths,
       {
       dir += "/";
       }
+    if(subdir == "lib")
+      {
+      const char* arch =
+        this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE");
+      if(arch && *arch)
+        {
+        this->AddPathInternal(dir+"lib/"+arch, pathType);
+        }
+      }
     std::string add = dir + subdir;
     if(add != "/")
       {
diff --git a/Source/cmFindLibraryCommand.cxx b/Source/cmFindLibraryCommand.cxx
index b309376..6355a85 100644
--- a/Source/cmFindLibraryCommand.cxx
+++ b/Source/cmFindLibraryCommand.cxx
@@ -44,6 +44,10 @@ void cmFindLibraryCommand::GenerateDocumentation()
                                "SEARCH_XXX", "library");
   cmSystemTools::ReplaceString(this->GenericDocumentation,
                                "XXX_SUBDIR", "lib");
+  cmSystemTools::ReplaceString(
+    this->GenericDocumentation,
+    "XXX_EXTRA_PREFIX_ENTRY",
+    "   <prefix>/lib/<arch> if CMAKE_LIBRARY_ARCHITECTURE is set, and\n");
   cmSystemTools::ReplaceString(this->GenericDocumentation,
                                "CMAKE_FIND_ROOT_PATH_MODE_XXX",
                                "CMAKE_FIND_ROOT_PATH_MODE_LIBRARY");
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index b77273c..5f106bc 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -243,9 +243,9 @@ void cmFindPackageCommand::GenerateDocumentation()
     "  <prefix>/(cmake|CMake)/                                 (W)\n"
     "  <prefix>/<name>*/                                       (W)\n"
     "  <prefix>/<name>*/(cmake|CMake)/                         (W)\n"
-    "  <prefix>/(share|lib)/cmake/<name>*/                     (U)\n"
-    "  <prefix>/(share|lib)/<name>*/                           (U)\n"
-    "  <prefix>/(share|lib)/<name>*/(cmake|CMake)/             (U)\n"
+    "  <prefix>/(lib/<arch>|lib|share)/cmake/<name>*/          (U)\n"
+    "  <prefix>/(lib/<arch>|lib|share)/<name>*/                (U)\n"
+    "  <prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/  (U)\n"
     "On systems supporting OS X Frameworks and Application Bundles "
     "the following directories are searched for frameworks or bundles "
     "containing a configuration file:\n"
@@ -257,6 +257,7 @@ void cmFindPackageCommand::GenerateDocumentation()
     "  <prefix>/<name>.app/Contents/Resources/CMake/           (A)\n"
     "In all cases the <name> is treated as case-insensitive and corresponds "
     "to any of the names specified (<package> or names given by NAMES).  "
+    "Paths with lib/<arch> are enabled if CMAKE_LIBRARY_ARCHITECTURE is set.  "
     "If PATH_SUFFIXES is specified the suffixes are appended to each "
     "(W) or (U) directory entry one-by-one.\n"
     "This set of directories is intended to work in cooperation with "
@@ -362,6 +363,13 @@ bool cmFindPackageCommand
   // Check for debug mode.
   this->DebugMode = this->Makefile->IsOn("CMAKE_FIND_DEBUG_MODE");
 
+  // Lookup target architecture, if any.
+  if(const char* arch =
+     this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE"))
+    {
+    this->LibraryArchitecture = arch;
+    }
+
   // Lookup whether lib64 paths should be used.
   if(this->Makefile->PlatformIs64Bit() &&
      this->Makefile->GetCMakeInstance()
@@ -2189,6 +2197,10 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in)
 
   // Construct list of common install locations (lib and share).
   std::vector<std::string> common;
+  if(!this->LibraryArchitecture.empty())
+    {
+    common.push_back("lib/"+this->LibraryArchitecture);
+    }
   if(this->UseLib64Paths)
     {
     common.push_back("lib64");
@@ -2196,7 +2208,7 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in)
   common.push_back("lib");
   common.push_back("share");
 
-  //  PREFIX/(share|lib)/cmake/(Foo|foo|FOO).*/
+  //  PREFIX/(lib/ARCH|lib|share)/cmake/(Foo|foo|FOO).*/
   {
   cmFindPackageFileList lister(this);
   lister
@@ -2210,7 +2222,7 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in)
     }
   }
 
-  //  PREFIX/(share|lib)/(Foo|foo|FOO).*/
+  //  PREFIX/(lib/ARCH|lib|share)/(Foo|foo|FOO).*/
   {
   cmFindPackageFileList lister(this);
   lister
@@ -2223,7 +2235,7 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in)
     }
   }
 
-  //  PREFIX/(share|lib)/(Foo|foo|FOO).*/(cmake|CMake)/
+  //  PREFIX/(lib/ARCH|lib|share)/(Foo|foo|FOO).*/(cmake|CMake)/
   {
   cmFindPackageFileList lister(this);
   lister
diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h
index 4229d37..2b2e1da 100644
--- a/Source/cmFindPackageCommand.h
+++ b/Source/cmFindPackageCommand.h
@@ -141,6 +141,7 @@ private:
   bool DebugMode;
   bool UseLib64Paths;
   bool PolicyScope;
+  std::string LibraryArchitecture;
   std::vector<std::string> Names;
   std::vector<std::string> Configs;
   std::set<std::string> IgnoredPaths;
diff --git a/Source/cmFindPathCommand.cxx b/Source/cmFindPathCommand.cxx
index 83b651b..846d187 100644
--- a/Source/cmFindPathCommand.cxx
+++ b/Source/cmFindPathCommand.cxx
@@ -46,6 +46,8 @@ void cmFindPathCommand::GenerateDocumentation()
   cmSystemTools::ReplaceString(this->GenericDocumentation,
                                "XXX_SUBDIR", "include");
   cmSystemTools::ReplaceString(this->GenericDocumentation,
+                               "XXX_EXTRA_PREFIX_ENTRY", "");
+  cmSystemTools::ReplaceString(this->GenericDocumentation,
                                "CMAKE_FIND_ROOT_PATH_MODE_XXX",
                                "CMAKE_FIND_ROOT_PATH_MODE_INCLUDE");
   if(!this->IncludeFileInPath)
diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx
index 71cfdcb..7c56ad7 100644
--- a/Source/cmFindProgramCommand.cxx
+++ b/Source/cmFindProgramCommand.cxx
@@ -42,6 +42,8 @@ void cmFindProgramCommand::GenerateDocumentation()
   cmSystemTools::ReplaceString(this->GenericDocumentation,
                                "XXX_SUBDIR", "[s]bin");
   cmSystemTools::ReplaceString(this->GenericDocumentation,
+                               "XXX_EXTRA_PREFIX_ENTRY", "");
+  cmSystemTools::ReplaceString(this->GenericDocumentation,
                                "CMAKE_FIND_ROOT_PATH_MODE_XXX",
                                "CMAKE_FIND_ROOT_PATH_MODE_PROGRAM");
 }
-- 
cgit v0.12