Link libraries by full path even in implicit directories

When CMP0003 was first introduced we wanted to link all libraries by
full path.  However, some projects had problems on platforms where
find_library would find /usr/lib/libfoo.so when the project really
wanted to link to /usr/lib/<arch>/libfoo.so and had been working by
accident because pre-CMP0003 behavior used -lfoo to link.

We first tried to address that in commit v2.6.0~440 (Teach find_library
to avoid returning library paths in system directories, 2008-01-23) by
returning just "foo" for libraries in implicit link directories.  This
caused problems for projects expecting find_library to always return a
full path.  We ended up using the solution in commit v2.6.0~366 (...
switch library paths found in implicit link directories to use -l,
2008-01-31).  However, the special case for libraries in implicit link
directories has also proven problematic and confusing.

Introduce policy CMP0060 to switch to linking all libraries by full path
even if they are in implicit link directories.  Explain in the policy
documentation the factors that led to the original approach and now to
this approach.
This commit is contained in:
Brad King 2015-04-07 10:43:47 -04:00
parent 318cd37097
commit 882f48e5ba
28 changed files with 234 additions and 6 deletions

View File

@ -34,14 +34,20 @@ Each ``<item>`` may be:
automatically be added in the build system to make sure the named automatically be added in the build system to make sure the named
library target is up-to-date before the ``<target>`` links. library target is up-to-date before the ``<target>`` links.
If an imported library has the :prop_tgt:`IMPORTED_NO_SONAME`
target property set, CMake may ask the linker to search for
the library instead of using the full path
(e.g. ``/usr/lib/libfoo.so`` becomes ``-lfoo``).
* **A full path to a library file**: The generated link line will * **A full path to a library file**: The generated link line will
normally preserve the full path to the file. However, there are normally preserve the full path to the file. The buildsystem will
some cases where CMake must ask the linker to search for the library
(e.g. ``/usr/lib/libfoo.so`` becomes ``-lfoo``), such as when it
appears in a system library directory that the compiler front-end
may replace with an alternative. Either way, the buildsystem will
have a dependency to re-link ``<target>`` if the library file changes. have a dependency to re-link ``<target>`` if the library file changes.
There are some cases where CMake may ask the linker to search for
the library (e.g. ``/usr/lib/libfoo.so`` becomes ``-lfoo``), such
as when a shared library is detected to have no ``SONAME`` field.
See policy :policy:`CMP0060` for discussion of another case.
If the library file is in a Mac OSX framework, the ``Headers`` directory If the library file is in a Mac OSX framework, the ``Headers`` directory
of the framework will also be processed as a of the framework will also be processed as a
:ref:`usage requirement <Target Usage Requirements>`. This has the same :ref:`usage requirement <Target Usage Requirements>`. This has the same

View File

@ -117,3 +117,4 @@ All Policies
/policy/CMP0057 /policy/CMP0057
/policy/CMP0058 /policy/CMP0058
/policy/CMP0059 /policy/CMP0059
/policy/CMP0060

63
Help/policy/CMP0060.rst Normal file
View File

@ -0,0 +1,63 @@
CMP0060
-------
Link libraries by full path even in implicit directories.
Policy :policy:`CMP0003` was introduced with the intention of always
linking library files by full path when a full path is given to the
:command:`target_link_libraries` command. However, on some platforms
(e.g. HP-UX) the compiler front-end adds alternative library search paths
for the current architecture (e.g. ``/usr/lib/<arch>`` has alternatives
to libraries in ``/usr/lib`` for the current architecture).
On such platforms the :command:`find_library` may find a library such as
``/usr/lib/libfoo.so`` that does not belong to the current architecture.
Prior to policy :policy:`CMP0003` projects would still build in such
cases because the incorrect library path would be converted to ``-lfoo``
on the link line and the linker would find the proper library in the
arch-specific search path provided by the compiler front-end implicitly.
At the time we chose to remain compatible with such projects by always
converting library files found in implicit link directories to ``-lfoo``
flags to ask the linker to search for them. This approach allowed existing
projects to continue to build while still linking to libraries outside
implicit link directories via full path (such as those in the build tree).
CMake does allow projects to override this behavior by using an
:ref:`IMPORTED library target <Imported Targets>` with its
:prop_tgt:`IMPORTED_LOCATION` property set to the desired full path to
a library file. In fact, many :ref:`Find Modules` are learning to provide
:ref:`Imported Targets` instead of just the traditional ``Foo_LIBRARIES``
variable listing library files. However, this makes the link line
generated for a library found by a Find Module depend on whether it
is linked through an imported target or not, which is inconsistent.
Furthermore, this behavior has been a source of confusion because the
generated link line for a library file depends on its location. It is
also problematic for projects trying to link statically because flags
like ``-Wl,-Bstatic -lfoo -Wl,-Bdynamic`` may be used to help the linker
select ``libfoo.a`` instead of ``libfoo.so`` but then leak dynamic linking
to following libraries. (See the :prop_tgt:`LINK_SEARCH_END_STATIC`
target property for a solution typically used for that problem.)
When the special case for libraries in implicit link directories was first
introduced the list of implicit link directories was simply hard-coded
(e.g. ``/lib``, ``/usr/lib``, and a few others). Since that time, CMake
has learned to detect the implicit link directories used by the compiler
front-end. If necessary, the :command:`find_library` command could be
taught to use this information to help find libraries of the proper
architecture.
For these reasons, CMake 3.3 and above prefer to drop the special case
and link libraries by full path even when they are in implicit link
directories. Policy ``CMP0060`` provides compatibility for existing
projects.
The OLD behavior for this policy is to ask the linker to search for
libraries whose full paths are known to be in implicit link directories.
The NEW behavior for this policy is to link libraries by full path even
if they are in implicit link directories.
This policy was introduced in CMake version 3.3. Unlike most policies,
CMake version |release| does *not* warn by default when this policy
is not set and simply uses OLD behavior. See documentation of the
:variable:`CMAKE_POLICY_WARNING_CMP0060 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
variable to control the warning.

View File

@ -0,0 +1,6 @@
link-implicit-libs-full-path
----------------------------
* Linking to library files by a full path in an implicit linker search
directory (e.g. ``/usr/lib/libfoo.a``) no longer asks the linker to
search for the library (e.g. ``-lfoo``). See policy :policy:`CMP0060`.

View File

@ -11,6 +11,8 @@ warn by default:
policy :policy:`CMP0047`. policy :policy:`CMP0047`.
* ``CMAKE_POLICY_WARNING_CMP0056`` controls the warning for * ``CMAKE_POLICY_WARNING_CMP0056`` controls the warning for
policy :policy:`CMP0056`. policy :policy:`CMP0056`.
* ``CMAKE_POLICY_WARNING_CMP0060`` controls the warning for
policy :policy:`CMP0060`.
This variable should not be set by a project in CMake code. Project This variable should not be set by a project in CMake code. Project
developers running CMake may set this variable in their cache to developers running CMake may set this variable in their cache to

View File

@ -411,6 +411,10 @@ cmComputeLinkInformation
std::vector<std::string> const& dirs = this->Target->GetLinkDirectories(); std::vector<std::string> const& dirs = this->Target->GetLinkDirectories();
this->OldLinkDirMask.insert(dirs.begin(), dirs.end()); this->OldLinkDirMask.insert(dirs.begin(), dirs.end());
} }
this->CMP0060Warn =
this->Makefile->PolicyOptionalWarningEnabled(
"CMAKE_POLICY_WARNING_CMP0060");
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -548,6 +552,22 @@ bool cmComputeLinkInformation::Compute()
// Add implicit language runtime libraries and directories. // Add implicit language runtime libraries and directories.
this->AddImplicitLinkInfo(); this->AddImplicitLinkInfo();
if (!this->CMP0060WarnItems.empty())
{
std::ostringstream w;
w << (this->Makefile->GetCMakeInstance()->GetPolicies()
->GetPolicyWarning(cmPolicies::CMP0060)) << "\n"
"Some library files are in directories implicitly searched by "
"the linker when invoked for " << this->LinkLanguage << ":\n"
" " << cmJoin(this->CMP0060WarnItems, "\n ") << "\n"
"For compatibility with older versions of CMake, the generated "
"link line will ask the linker to search for these by library "
"name."
;
this->CMakeInstance->IssueMessage(cmake::AUTHOR_WARNING, w.str(),
this->Target->GetBacktrace());
}
return true; return true;
} }
@ -1190,6 +1210,28 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
return false; return false;
} }
// Check the policy for whether we should use the approach below.
switch (this->Target->GetPolicyStatusCMP0060())
{
case cmPolicies::WARN:
if (this->CMP0060Warn)
{
// Print the warning at most once for this item.
std::string const& wid = "CMP0060-WARNING-GIVEN-" + item;
if (!this->CMakeInstance->GetPropertyAsBool(wid))
{
this->CMakeInstance->SetProperty(wid, "1");
this->CMP0060WarnItems.insert(item);
}
}
case cmPolicies::OLD:
break;
case cmPolicies::REQUIRED_ALWAYS:
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::NEW:
return false;
}
// Many system linkers support multiple architectures by // Many system linkers support multiple architectures by
// automatically selecting the implicit linker search path for the // automatically selecting the implicit linker search path for the
// current architecture. If the library appears in an implicit link // current architecture. If the library appears in an implicit link

View File

@ -175,6 +175,10 @@ private:
std::vector<std::string> OldUserFlagItems; std::vector<std::string> OldUserFlagItems;
bool OldLinkDirMode; bool OldLinkDirMode;
// CMP0060 warnings.
bool CMP0060Warn;
std::set<std::string> CMP0060WarnItems;
// Runtime path computation. // Runtime path computation.
cmOrderDirectories* OrderRuntimeSearchPath; cmOrderDirectories* OrderRuntimeSearchPath;
void AddLibraryRuntimeInfo(std::string const& fullPath, void AddLibraryRuntimeInfo(std::string const& fullPath,

View File

@ -390,6 +390,11 @@ cmPolicies::cmPolicies()
CMP0059, "CMP0059", CMP0059, "CMP0059",
"Do no treat DEFINITIONS as a built-in directory property.", "Do no treat DEFINITIONS as a built-in directory property.",
3,3,0, cmPolicies::WARN); 3,3,0, cmPolicies::WARN);
this->DefinePolicy(
CMP0060, "CMP0060",
"Link libraries by full path even in implicit directories.",
3,3,0, cmPolicies::WARN);
} }
cmPolicies::~cmPolicies() cmPolicies::~cmPolicies()

View File

@ -118,6 +118,7 @@ public:
CMP0058, ///< Ninja requires custom command byproducts to be explicit CMP0058, ///< Ninja requires custom command byproducts to be explicit
CMP0059, ///< Do not treat ``DEFINITIONS`` as a built-in directory CMP0059, ///< Do not treat ``DEFINITIONS`` as a built-in directory
/// property. /// property.
CMP0060, ///< Link libraries by full path even in implicit directories.
/** \brief Always the last entry. /** \brief Always the last entry.
* *

View File

@ -34,7 +34,8 @@
F(CMP0041) \ F(CMP0041) \
F(CMP0042) \ F(CMP0042) \
F(CMP0046) \ F(CMP0046) \
F(CMP0052) F(CMP0052) \
F(CMP0060)
class cmake; class cmake;
class cmMakefile; class cmMakefile;

View File

@ -0,0 +1,35 @@
# Always build in a predictable configuration. For multi-config
# generators we depend on RunCMakeTest.cmake to do this for us.
if(NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Debug)
endif()
# Convince CMake that it can instruct the linker to search for the
# library of the proper linkage type, but do not really pass flags.
set(CMAKE_EXE_LINK_STATIC_C_FLAGS " ")
set(CMAKE_EXE_LINK_DYNAMIC_C_FLAGS " ")
# Make a link line asking for the linker to search for the library
# look like a missing object file so we will get predictable content
# in the error message. This also ensures that cases expected to use
# the full path can be verified by confirming that they link.
set(CMAKE_LINK_LIBRARY_FLAG LINKFLAG_)
set(CMAKE_LINK_LIBRARY_SUFFIX _LINKSUFFIX${CMAKE_C_OUTPUT_EXTENSION})
# Convince CMake that our library is in an implicit linker search directory.
list(APPEND CMAKE_C_IMPLICIT_LINK_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/lib)
# Create a simple library file. Place it in our library directory.
add_library(CMP0060 STATIC cmp0060.c)
set_property(TARGET CMP0060 PROPERTY
ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}/lib)
# Add a target to link the library file by full path.
add_executable(main1 main.c)
target_link_libraries(main1 $<TARGET_FILE:CMP0060>)
add_dependencies(main1 CMP0060)
# Add a second target to verify the warning only appears once.
add_executable(main2 main.c)
target_link_libraries(main2 $<TARGET_FILE:CMP0060>)
add_dependencies(main2 CMP0060)

View File

@ -0,0 +1,2 @@
cmake_policy(SET CMP0060 NEW)
include(CMP0060-Common.cmake)

View File

@ -0,0 +1 @@
[^0]

View File

@ -0,0 +1 @@
LINKFLAG_CMP0060_LINKSUFFIX

View File

@ -0,0 +1,2 @@
cmake_policy(SET CMP0060 OLD)
include(CMP0060-Common.cmake)

View File

@ -0,0 +1 @@
[^0]

View File

@ -0,0 +1 @@
LINKFLAG_CMP0060_LINKSUFFIX

View File

@ -0,0 +1 @@
include(CMP0060-Common.cmake)

View File

@ -0,0 +1 @@
[^0]

View File

@ -0,0 +1 @@
LINKFLAG_CMP0060_LINKSUFFIX

View File

@ -0,0 +1,16 @@
^CMake Warning \(dev\) at CMP0060-Common.cmake:[0-9]+ \(add_executable\):
Policy CMP0060 is not set: Link libraries by full path even in implicit
directories. Run "cmake --help-policy CMP0060" for policy details. Use
the cmake_policy command to set the policy and suppress this warning.
Some library files are in directories implicitly searched by the linker
when invoked for C:
.*/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-build/lib/(lib)?CMP0060.(a|lib)
For compatibility with older versions of CMake, the generated link line
will ask the linker to search for these by library name.
Call Stack \(most recent call first\):
CMP0060-WARN-ON.cmake:[0-9]+ \(include\)
CMakeLists.txt:3 \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.$

View File

@ -0,0 +1,2 @@
set(CMAKE_POLICY_WARNING_CMP0060 1)
include(CMP0060-Common.cmake)

View File

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.2)
project(${RunCMake_TEST} C)
include(${RunCMake_TEST}.cmake)

View File

@ -0,0 +1,19 @@
include(RunCMake)
function(run_cmake_CMP0060 CASE)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0060-${CASE}-build)
set(RunCMake_TEST_NO_CLEAN 1)
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
run_cmake(CMP0060-${CASE})
set(RunCMake_TEST_OUTPUT_MERGE 1)
run_cmake_command(CMP0060-${CASE}-Build
${CMAKE_COMMAND} --build . --config Debug
)
endfunction()
run_cmake_CMP0060(OLD)
run_cmake_CMP0060(WARN-OFF)
run_cmake_CMP0060(WARN-ON)
run_cmake_CMP0060(NEW)

View File

@ -0,0 +1,4 @@
int libCMP0060(void)
{
return 0;
}

View File

@ -0,0 +1,5 @@
extern int libCMP0060(void);
int main(void)
{
return libCMP0060();
}

View File

@ -65,6 +65,7 @@ add_RunCMake_test(CMP0054)
add_RunCMake_test(CMP0055) add_RunCMake_test(CMP0055)
add_RunCMake_test(CMP0057) add_RunCMake_test(CMP0057)
add_RunCMake_test(CMP0059) add_RunCMake_test(CMP0059)
add_RunCMake_test(CMP0060)
if(CMAKE_GENERATOR STREQUAL "Ninja") if(CMAKE_GENERATOR STREQUAL "Ninja")
add_RunCMake_test(Ninja) add_RunCMake_test(Ninja)
endif() endif()

View File

@ -17,6 +17,7 @@
\* CMP0042 \* CMP0042
\* CMP0046 \* CMP0046
\* CMP0052 \* CMP0052
\* CMP0060
Call Stack \(most recent call first\): Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\) CMakeLists.txt:3 \(include\)