1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmComputeLinkInformation.h"
4 
5 #include <algorithm>
6 #include <cctype>
7 #include <sstream>
8 #include <utility>
9 
10 #include <cm/memory>
11 #include <cmext/algorithm>
12 
13 #include "cmComputeLinkDepends.h"
14 #include "cmGeneratorTarget.h"
15 #include "cmGlobalGenerator.h"
16 #include "cmListFileCache.h"
17 #include "cmLocalGenerator.h"
18 #include "cmMakefile.h"
19 #include "cmMessageType.h"
20 #include "cmOrderDirectories.h"
21 #include "cmOutputConverter.h"
22 #include "cmPolicies.h"
23 #include "cmState.h"
24 #include "cmStateTypes.h"
25 #include "cmStringAlgorithms.h"
26 #include "cmSystemTools.h"
27 #include "cmTarget.h"
28 #include "cmValue.h"
29 #include "cmake.h"
30 
31 //#define CM_COMPUTE_LINK_INFO_DEBUG
32 
33 /*
34 Notes about linking on various platforms:
35 
36 ------------------------------------------------------------------------------
37 
38 Linux, FreeBSD, macOS, Sun, Windows:
39 
40 Linking to libraries using the full path works fine.
41 
42 ------------------------------------------------------------------------------
43 
44 On AIX, more work is needed.
45 
46   The "-bnoipath" option is needed.  From "man ld":
47 
48     Note: If you specify a shared object, or an archive file
49     containing a shared object, with an absolute or relative path
50     name, instead of with the -lName flag, the path name is
51     included in the import file ID string in the loader section of
52     the output file. You can override this behavior with the
53     -bnoipath option.
54 
55       noipath
56 
57         For shared objects listed on the command-line, rather than
58         specified with the -l flag, use a null path component when
59         listing the shared object in the loader section of the
60         output file. A null path component is always used for
61         shared objects specified with the -l flag. This option
62         does not affect the specification of a path component by
63         using a line beginning with #! in an import file. The
64         default is the ipath option.
65 
66   This prevents the full path specified on the compile line from being
67   compiled directly into the binary.
68 
69   By default the linker places -L paths in the embedded runtime path.
70   In order to implement CMake's RPATH interface correctly, we need the
71   -blibpath:Path option.  From "man ld":
72 
73       libpath:Path
74 
75         Uses Path as the library path when writing the loader section
76         of the output file. Path is neither checked for validity nor
77         used when searching for libraries specified by the -l flag.
78         Path overrides any library paths generated when the -L flag is
79         used.
80 
81         If you do not specify any -L flags, or if you specify the
82         nolibpath option, the default library path information is
83         written in the loader section of the output file. The default
84         library path information is the value of the LIBPATH
85         environment variable if it is defined, and /usr/lib:/lib,
86         otherwise.
87 
88   We can pass -Wl,-blibpath:/usr/lib:/lib always to avoid the -L stuff
89   and not break when the user sets LIBPATH.  Then if we want to add an
90   rpath we insert it into the option before /usr/lib.
91 
92 ------------------------------------------------------------------------------
93 
94 On HP-UX, more work is needed.  There are differences between
95 versions.
96 
97 ld: 92453-07 linker linker ld B.10.33 990520
98 
99   Linking with a full path works okay for static and shared libraries.
100   The linker seems to always put the full path to where the library
101   was found in the binary whether using a full path or -lfoo syntax.
102   Transitive link dependencies work just fine due to the full paths.
103 
104   It has the "-l:libfoo.sl" option.  The +nodefaultrpath is accepted
105   but not documented and does not seem to do anything.  There is no
106   +forceload option.
107 
108 ld: 92453-07 linker ld HP Itanium(R) B.12.41  IPF/IPF
109 
110   Linking with a full path works okay for static libraries.
111 
112   Linking with a full path works okay for shared libraries.  However
113   dependent (transitive) libraries of those linked directly must be
114   either found with an rpath stored in the direct dependencies or
115   found in -L paths as if they were specified with "-l:libfoo.sl"
116   (really "-l:<soname>").  The search matches that of the dynamic
117   loader but only with -L paths.  In other words, if we have an
118   executable that links to shared library bar which links to shared
119   library foo, the link line for the exe must contain
120 
121     /dir/with/bar/libbar.sl -L/dir/with/foo
122 
123   It does not matter whether the exe wants to link to foo directly or
124   whether /dir/with/foo/libfoo.sl is listed.  The -L path must still
125   be present.  It should match the runtime path computed for the
126   executable taking all directly and transitively linked libraries
127   into account.
128 
129   The "+nodefaultrpath" option should be used to avoid getting -L
130   paths in the rpath unless we add our own rpath with +b.  This means
131   that skip-build-rpath should use this option.
132 
133   See documentation in "man ld", "man dld.so", and
134   http://docs.hp.com/en/B2355-90968/creatingandusinglibraries.htm
135 
136     +[no]defaultrpath
137       +defaultrpath is the default.  Include any paths that are
138       specified with -L in the embedded path, unless you specify the
139       +b option.  If you use +b, only the path list specified by +b is
140       in the embedded path.
141 
142       The +nodefaultrpath option removes all library paths that were
143       specified with the -L option from the embedded path.  The linker
144       searches the library paths specified by the -L option at link
145       time.  At run time, the only library paths searched are those
146       specified by the environment variables LD_LIBRARY_PATH and
147       SHLIB_PATH, library paths specified by the +b linker option, and
148       finally the default library paths.
149 
150     +rpathfirst
151       This option will cause the paths specified in RPATH (embedded
152       path) to be used before the paths specified in LD_LIBRARY_PATH
153       or SHLIB_PATH, in searching for shared libraries.  This changes
154       the default search order of LD_LIBRARY_PATH, SHLIB_PATH, and
155       RPATH (embedded path).
156 
157 ------------------------------------------------------------------------------
158 Notes about dependent (transitive) shared libraries:
159 
160 On non-Windows systems shared libraries may have transitive
161 dependencies.  In order to support LINK_INTERFACE_LIBRARIES we must
162 support linking to a shared library without listing all the libraries
163 to which it links.  Some linkers want to be able to find the
164 transitive dependencies (dependent libraries) of shared libraries
165 listed on the command line.
166 
167   - On Windows, DLLs are not directly linked, and the import libraries
168     have no transitive dependencies.
169 
170   - On Mac OS X 10.5 and above transitive dependencies are not needed.
171 
172   - On Mac OS X 10.4 and below we need to actually list the dependencies.
173     Otherwise when using -isysroot for universal binaries it cannot
174     find the dependent libraries.  Listing them on the command line
175     tells the linker where to find them, but unfortunately also links
176     the library.
177 
178   - On HP-UX, the linker wants to find the transitive dependencies of
179     shared libraries in the -L paths even if the dependent libraries
180     are given on the link line.
181 
182   - On AIX the transitive dependencies are not needed.
183 
184   - On SGI, the linker wants to find the transitive dependencies of
185     shared libraries in the -L paths if they are not given on the link
186     line.  Transitive linking can be disabled using the options
187 
188       -no_transitive_link -Wl,-no_transitive_link
189 
190     which disable it.  Both options must be given when invoking the
191     linker through the compiler.
192 
193   - On Sun, the linker wants to find the transitive dependencies of
194     shared libraries in the -L paths if they are not given on the link
195     line.
196 
197   - On Linux, FreeBSD, and QNX:
198 
199     The linker wants to find the transitive dependencies of shared
200     libraries in the "-rpath-link" paths option if they have not been
201     given on the link line.  The option is like rpath but just for
202     link time:
203 
204       -Wl,-rpath-link,"/path1:/path2"
205 
206 For -rpath-link, we need a separate runtime path ordering pass
207 including just the dependent libraries that are not linked.
208 
209 For -L paths on non-HP, we can do the same thing as with rpath-link
210 but put the results in -L paths.  The paths should be listed at the
211 end to avoid conflicting with user search paths (?).
212 
213 For -L paths on HP, we should do a runtime path ordering pass with
214 all libraries, both linked and non-linked.  Even dependent
215 libraries that are also linked need to be listed in -L paths.
216 
217 In our implementation we add all dependent libraries to the runtime
218 path computation.  Then the auto-generated RPATH will find everything.
219 
220 ------------------------------------------------------------------------------
221 Notes about shared libraries with not builtin soname:
222 
223 Some UNIX shared libraries may be created with no builtin soname.  On
224 some platforms such libraries cannot be linked using the path to their
225 location because the linker will copy the path into the field used to
226 find the library at runtime.
227 
228   Apple:    ../libfoo.dylib  ==>  libfoo.dylib  # ok, uses install_name
229   SGI:      ../libfoo.so     ==>  libfoo.so     # ok
230   AIX:      ../libfoo.so     ==>  libfoo.so     # ok
231   Linux:    ../libfoo.so     ==>  ../libfoo.so  # bad
232   HP-UX:    ../libfoo.so     ==>  ../libfoo.so  # bad
233   Sun:      ../libfoo.so     ==>  ../libfoo.so  # bad
234   FreeBSD:  ../libfoo.so     ==>  ../libfoo.so  # bad
235 
236 In order to link these libraries we need to use the old-style split
237 into -L.. and -lfoo options.  This should be fairly safe because most
238 problems with -lfoo options were related to selecting shared libraries
239 instead of static but in this case we want the shared lib.  Link
240 directory ordering needs to be done to make sure these shared
241 libraries are found first.  There should be very few restrictions
242 because this need be done only for shared libraries without soname-s.
243 
244 */
245 
cmComputeLinkInformation(const cmGeneratorTarget * target,const std::string & config)246 cmComputeLinkInformation::cmComputeLinkInformation(
247   const cmGeneratorTarget* target, const std::string& config)
248   // Store context information.
249   : Target(target)
250   , Makefile(target->Target->GetMakefile())
251   , GlobalGenerator(target->GetLocalGenerator()->GetGlobalGenerator())
252   , CMakeInstance(this->GlobalGenerator->GetCMakeInstance())
253   // The configuration being linked.
254   , Config(config)
255 {
256   // Check whether to recognize OpenBSD-style library versioned names.
257   this->OpenBSD = this->Makefile->GetState()->GetGlobalPropertyAsBool(
258     "FIND_LIBRARY_USE_OPENBSD_VERSIONING");
259 
260   // Allocate internals.
261   this->OrderLinkerSearchPath = cm::make_unique<cmOrderDirectories>(
262     this->GlobalGenerator, target, "linker search path");
263   this->OrderRuntimeSearchPath = cm::make_unique<cmOrderDirectories>(
264     this->GlobalGenerator, target, "runtime search path");
265 
266   // Get the language used for linking this target.
267   this->LinkLanguage = this->Target->GetLinkerLanguage(config);
268   if (this->LinkLanguage.empty()) {
269     // The Compute method will do nothing, so skip the rest of the
270     // initialization.
271     return;
272   }
273 
274   // Check whether we should skip dependencies on shared library files.
275   this->LinkDependsNoShared =
276     this->Target->GetPropertyAsBool("LINK_DEPENDS_NO_SHARED");
277 
278   // On platforms without import libraries there may be a special flag
279   // to use when creating a plugin (module) that obtains symbols from
280   // the program that will load it.
281   if (!this->Target->IsDLLPlatform() &&
282       this->Target->GetType() == cmStateEnums::MODULE_LIBRARY) {
283     std::string loader_flag_var =
284       cmStrCat("CMAKE_SHARED_MODULE_LOADER_", this->LinkLanguage, "_FLAG");
285     this->LoaderFlag = this->Makefile->GetDefinition(loader_flag_var);
286   }
287 
288   // Get options needed to link libraries.
289   if (cmValue flag = this->Makefile->GetDefinition(
290         "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FLAG")) {
291     this->LibLinkFlag = *flag;
292   } else {
293     this->LibLinkFlag =
294       this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FLAG");
295   }
296   if (cmValue flag = this->Makefile->GetDefinition(
297         "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FILE_FLAG")) {
298     this->LibLinkFileFlag = *flag;
299   } else {
300     this->LibLinkFileFlag =
301       this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FILE_FLAG");
302   }
303   if (cmValue suffix = this->Makefile->GetDefinition(
304         "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_SUFFIX")) {
305     this->LibLinkSuffix = *suffix;
306   } else {
307     this->LibLinkSuffix =
308       this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX");
309   }
310   if (cmValue flag = this->Makefile->GetDefinition(
311         "CMAKE_" + this->LinkLanguage + "_LINK_OBJECT_FILE_FLAG")) {
312     this->ObjLinkFileFlag = *flag;
313   } else {
314     this->ObjLinkFileFlag =
315       this->Makefile->GetSafeDefinition("CMAKE_LINK_OBJECT_FILE_FLAG");
316   }
317 
318   // Get options needed to specify RPATHs.
319   this->RuntimeUseChrpath = false;
320   if (this->Target->GetType() != cmStateEnums::STATIC_LIBRARY) {
321     const char* tType = ((this->Target->GetType() == cmStateEnums::EXECUTABLE)
322                            ? "EXECUTABLE"
323                            : "SHARED_LIBRARY");
324     std::string rtVar =
325       cmStrCat("CMAKE_", tType, "_RUNTIME_", this->LinkLanguage, "_FLAG");
326     std::string rtSepVar = rtVar + "_SEP";
327     this->RuntimeFlag = this->Makefile->GetSafeDefinition(rtVar);
328     this->RuntimeSep = this->Makefile->GetSafeDefinition(rtSepVar);
329     this->RuntimeAlways = (this->Makefile->GetSafeDefinition(
330       "CMAKE_PLATFORM_REQUIRED_RUNTIME_PATH"));
331 
332     this->RuntimeUseChrpath = this->Target->IsChrpathUsed(config);
333 
334     // Get options needed to help find dependent libraries.
335     std::string rlVar =
336       cmStrCat("CMAKE_", tType, "_RPATH_LINK_", this->LinkLanguage, "_FLAG");
337     this->RPathLinkFlag = this->Makefile->GetSafeDefinition(rlVar);
338   }
339 
340   // Check if we need to include the runtime search path at link time.
341   {
342     std::string var = cmStrCat("CMAKE_SHARED_LIBRARY_LINK_",
343                                this->LinkLanguage, "_WITH_RUNTIME_PATH");
344     this->LinkWithRuntimePath = this->Makefile->IsOn(var);
345   }
346 
347   // Check the platform policy for missing soname case.
348   this->NoSONameUsesPath =
349     this->Makefile->IsOn("CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME");
350 
351   // Get link type information.
352   this->ComputeLinkTypeInfo();
353 
354   // Setup the link item parser.
355   this->ComputeItemParserInfo();
356 
357   // Setup framework support.
358   this->ComputeFrameworkInfo();
359 
360   // Choose a mode for dealing with shared library dependencies.
361   this->SharedDependencyMode = SharedDepModeNone;
362   if (this->Makefile->IsOn("CMAKE_LINK_DEPENDENT_LIBRARY_FILES")) {
363     this->SharedDependencyMode = SharedDepModeLink;
364   } else if (this->Makefile->IsOn("CMAKE_LINK_DEPENDENT_LIBRARY_DIRS")) {
365     this->SharedDependencyMode = SharedDepModeLibDir;
366   } else if (!this->RPathLinkFlag.empty()) {
367     this->SharedDependencyMode = SharedDepModeDir;
368     this->OrderDependentRPath = cm::make_unique<cmOrderDirectories>(
369       this->GlobalGenerator, target, "dependent library path");
370   }
371 
372   // Add the search path entries requested by the user to path ordering.
373   std::vector<std::string> directories;
374   this->Target->GetLinkDirectories(directories, config, this->LinkLanguage);
375   this->OrderLinkerSearchPath->AddUserDirectories(directories);
376   this->OrderRuntimeSearchPath->AddUserDirectories(directories);
377 
378   // Set up the implicit link directories.
379   this->LoadImplicitLinkInfo();
380   this->OrderLinkerSearchPath->SetImplicitDirectories(this->ImplicitLinkDirs);
381   this->OrderRuntimeSearchPath->SetImplicitDirectories(this->ImplicitLinkDirs);
382   if (this->OrderDependentRPath) {
383     this->OrderDependentRPath->SetImplicitDirectories(this->ImplicitLinkDirs);
384     this->OrderDependentRPath->AddLanguageDirectories(this->RuntimeLinkDirs);
385   }
386 
387   // Decide whether to enable compatible library search path mode.
388   // There exists code that effectively does
389   //
390   //    /path/to/libA.so -lB
391   //
392   // where -lB is meant to link to /path/to/libB.so.  This is broken
393   // because it specified -lB without specifying a link directory (-L)
394   // in which to search for B.  This worked in CMake 2.4 and below
395   // because -L/path/to would be added by the -L/-l split for A.  In
396   // order to support such projects we need to add the directories
397   // containing libraries linked with a full path to the -L path.
398   this->OldLinkDirMode =
399     this->Target->GetPolicyStatusCMP0003() != cmPolicies::NEW;
400   if (this->OldLinkDirMode) {
401     // Construct a mask to not bother with this behavior for link
402     // directories already specified by the user.
403     this->OldLinkDirMask.insert(directories.begin(), directories.end());
404   }
405 
406   this->CMP0060Warn = this->Makefile->PolicyOptionalWarningEnabled(
407     "CMAKE_POLICY_WARNING_CMP0060");
408 }
409 
410 cmComputeLinkInformation::~cmComputeLinkInformation() = default;
411 
AppendValues(std::string & result,std::vector<BT<std::string>> & values)412 void cmComputeLinkInformation::AppendValues(
413   std::string& result, std::vector<BT<std::string>>& values)
414 {
415   for (BT<std::string>& p : values) {
416     if (result.empty()) {
417       result.append(" ");
418     }
419 
420     result.append(p.Value);
421   }
422 }
423 
424 cmComputeLinkInformation::ItemVector const&
GetItems() const425 cmComputeLinkInformation::GetItems() const
426 {
427   return this->Items;
428 }
429 
GetDirectories() const430 std::vector<std::string> const& cmComputeLinkInformation::GetDirectories()
431   const
432 {
433   return this->OrderLinkerSearchPath->GetOrderedDirectories();
434 }
435 
436 std::vector<BT<std::string>>
GetDirectoriesWithBacktraces()437 cmComputeLinkInformation::GetDirectoriesWithBacktraces()
438 {
439   std::vector<BT<std::string>> directoriesWithBacktraces;
440 
441   std::vector<BT<std::string>> targetLinkDirectores =
442     this->Target->GetLinkDirectories(this->Config, this->LinkLanguage);
443 
444   const std::vector<std::string>& orderedDirectories = this->GetDirectories();
445   for (const std::string& dir : orderedDirectories) {
446     auto result =
447       std::find(targetLinkDirectores.begin(), targetLinkDirectores.end(), dir);
448     if (result != targetLinkDirectores.end()) {
449       directoriesWithBacktraces.emplace_back(std::move(*result));
450     } else {
451       directoriesWithBacktraces.emplace_back(dir);
452     }
453   }
454 
455   return directoriesWithBacktraces;
456 }
457 
GetRPathLinkString() const458 std::string cmComputeLinkInformation::GetRPathLinkString() const
459 {
460   // If there is no separate linker runtime search flag (-rpath-link)
461   // there is no reason to compute a string.
462   if (!this->OrderDependentRPath) {
463     return "";
464   }
465 
466   // Construct the linker runtime search path. These MUST NOT contain tokens
467   // such as $ORIGIN, see https://sourceware.org/bugzilla/show_bug.cgi?id=16936
468   return cmJoin(this->OrderDependentRPath->GetOrderedDirectories(), ":");
469 }
470 
GetDepends() const471 std::vector<std::string> const& cmComputeLinkInformation::GetDepends() const
472 {
473   return this->Depends;
474 }
475 
GetFrameworkPaths() const476 std::vector<std::string> const& cmComputeLinkInformation::GetFrameworkPaths()
477   const
478 {
479   return this->FrameworkPaths;
480 }
481 
482 std::set<std::string> const&
GetFrameworkPathsEmitted() const483 cmComputeLinkInformation::GetFrameworkPathsEmitted() const
484 {
485   return this->FrameworkPathsEmitted;
486 }
487 
488 const std::set<const cmGeneratorTarget*>&
GetSharedLibrariesLinked() const489 cmComputeLinkInformation::GetSharedLibrariesLinked() const
490 {
491   return this->SharedLibrariesLinked;
492 }
493 
Compute()494 bool cmComputeLinkInformation::Compute()
495 {
496   // Skip targets that do not link.
497   if (!(this->Target->GetType() == cmStateEnums::EXECUTABLE ||
498         this->Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
499         this->Target->GetType() == cmStateEnums::MODULE_LIBRARY ||
500         this->Target->GetType() == cmStateEnums::STATIC_LIBRARY)) {
501     return false;
502   }
503 
504   // We require a link language for the target.
505   if (this->LinkLanguage.empty()) {
506     cmSystemTools::Error(
507       "CMake can not determine linker language for target: " +
508       this->Target->GetName());
509     return false;
510   }
511 
512   // Compute the ordered link line items.
513   cmComputeLinkDepends cld(this->Target, this->Config);
514   cld.SetOldLinkDirMode(this->OldLinkDirMode);
515   cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute();
516 
517   // Add the link line items.
518   for (cmComputeLinkDepends::LinkEntry const& linkEntry : linkEntries) {
519     if (linkEntry.IsSharedDep) {
520       this->AddSharedDepItem(linkEntry.Item, linkEntry.Target);
521     } else {
522       this->AddItem(linkEntry.Item, linkEntry.Target,
523                     linkEntry.IsObject ? ItemIsObject::Yes : ItemIsObject::No);
524     }
525   }
526 
527   // Restore the target link type so the correct system runtime
528   // libraries are found.
529   cmValue lss = this->Target->GetProperty("LINK_SEARCH_END_STATIC");
530   if (cmIsOn(lss)) {
531     this->SetCurrentLinkType(LinkStatic);
532   } else {
533     this->SetCurrentLinkType(this->StartLinkType);
534   }
535 
536   // Finish listing compatibility paths.
537   if (this->OldLinkDirMode) {
538     // For CMake 2.4 bug-compatibility we need to consider the output
539     // directories of targets linked in another configuration as link
540     // directories.
541     std::set<cmGeneratorTarget const*> const& wrongItems =
542       cld.GetOldWrongConfigItems();
543     for (cmGeneratorTarget const* tgt : wrongItems) {
544       cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(this->Config)
545         ? cmStateEnums::ImportLibraryArtifact
546         : cmStateEnums::RuntimeBinaryArtifact;
547       this->OldLinkDirItems.push_back(
548         tgt->GetFullPath(this->Config, artifact, true));
549     }
550   }
551 
552   // Finish setting up linker search directories.
553   if (!this->FinishLinkerSearchDirectories()) {
554     return false;
555   }
556 
557   // Add implicit language runtime libraries and directories.
558   this->AddImplicitLinkInfo();
559 
560   if (!this->CMP0060WarnItems.empty()) {
561     std::ostringstream w;
562     /* clang-format off */
563     w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0060) << "\n"
564       "Some library files are in directories implicitly searched by "
565       "the linker when invoked for " << this->LinkLanguage << ":\n"
566       " " << cmJoin(this->CMP0060WarnItems, "\n ") << "\n"
567       "For compatibility with older versions of CMake, the generated "
568       "link line will ask the linker to search for these by library "
569       "name."
570       ;
571     /* clang-format on */
572     this->CMakeInstance->IssueMessage(MessageType::AUTHOR_WARNING, w.str(),
573                                       this->Target->GetBacktrace());
574   }
575 
576   return true;
577 }
578 
AddImplicitLinkInfo()579 void cmComputeLinkInformation::AddImplicitLinkInfo()
580 {
581   // The link closure lists all languages whose implicit info is needed.
582   cmGeneratorTarget::LinkClosure const* lc =
583     this->Target->GetLinkClosure(this->Config);
584   for (std::string const& li : lc->Languages) {
585 
586     if (li == "CUDA" || li == "HIP") {
587       // These need to go before the other implicit link information
588       // as they could require symbols from those other library
589       // Currently restricted as CUDA and HIP are the only languages
590       // we have documented runtime behavior controls for
591       this->AddRuntimeLinkLibrary(li);
592     }
593 
594     // Skip those of the linker language.  They are implicit.
595     if (li != this->LinkLanguage) {
596       this->AddImplicitLinkInfo(li);
597     }
598   }
599 }
600 
AddRuntimeLinkLibrary(std::string const & lang)601 void cmComputeLinkInformation::AddRuntimeLinkLibrary(std::string const& lang)
602 {
603   std::string const& runtimeLibrary =
604     this->Target->GetRuntimeLinkLibrary(lang, this->Config);
605   if (runtimeLibrary.empty()) {
606     return;
607   }
608   if (cmValue runtimeLinkOptions = this->Makefile->GetDefinition(
609         "CMAKE_" + lang + "_RUNTIME_LIBRARY_LINK_OPTIONS_" + runtimeLibrary)) {
610     std::vector<std::string> libsVec = cmExpandedList(*runtimeLinkOptions);
611     for (std::string const& i : libsVec) {
612       if (!cm::contains(this->ImplicitLinkLibs, i)) {
613         this->AddItem(i, nullptr);
614       }
615     }
616   }
617 }
618 
AddImplicitLinkInfo(std::string const & lang)619 void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang)
620 {
621   // Add libraries for this language that are not implied by the
622   // linker language.
623   std::string libVar = cmStrCat("CMAKE_", lang, "_IMPLICIT_LINK_LIBRARIES");
624   if (cmValue libs = this->Makefile->GetDefinition(libVar)) {
625     std::vector<std::string> libsVec = cmExpandedList(*libs);
626     for (std::string const& i : libsVec) {
627       if (!cm::contains(this->ImplicitLinkLibs, i)) {
628         this->AddItem(i, nullptr);
629       }
630     }
631   }
632 
633   // Add linker search paths for this language that are not
634   // implied by the linker language.
635   std::string dirVar = cmStrCat("CMAKE_", lang, "_IMPLICIT_LINK_DIRECTORIES");
636   if (cmValue dirs = this->Makefile->GetDefinition(dirVar)) {
637     std::vector<std::string> dirsVec = cmExpandedList(*dirs);
638     this->OrderLinkerSearchPath->AddLanguageDirectories(dirsVec);
639   }
640 }
641 
AddItem(BT<std::string> const & item,cmGeneratorTarget const * tgt,ItemIsObject isObject)642 void cmComputeLinkInformation::AddItem(BT<std::string> const& item,
643                                        cmGeneratorTarget const* tgt,
644                                        ItemIsObject isObject)
645 {
646   // Compute the proper name to use to link this library.
647   const std::string& config = this->Config;
648   bool impexe = (tgt && tgt->IsExecutableWithExports());
649   if (impexe && !tgt->HasImportLibrary(config) && !this->LoaderFlag) {
650     // Skip linking to executables on platforms with no import
651     // libraries or loader flags.
652     return;
653   }
654 
655   if (tgt && tgt->IsLinkable()) {
656     // This is a CMake target.  Ask the target for its real name.
657     if (impexe && this->LoaderFlag) {
658       // This link item is an executable that may provide symbols
659       // used by this target.  A special flag is needed on this
660       // platform.  Add it now.
661       std::string linkItem = this->LoaderFlag;
662       cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config)
663         ? cmStateEnums::ImportLibraryArtifact
664         : cmStateEnums::RuntimeBinaryArtifact;
665 
666       std::string exe = tgt->GetFullPath(config, artifact, true);
667       linkItem += exe;
668       this->Items.emplace_back(BT<std::string>(linkItem, item.Backtrace),
669                                ItemIsPath::Yes, ItemIsObject::No, tgt);
670       this->Depends.push_back(std::move(exe));
671     } else if (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
672       // Add the interface library as an item so it can be considered as part
673       // of COMPATIBLE_INTERFACE_ enforcement.  The generators will ignore
674       // this for the actual link line.
675       this->Items.emplace_back(std::string(), ItemIsPath::No, ItemIsObject::No,
676                                tgt);
677 
678       // Also add the item the interface specifies to be used in its place.
679       std::string const& libName = tgt->GetImportedLibName(config);
680       if (!libName.empty()) {
681         this->AddItem(BT<std::string>(libName, item.Backtrace), nullptr);
682       }
683     } else if (tgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
684       // Ignore object library!
685       // Its object-files should already have been extracted for linking.
686     } else {
687       // Decide whether to use an import library.
688       cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config)
689         ? cmStateEnums::ImportLibraryArtifact
690         : cmStateEnums::RuntimeBinaryArtifact;
691 
692       // Pass the full path to the target file.
693       BT<std::string> lib = BT<std::string>(
694         tgt->GetFullPath(config, artifact, true), item.Backtrace);
695       if (tgt->Target->IsAIX() && cmHasLiteralSuffix(lib.Value, "-NOTFOUND") &&
696           artifact == cmStateEnums::ImportLibraryArtifact) {
697         // This is an imported executable on AIX that has ENABLE_EXPORTS
698         // but not IMPORTED_IMPLIB.  CMake used to produce and accept such
699         // imported executables on AIX before we taught it to use linker
700         // import files.  For compatibility, simply skip linking to this
701         // executable as we did before.  It works with runtime linking.
702         return;
703       }
704       if (!this->LinkDependsNoShared ||
705           tgt->GetType() != cmStateEnums::SHARED_LIBRARY) {
706         this->Depends.push_back(lib.Value);
707       }
708 
709       this->AddTargetItem(lib, tgt);
710       this->AddLibraryRuntimeInfo(lib.Value, tgt);
711       if (tgt && tgt->GetType() == cmStateEnums::SHARED_LIBRARY &&
712           this->Target->IsDLLPlatform()) {
713         this->AddRuntimeDLL(tgt);
714       }
715     }
716   } else {
717     // This is not a CMake target.  Use the name given.
718     if (cmSystemTools::FileIsFullPath(item.Value)) {
719       if (cmSystemTools::IsPathToFramework(item.Value) &&
720           this->Makefile->IsOn("APPLE")) {
721         // This is a framework.
722         this->AddFrameworkItem(item.Value);
723       } else if (cmSystemTools::FileIsDirectory(item.Value)) {
724         // This is a directory.
725         this->DropDirectoryItem(item.Value);
726       } else {
727         // Use the full path given to the library file.
728         this->Depends.push_back(item.Value);
729         this->AddFullItem(item, isObject);
730         this->AddLibraryRuntimeInfo(item.Value);
731       }
732     } else {
733       // This is a library or option specified by the user.
734       this->AddUserItem(item, true);
735     }
736   }
737 }
738 
AddSharedDepItem(BT<std::string> const & item,const cmGeneratorTarget * tgt)739 void cmComputeLinkInformation::AddSharedDepItem(BT<std::string> const& item,
740                                                 const cmGeneratorTarget* tgt)
741 {
742   // Record dependencies on DLLs.
743   if (tgt && tgt->GetType() == cmStateEnums::SHARED_LIBRARY &&
744       this->Target->IsDLLPlatform() &&
745       this->SharedDependencyMode != SharedDepModeLink) {
746     this->AddRuntimeDLL(tgt);
747   }
748 
749   // If dropping shared library dependencies, ignore them.
750   if (this->SharedDependencyMode == SharedDepModeNone) {
751     return;
752   }
753 
754   // The user may have incorrectly named an item.  Skip items that are
755   // not full paths to shared libraries.
756   if (tgt) {
757     // The target will provide a full path.  Make sure it is a shared
758     // library.
759     if (tgt->GetType() != cmStateEnums::SHARED_LIBRARY) {
760       return;
761     }
762   } else {
763     // Skip items that are not full paths.  We will not be able to
764     // reliably specify them.
765     if (!cmSystemTools::FileIsFullPath(item.Value)) {
766       return;
767     }
768 
769     // Get the name of the library from the file name.
770     std::string file = cmSystemTools::GetFilenameName(item.Value);
771     if (!this->ExtractSharedLibraryName.find(file)) {
772       // This is not the name of a shared library.
773       return;
774     }
775   }
776 
777   // If in linking mode, just link to the shared library.
778   if (this->SharedDependencyMode == SharedDepModeLink) {
779     this->AddItem(item, tgt);
780     return;
781   }
782 
783   // Get a full path to the dependent shared library.
784   // Add it to the runtime path computation so that the target being
785   // linked will be able to find it.
786   std::string lib;
787   if (tgt) {
788     cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(this->Config)
789       ? cmStateEnums::ImportLibraryArtifact
790       : cmStateEnums::RuntimeBinaryArtifact;
791     lib = tgt->GetFullPath(this->Config, artifact);
792     this->AddLibraryRuntimeInfo(lib, tgt);
793   } else {
794     lib = item.Value;
795     this->AddLibraryRuntimeInfo(lib);
796   }
797 
798   // Check if we need to include the dependent shared library in other
799   // path ordering.
800   cmOrderDirectories* order = nullptr;
801   if (this->SharedDependencyMode == SharedDepModeLibDir &&
802       !this->LinkWithRuntimePath /* AddLibraryRuntimeInfo adds it */) {
803     // Add the item to the linker search path.
804     order = this->OrderLinkerSearchPath.get();
805   } else if (this->SharedDependencyMode == SharedDepModeDir) {
806     // Add the item to the separate dependent library search path.
807     order = this->OrderDependentRPath.get();
808   }
809   if (order) {
810     if (tgt) {
811       std::string soName = tgt->GetSOName(this->Config);
812       const char* soname = soName.empty() ? nullptr : soName.c_str();
813       order->AddRuntimeLibrary(lib, soname);
814     } else {
815       order->AddRuntimeLibrary(lib);
816     }
817   }
818 }
819 
AddRuntimeDLL(cmGeneratorTarget const * tgt)820 void cmComputeLinkInformation::AddRuntimeDLL(cmGeneratorTarget const* tgt)
821 {
822   if (std::find(this->RuntimeDLLs.begin(), this->RuntimeDLLs.end(), tgt) ==
823       this->RuntimeDLLs.end()) {
824     this->RuntimeDLLs.emplace_back(tgt);
825   }
826 }
827 
ComputeLinkTypeInfo()828 void cmComputeLinkInformation::ComputeLinkTypeInfo()
829 {
830   // Check whether archives may actually be shared libraries.
831   this->ArchivesMayBeShared =
832     this->CMakeInstance->GetState()->GetGlobalPropertyAsBool(
833       "TARGET_ARCHIVES_MAY_BE_SHARED_LIBS");
834 
835   // First assume we cannot do link type stuff.
836   this->LinkTypeEnabled = false;
837 
838   // Lookup link type selection flags.
839   cmValue static_link_type_flag = nullptr;
840   cmValue shared_link_type_flag = nullptr;
841   const char* target_type_str = nullptr;
842   switch (this->Target->GetType()) {
843     case cmStateEnums::EXECUTABLE:
844       target_type_str = "EXE";
845       break;
846     case cmStateEnums::SHARED_LIBRARY:
847       target_type_str = "SHARED_LIBRARY";
848       break;
849     case cmStateEnums::MODULE_LIBRARY:
850       target_type_str = "SHARED_MODULE";
851       break;
852     default:
853       break;
854   }
855   if (target_type_str) {
856     std::string static_link_type_flag_var =
857       cmStrCat("CMAKE_", target_type_str, "_LINK_STATIC_", this->LinkLanguage,
858                "_FLAGS");
859     static_link_type_flag =
860       this->Makefile->GetDefinition(static_link_type_flag_var);
861 
862     std::string shared_link_type_flag_var =
863       cmStrCat("CMAKE_", target_type_str, "_LINK_DYNAMIC_", this->LinkLanguage,
864                "_FLAGS");
865     shared_link_type_flag =
866       this->Makefile->GetDefinition(shared_link_type_flag_var);
867   }
868 
869   // We can support link type switching only if all needed flags are
870   // known.
871   if (cmNonempty(static_link_type_flag) && cmNonempty(shared_link_type_flag)) {
872     this->LinkTypeEnabled = true;
873     this->StaticLinkTypeFlag = *static_link_type_flag;
874     this->SharedLinkTypeFlag = *shared_link_type_flag;
875   }
876 
877   // Lookup the starting link type from the target (linked statically?).
878   cmValue lss = this->Target->GetProperty("LINK_SEARCH_START_STATIC");
879   this->StartLinkType = cmIsOn(lss) ? LinkStatic : LinkShared;
880   this->CurrentLinkType = this->StartLinkType;
881 }
882 
ComputeItemParserInfo()883 void cmComputeLinkInformation::ComputeItemParserInfo()
884 {
885   // Get possible library name prefixes.
886   cmMakefile* mf = this->Makefile;
887   this->AddLinkPrefix(mf->GetSafeDefinition("CMAKE_STATIC_LIBRARY_PREFIX"));
888   this->AddLinkPrefix(mf->GetSafeDefinition("CMAKE_SHARED_LIBRARY_PREFIX"));
889 
890   // Import library names should be matched and treated as shared
891   // libraries for the purposes of linking.
892   this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX"),
893                          LinkShared);
894   this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_STATIC_LIBRARY_SUFFIX"),
895                          LinkStatic);
896   this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_SHARED_LIBRARY_SUFFIX"),
897                          LinkShared);
898   this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX"),
899                          LinkUnknown);
900   if (cmValue linkSuffixes =
901         mf->GetDefinition("CMAKE_EXTRA_LINK_EXTENSIONS")) {
902     std::vector<std::string> linkSuffixVec = cmExpandedList(*linkSuffixes);
903     for (std::string const& i : linkSuffixVec) {
904       this->AddLinkExtension(i, LinkUnknown);
905     }
906   }
907   if (cmValue sharedSuffixes =
908         mf->GetDefinition("CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES")) {
909     std::vector<std::string> sharedSuffixVec = cmExpandedList(*sharedSuffixes);
910     for (std::string const& i : sharedSuffixVec) {
911       this->AddLinkExtension(i, LinkShared);
912     }
913   }
914 
915   // Compute a regex to match link extensions.
916   std::string libext =
917     this->CreateExtensionRegex(this->LinkExtensions, LinkUnknown);
918 
919   // Create regex to remove any library extension.
920   std::string reg("(.*)");
921   reg += libext;
922   this->OrderLinkerSearchPath->SetLinkExtensionInfo(this->LinkExtensions, reg);
923 
924   // Create a regex to match a library name.  Match index 1 will be
925   // the prefix if it exists and empty otherwise.  Match index 2 will
926   // be the library name.  Match index 3 will be the library
927   // extension.
928   reg = "^(";
929   for (std::string const& p : this->LinkPrefixes) {
930     reg += p;
931     reg += "|";
932   }
933   reg += ")";
934   reg += "([^/:]*)";
935 
936   // Create a regex to match any library name.
937   std::string reg_any = cmStrCat(reg, libext);
938 #ifdef CM_COMPUTE_LINK_INFO_DEBUG
939   fprintf(stderr, "any regex [%s]\n", reg_any.c_str());
940 #endif
941   this->ExtractAnyLibraryName.compile(reg_any);
942 
943   // Create a regex to match static library names.
944   if (!this->StaticLinkExtensions.empty()) {
945     std::string reg_static = cmStrCat(
946       reg, this->CreateExtensionRegex(this->StaticLinkExtensions, LinkStatic));
947 #ifdef CM_COMPUTE_LINK_INFO_DEBUG
948     fprintf(stderr, "static regex [%s]\n", reg_static.c_str());
949 #endif
950     this->ExtractStaticLibraryName.compile(reg_static);
951   }
952 
953   // Create a regex to match shared library names.
954   if (!this->SharedLinkExtensions.empty()) {
955     std::string reg_shared = reg;
956     this->SharedRegexString =
957       this->CreateExtensionRegex(this->SharedLinkExtensions, LinkShared);
958     reg_shared += this->SharedRegexString;
959 #ifdef CM_COMPUTE_LINK_INFO_DEBUG
960     fprintf(stderr, "shared regex [%s]\n", reg_shared.c_str());
961 #endif
962     this->ExtractSharedLibraryName.compile(reg_shared);
963   }
964 }
965 
AddLinkPrefix(std::string const & p)966 void cmComputeLinkInformation::AddLinkPrefix(std::string const& p)
967 {
968   if (!p.empty()) {
969     this->LinkPrefixes.insert(p);
970   }
971 }
972 
AddLinkExtension(std::string const & e,LinkType type)973 void cmComputeLinkInformation::AddLinkExtension(std::string const& e,
974                                                 LinkType type)
975 {
976   if (!e.empty()) {
977     if (type == LinkStatic) {
978       this->StaticLinkExtensions.emplace_back(e);
979     }
980     if (type == LinkShared) {
981       this->SharedLinkExtensions.emplace_back(e);
982     }
983     this->LinkExtensions.emplace_back(e);
984   }
985 }
986 
987 // XXX(clang-tidy): This method's const-ness is platform dependent, so we
988 // cannot make it `const` as `clang-tidy` wants us to.
989 // NOLINTNEXTLINE(readability-make-member-function-const)
CreateExtensionRegex(std::vector<std::string> const & exts,LinkType type)990 std::string cmComputeLinkInformation::CreateExtensionRegex(
991   std::vector<std::string> const& exts, LinkType type)
992 {
993   // Build a list of extension choices.
994   std::string libext = "(";
995   const char* sep = "";
996   for (std::string const& i : exts) {
997     // Separate this choice from the previous one.
998     libext += sep;
999     sep = "|";
1000 
1001     // Store this extension choice with the "." escaped.
1002     libext += "\\";
1003 #if defined(_WIN32) && !defined(__CYGWIN__)
1004     libext += this->NoCaseExpression(i);
1005 #else
1006     libext += i;
1007 #endif
1008   }
1009 
1010   // Finish the list.
1011   libext += ")";
1012 
1013   // Add an optional OpenBSD-style version or major.minor.version component.
1014   if (this->OpenBSD || type == LinkShared) {
1015     libext += "(\\.[0-9]+)*";
1016   }
1017 
1018   libext += "$";
1019   return libext;
1020 }
1021 
NoCaseExpression(std::string const & str)1022 std::string cmComputeLinkInformation::NoCaseExpression(std::string const& str)
1023 {
1024   std::string ret;
1025   ret.reserve(str.size() * 4);
1026   for (char c : str) {
1027     if (c == '.') {
1028       ret += c;
1029     } else {
1030       ret += '[';
1031       ret += static_cast<char>(tolower(c));
1032       ret += static_cast<char>(toupper(c));
1033       ret += ']';
1034     }
1035   }
1036   return ret;
1037 }
1038 
SetCurrentLinkType(LinkType lt)1039 void cmComputeLinkInformation::SetCurrentLinkType(LinkType lt)
1040 {
1041   // If we are changing the current link type add the flag to tell the
1042   // linker about it.
1043   if (this->CurrentLinkType != lt) {
1044     this->CurrentLinkType = lt;
1045 
1046     if (this->LinkTypeEnabled) {
1047       switch (this->CurrentLinkType) {
1048         case LinkStatic:
1049           this->Items.emplace_back(this->StaticLinkTypeFlag, ItemIsPath::No);
1050           break;
1051         case LinkShared:
1052           this->Items.emplace_back(this->SharedLinkTypeFlag, ItemIsPath::No);
1053           break;
1054         default:
1055           break;
1056       }
1057     }
1058   }
1059 }
1060 
AddTargetItem(BT<std::string> const & item,cmGeneratorTarget const * target)1061 void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item,
1062                                              cmGeneratorTarget const* target)
1063 {
1064   // This is called to handle a link item that is a full path to a target.
1065   // If the target is not a static library make sure the link type is
1066   // shared.  This is because dynamic-mode linking can handle both
1067   // shared and static libraries but static-mode can handle only
1068   // static libraries.  If a previous user item changed the link type
1069   // to static we need to make sure it is back to shared.
1070   if (target->GetType() != cmStateEnums::STATIC_LIBRARY) {
1071     this->SetCurrentLinkType(LinkShared);
1072   }
1073 
1074   // Keep track of shared library targets linked.
1075   if (target->GetType() == cmStateEnums::SHARED_LIBRARY) {
1076     this->SharedLibrariesLinked.insert(target);
1077   }
1078 
1079   // Handle case of an imported shared library with no soname.
1080   if (this->NoSONameUsesPath &&
1081       target->IsImportedSharedLibWithoutSOName(this->Config)) {
1082     this->AddSharedLibNoSOName(item.Value);
1083     return;
1084   }
1085 
1086   // For compatibility with CMake 2.4 include the item's directory in
1087   // the linker search path.
1088   if (this->OldLinkDirMode && !target->IsFrameworkOnApple() &&
1089       !cm::contains(this->OldLinkDirMask,
1090                     cmSystemTools::GetFilenamePath(item.Value))) {
1091     this->OldLinkDirItems.push_back(item.Value);
1092   }
1093 
1094   // Now add the full path to the library.
1095   this->Items.emplace_back(item, ItemIsPath::Yes, ItemIsObject::No, target);
1096 }
1097 
AddFullItem(BT<std::string> const & item,ItemIsObject isObject)1098 void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item,
1099                                            ItemIsObject isObject)
1100 {
1101   // Check for the implicit link directory special case.
1102   if (this->CheckImplicitDirItem(item.Value)) {
1103     return;
1104   }
1105 
1106   // Check for case of shared library with no builtin soname.
1107   if (this->NoSONameUsesPath && this->CheckSharedLibNoSOName(item.Value)) {
1108     return;
1109   }
1110 
1111   // Full path libraries should specify a valid library file name.
1112   // See documentation of CMP0008.
1113   std::string generator = this->GlobalGenerator->GetName();
1114   if (this->Target->GetPolicyStatusCMP0008() != cmPolicies::NEW &&
1115       (generator.find("Visual Studio") != std::string::npos ||
1116        generator.find("Xcode") != std::string::npos)) {
1117     std::string file = cmSystemTools::GetFilenameName(item.Value);
1118     if (!this->ExtractAnyLibraryName.find(file)) {
1119       this->HandleBadFullItem(item.Value, file);
1120       return;
1121     }
1122   }
1123 
1124   // This is called to handle a link item that is a full path.
1125   // If the target is not a static library make sure the link type is
1126   // shared.  This is because dynamic-mode linking can handle both
1127   // shared and static libraries but static-mode can handle only
1128   // static libraries.  If a previous user item changed the link type
1129   // to static we need to make sure it is back to shared.
1130   if (this->LinkTypeEnabled) {
1131     std::string name = cmSystemTools::GetFilenameName(item.Value);
1132     if (this->ExtractSharedLibraryName.find(name)) {
1133       this->SetCurrentLinkType(LinkShared);
1134     } else if (!this->ExtractStaticLibraryName.find(item.Value)) {
1135       // We cannot determine the type.  Assume it is the target's
1136       // default type.
1137       this->SetCurrentLinkType(this->StartLinkType);
1138     }
1139   }
1140 
1141   // For compatibility with CMake 2.4 include the item's directory in
1142   // the linker search path.
1143   if (this->OldLinkDirMode &&
1144       !cm::contains(this->OldLinkDirMask,
1145                     cmSystemTools::GetFilenamePath(item.Value))) {
1146     this->OldLinkDirItems.push_back(item.Value);
1147   }
1148 
1149   // Now add the full path to the library.
1150   this->Items.emplace_back(item, ItemIsPath::Yes, isObject);
1151 }
1152 
CheckImplicitDirItem(std::string const & item)1153 bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item)
1154 {
1155   // We only switch to a pathless item if the link type may be
1156   // enforced.  Fortunately only platforms that support link types
1157   // seem to have magic per-architecture implicit link directories.
1158   if (!this->LinkTypeEnabled) {
1159     return false;
1160   }
1161 
1162   // Check if this item is in an implicit link directory.
1163   std::string dir = cmSystemTools::GetFilenamePath(item);
1164   if (!cm::contains(this->ImplicitLinkDirs, dir)) {
1165     // Only libraries in implicit link directories are converted to
1166     // pathless items.
1167     return false;
1168   }
1169 
1170   // Only apply the policy below if the library file is one that can
1171   // be found by the linker.
1172   std::string file = cmSystemTools::GetFilenameName(item);
1173   if (!this->ExtractAnyLibraryName.find(file)) {
1174     return false;
1175   }
1176 
1177   // Check the policy for whether we should use the approach below.
1178   switch (this->Target->GetPolicyStatusCMP0060()) {
1179     case cmPolicies::WARN:
1180       if (this->CMP0060Warn) {
1181         // Print the warning at most once for this item.
1182         std::string const& wid = "CMP0060-WARNING-GIVEN-" + item;
1183         if (!this->CMakeInstance->GetPropertyAsBool(wid)) {
1184           this->CMakeInstance->SetProperty(wid, "1");
1185           this->CMP0060WarnItems.insert(item);
1186         }
1187       }
1188       CM_FALLTHROUGH;
1189     case cmPolicies::OLD:
1190       break;
1191     case cmPolicies::REQUIRED_ALWAYS:
1192     case cmPolicies::REQUIRED_IF_USED:
1193     case cmPolicies::NEW:
1194       return false;
1195   }
1196 
1197   // Many system linkers support multiple architectures by
1198   // automatically selecting the implicit linker search path for the
1199   // current architecture.  If the library appears in an implicit link
1200   // directory then just report the file name without the directory
1201   // portion.  This will allow the system linker to locate the proper
1202   // library for the architecture at link time.
1203   this->AddUserItem(file, false);
1204 
1205   // Make sure the link directory ordering will find the library.
1206   this->OrderLinkerSearchPath->AddLinkLibrary(item);
1207 
1208   return true;
1209 }
1210 
AddUserItem(BT<std::string> const & item,bool pathNotKnown)1211 void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item,
1212                                            bool pathNotKnown)
1213 {
1214   // This is called to handle a link item that does not match a CMake
1215   // target and is not a full path.  We check here if it looks like a
1216   // library file name to automatically request the proper link type
1217   // from the linker.  For example:
1218   //
1219   //   foo       ==>  -lfoo
1220   //   libfoo.a  ==>  -Wl,-Bstatic -lfoo
1221 
1222   // Pass flags through untouched.
1223   if (item.Value[0] == '-' || item.Value[0] == '$' || item.Value[0] == '`') {
1224     // if this is a -l option then we might need to warn about
1225     // CMP0003 so put it in OldUserFlagItems, if it is not a -l
1226     // or -Wl,-l (-framework -pthread), then allow it without a
1227     // CMP0003 as -L will not affect those other linker flags
1228     if (cmHasLiteralPrefix(item.Value, "-l") ||
1229         cmHasLiteralPrefix(item.Value, "-Wl,-l")) {
1230       // This is a linker option provided by the user.
1231       this->OldUserFlagItems.push_back(item.Value);
1232     }
1233 
1234     // Restore the target link type since this item does not specify
1235     // one.
1236     this->SetCurrentLinkType(this->StartLinkType);
1237 
1238     // Use the item verbatim.
1239     this->Items.emplace_back(item, ItemIsPath::No);
1240     return;
1241   }
1242 
1243   // Parse out the prefix, base, and suffix components of the
1244   // library name.  If the name matches that of a shared or static
1245   // library then set the link type accordingly.
1246   //
1247   // Search for shared library names first because some platforms
1248   // have shared libraries with names that match the static library
1249   // pattern.  For example cygwin and msys use the convention
1250   // libfoo.dll.a for import libraries and libfoo.a for static
1251   // libraries.  On AIX a library with the name libfoo.a can be
1252   // shared!
1253   std::string lib;
1254   if (this->ExtractSharedLibraryName.find(item.Value)) {
1255 // This matches a shared library file name.
1256 #ifdef CM_COMPUTE_LINK_INFO_DEBUG
1257     fprintf(stderr, "shared regex matched [%s] [%s] [%s]\n",
1258             this->ExtractSharedLibraryName.match(1).c_str(),
1259             this->ExtractSharedLibraryName.match(2).c_str(),
1260             this->ExtractSharedLibraryName.match(3).c_str());
1261 #endif
1262     // Set the link type to shared.
1263     this->SetCurrentLinkType(LinkShared);
1264 
1265     // Use just the library name so the linker will search.
1266     lib = this->ExtractSharedLibraryName.match(2);
1267   } else if (this->ExtractStaticLibraryName.find(item.Value)) {
1268 // This matches a static library file name.
1269 #ifdef CM_COMPUTE_LINK_INFO_DEBUG
1270     fprintf(stderr, "static regex matched [%s] [%s] [%s]\n",
1271             this->ExtractStaticLibraryName.match(1).c_str(),
1272             this->ExtractStaticLibraryName.match(2).c_str(),
1273             this->ExtractStaticLibraryName.match(3).c_str());
1274 #endif
1275     // Set the link type to static.
1276     this->SetCurrentLinkType(LinkStatic);
1277 
1278     // Use just the library name so the linker will search.
1279     lib = this->ExtractStaticLibraryName.match(2);
1280   } else if (this->ExtractAnyLibraryName.find(item.Value)) {
1281 // This matches a library file name.
1282 #ifdef CM_COMPUTE_LINK_INFO_DEBUG
1283     fprintf(stderr, "any regex matched [%s] [%s] [%s]\n",
1284             this->ExtractAnyLibraryName.match(1).c_str(),
1285             this->ExtractAnyLibraryName.match(2).c_str(),
1286             this->ExtractAnyLibraryName.match(3).c_str());
1287 #endif
1288     // Restore the target link type since this item does not specify
1289     // one.
1290     this->SetCurrentLinkType(this->StartLinkType);
1291 
1292     // Use just the library name so the linker will search.
1293     lib = this->ExtractAnyLibraryName.match(2);
1294   } else {
1295     // This is a name specified by the user.
1296     if (pathNotKnown) {
1297       this->OldUserFlagItems.push_back(item.Value);
1298     }
1299 
1300     // We must ask the linker to search for a library with this name.
1301     // Restore the target link type since this item does not specify
1302     // one.
1303     this->SetCurrentLinkType(this->StartLinkType);
1304     lib = item.Value;
1305   }
1306 
1307   // Create an option to ask the linker to search for the library.
1308   std::string out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix);
1309   this->Items.emplace_back(BT<std::string>(out, item.Backtrace),
1310                            ItemIsPath::No);
1311 
1312   // Here we could try to find the library the linker will find and
1313   // add a runtime information entry for it.  It would probably not be
1314   // reliable and we want to encourage use of full paths for library
1315   // specification.
1316 }
1317 
AddFrameworkItem(std::string const & item)1318 void cmComputeLinkInformation::AddFrameworkItem(std::string const& item)
1319 {
1320   // Try to separate the framework name and path.
1321   if (!this->SplitFramework.find(item)) {
1322     std::ostringstream e;
1323     e << "Could not parse framework path \"" << item << "\" "
1324       << "linked by target " << this->Target->GetName() << ".";
1325     cmSystemTools::Error(e.str());
1326     return;
1327   }
1328 
1329   std::string fw_path = this->SplitFramework.match(1);
1330   std::string fw = this->SplitFramework.match(2);
1331   std::string full_fw = cmStrCat(fw_path, '/', fw, ".framework/", fw);
1332 
1333   // Add the directory portion to the framework search path.
1334   this->AddFrameworkPath(fw_path);
1335 
1336   // add runtime information
1337   this->AddLibraryRuntimeInfo(full_fw);
1338 
1339   if (this->GlobalGenerator->IsXcode()) {
1340     // Add framework path - it will be handled by Xcode after it's added to
1341     // "Link Binary With Libraries" build phase
1342     this->Items.emplace_back(item, ItemIsPath::Yes);
1343   } else {
1344     // Add the item using the -framework option.
1345     this->Items.emplace_back(std::string("-framework"), ItemIsPath::No);
1346     cmOutputConverter converter(this->Makefile->GetStateSnapshot());
1347     fw = converter.EscapeForShell(fw);
1348     this->Items.emplace_back(fw, ItemIsPath::No);
1349   }
1350 }
1351 
DropDirectoryItem(std::string const & item)1352 void cmComputeLinkInformation::DropDirectoryItem(std::string const& item)
1353 {
1354   // A full path to a directory was found as a link item.  Warn the
1355   // user.
1356   std::ostringstream e;
1357   e << "WARNING: Target \"" << this->Target->GetName()
1358     << "\" requests linking to directory \"" << item << "\".  "
1359     << "Targets may link only to libraries.  "
1360     << "CMake is dropping the item.";
1361   cmSystemTools::Message(e.str());
1362 }
1363 
ComputeFrameworkInfo()1364 void cmComputeLinkInformation::ComputeFrameworkInfo()
1365 {
1366   // Avoid adding implicit framework paths.
1367   std::vector<std::string> implicitDirVec;
1368 
1369   // Get platform-wide implicit directories.
1370   this->Makefile->GetDefExpandList(
1371     "CMAKE_PLATFORM_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES", implicitDirVec);
1372 
1373   // Get language-specific implicit directories.
1374   std::string implicitDirVar = cmStrCat(
1375     "CMAKE_", this->LinkLanguage, "_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES");
1376   this->Makefile->GetDefExpandList(implicitDirVar, implicitDirVec);
1377 
1378   this->FrameworkPathsEmitted.insert(implicitDirVec.begin(),
1379                                      implicitDirVec.end());
1380 
1381   // Regular expression to extract a framework path and name.
1382   this->SplitFramework.compile("(.*)/(.*)\\.framework$");
1383 }
1384 
AddFrameworkPath(std::string const & p)1385 void cmComputeLinkInformation::AddFrameworkPath(std::string const& p)
1386 {
1387   if (this->FrameworkPathsEmitted.insert(p).second) {
1388     this->FrameworkPaths.push_back(p);
1389   }
1390 }
1391 
CheckSharedLibNoSOName(std::string const & item)1392 bool cmComputeLinkInformation::CheckSharedLibNoSOName(std::string const& item)
1393 {
1394   // This platform will use the path to a library as its soname if the
1395   // library is given via path and was not built with an soname.  If
1396   // this is a shared library that might be the case.
1397   std::string file = cmSystemTools::GetFilenameName(item);
1398   if (this->ExtractSharedLibraryName.find(file)) {
1399     // If we can guess the soname fairly reliably then assume the
1400     // library has one.  Otherwise assume the library has no builtin
1401     // soname.
1402     std::string soname;
1403     if (!cmSystemTools::GuessLibrarySOName(item, soname)) {
1404       this->AddSharedLibNoSOName(item);
1405       return true;
1406     }
1407   }
1408   return false;
1409 }
1410 
AddSharedLibNoSOName(std::string const & item)1411 void cmComputeLinkInformation::AddSharedLibNoSOName(std::string const& item)
1412 {
1413   // We have a full path to a shared library with no soname.  We need
1414   // to ask the linker to locate the item because otherwise the path
1415   // we give to it will be embedded in the target linked.  Then at
1416   // runtime the dynamic linker will search for the library using the
1417   // path instead of just the name.
1418   std::string file = cmSystemTools::GetFilenameName(item);
1419   this->AddUserItem(file, false);
1420 
1421   // Make sure the link directory ordering will find the library.
1422   this->OrderLinkerSearchPath->AddLinkLibrary(item);
1423 }
1424 
HandleBadFullItem(std::string const & item,std::string const & file)1425 void cmComputeLinkInformation::HandleBadFullItem(std::string const& item,
1426                                                  std::string const& file)
1427 {
1428   // Do not depend on things that do not exist.
1429   auto i = std::find(this->Depends.begin(), this->Depends.end(), item);
1430   if (i != this->Depends.end()) {
1431     this->Depends.erase(i);
1432   }
1433 
1434   // Tell the linker to search for the item and provide the proper
1435   // path for it.  Do not contribute to any CMP0003 warning (do not
1436   // put in OldLinkDirItems or OldUserFlagItems).
1437   this->AddUserItem(file, false);
1438   this->OrderLinkerSearchPath->AddLinkLibrary(item);
1439 
1440   // Produce any needed message.
1441   switch (this->Target->GetPolicyStatusCMP0008()) {
1442     case cmPolicies::WARN: {
1443       // Print the warning at most once for this item.
1444       std::string wid = cmStrCat("CMP0008-WARNING-GIVEN-", item);
1445       if (!this->CMakeInstance->GetState()->GetGlobalPropertyAsBool(wid)) {
1446         this->CMakeInstance->GetState()->SetGlobalProperty(wid, "1");
1447         std::ostringstream w;
1448         /* clang-format off */
1449         w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0008) << "\n"
1450           << "Target \"" << this->Target->GetName() << "\" links to item\n"
1451           << "  " << item << "\n"
1452           << "which is a full-path but not a valid library file name.";
1453         /* clang-format on */
1454         this->CMakeInstance->IssueMessage(MessageType::AUTHOR_WARNING, w.str(),
1455                                           this->Target->GetBacktrace());
1456       }
1457     }
1458       CM_FALLTHROUGH;
1459     case cmPolicies::OLD: // NOLINT(bugprone-branch-clone)
1460       // OLD behavior does not warn.
1461       break;
1462     case cmPolicies::NEW:
1463       // NEW behavior will not get here.
1464       break;
1465     case cmPolicies::REQUIRED_IF_USED:
1466     case cmPolicies::REQUIRED_ALWAYS: {
1467       std::ostringstream e;
1468       /* clang-format off */
1469       e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0008) << "\n"
1470           << "Target \"" << this->Target->GetName() << "\" links to item\n"
1471           << "  " << item << "\n"
1472           << "which is a full-path but not a valid library file name.";
1473       /* clang-format on */
1474       this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(),
1475                                         this->Target->GetBacktrace());
1476     } break;
1477   }
1478 }
1479 
FinishLinkerSearchDirectories()1480 bool cmComputeLinkInformation::FinishLinkerSearchDirectories()
1481 {
1482   // Support broken projects if necessary.
1483   if (this->OldLinkDirItems.empty() || this->OldUserFlagItems.empty() ||
1484       !this->OldLinkDirMode) {
1485     return true;
1486   }
1487 
1488   // Enforce policy constraints.
1489   switch (this->Target->GetPolicyStatusCMP0003()) {
1490     case cmPolicies::WARN:
1491       if (!this->CMakeInstance->GetState()->GetGlobalPropertyAsBool(
1492             "CMP0003-WARNING-GIVEN")) {
1493         this->CMakeInstance->GetState()->SetGlobalProperty(
1494           "CMP0003-WARNING-GIVEN", "1");
1495         std::ostringstream w;
1496         this->PrintLinkPolicyDiagnosis(w);
1497         this->CMakeInstance->IssueMessage(MessageType::AUTHOR_WARNING, w.str(),
1498                                           this->Target->GetBacktrace());
1499       }
1500       CM_FALLTHROUGH;
1501     case cmPolicies::OLD:
1502       // OLD behavior is to add the paths containing libraries with
1503       // known full paths as link directories.
1504       break;
1505     case cmPolicies::NEW:
1506       // Should never happen due to assignment of OldLinkDirMode
1507       return true;
1508     case cmPolicies::REQUIRED_IF_USED:
1509     case cmPolicies::REQUIRED_ALWAYS: {
1510       std::ostringstream e;
1511       e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0003) << "\n";
1512       this->PrintLinkPolicyDiagnosis(e);
1513       this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(),
1514                                         this->Target->GetBacktrace());
1515       return false;
1516     }
1517   }
1518 
1519   // Add the link directories for full path items.
1520   for (std::string const& i : this->OldLinkDirItems) {
1521     this->OrderLinkerSearchPath->AddLinkLibrary(i);
1522   }
1523   return true;
1524 }
1525 
PrintLinkPolicyDiagnosis(std::ostream & os)1526 void cmComputeLinkInformation::PrintLinkPolicyDiagnosis(std::ostream& os)
1527 {
1528   // Tell the user what to do.
1529   /* clang-format off */
1530   os << "Policy CMP0003 should be set before this line.  "
1531      << "Add code such as\n"
1532      << "  if(COMMAND cmake_policy)\n"
1533      << "    cmake_policy(SET CMP0003 NEW)\n"
1534      << "  endif(COMMAND cmake_policy)\n"
1535      << "as early as possible but after the most recent call to "
1536      << "cmake_minimum_required or cmake_policy(VERSION).  ";
1537   /* clang-format on */
1538 
1539   // List the items that might need the old-style paths.
1540   os << "This warning appears because target \"" << this->Target->GetName()
1541      << "\" "
1542      << "links to some libraries for which the linker must search:\n";
1543   {
1544     // Format the list of unknown items to be as short as possible while
1545     // still fitting in the allowed width (a true solution would be the
1546     // bin packing problem if we were allowed to change the order).
1547     std::string::size_type max_size = 76;
1548     std::string line;
1549     const char* sep = "  ";
1550     for (std::string const& i : this->OldUserFlagItems) {
1551       // If the addition of another item will exceed the limit then
1552       // output the current line and reset it.  Note that the separator
1553       // is either " " or ", " which is always 2 characters.
1554       if (!line.empty() && (line.size() + i.size() + 2) > max_size) {
1555         os << line << "\n";
1556         sep = "  ";
1557         line.clear();
1558       }
1559       line += sep;
1560       line += i;
1561       // Convert to the other separator.
1562       sep = ", ";
1563     }
1564     if (!line.empty()) {
1565       os << line << "\n";
1566     }
1567   }
1568 
1569   // List the paths old behavior is adding.
1570   os << "and other libraries with known full path:\n";
1571   std::set<std::string> emitted;
1572   for (std::string const& i : this->OldLinkDirItems) {
1573     if (emitted.insert(cmSystemTools::GetFilenamePath(i)).second) {
1574       os << "  " << i << "\n";
1575     }
1576   }
1577 
1578   // Explain.
1579   os << "CMake is adding directories in the second list to the linker "
1580      << "search path in case they are needed to find libraries from the "
1581      << "first list (for backwards compatibility with CMake 2.4).  "
1582      << "Set policy CMP0003 to OLD or NEW to enable or disable this "
1583      << "behavior explicitly.  "
1584      << "Run \"cmake --help-policy CMP0003\" for more information.";
1585 }
1586 
LoadImplicitLinkInfo()1587 void cmComputeLinkInformation::LoadImplicitLinkInfo()
1588 {
1589   std::vector<std::string> implicitDirVec;
1590 
1591   // Get platform-wide implicit directories.
1592   this->Makefile->GetDefExpandList("CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES",
1593                                    implicitDirVec);
1594 
1595   // Append library architecture to all implicit platform directories
1596   // and add them to the set
1597   if (cmValue libraryArch =
1598         this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) {
1599     for (std::string const& i : implicitDirVec) {
1600       this->ImplicitLinkDirs.insert(i + "/" + *libraryArch);
1601     }
1602   }
1603 
1604   // Get language-specific implicit directories.
1605   std::string implicitDirVar =
1606     cmStrCat("CMAKE_", this->LinkLanguage, "_IMPLICIT_LINK_DIRECTORIES");
1607   this->Makefile->GetDefExpandList(implicitDirVar, implicitDirVec);
1608 
1609   // Store implicit link directories.
1610   this->ImplicitLinkDirs.insert(implicitDirVec.begin(), implicitDirVec.end());
1611 
1612   // Get language-specific implicit libraries.
1613   std::vector<std::string> implicitLibVec;
1614   std::string implicitLibVar =
1615     cmStrCat("CMAKE_", this->LinkLanguage, "_IMPLICIT_LINK_LIBRARIES");
1616   this->Makefile->GetDefExpandList(implicitLibVar, implicitLibVec);
1617 
1618   // Store implicit link libraries.
1619   for (std::string const& item : implicitLibVec) {
1620     // Items starting in '-' but not '-l' are flags, not libraries,
1621     // and should not be filtered by this implicit list.
1622     if (item[0] != '-' || item[1] == 'l') {
1623       this->ImplicitLinkLibs.insert(item);
1624     }
1625   }
1626 
1627   // Get platform specific rpath link directories
1628   this->Makefile->GetDefExpandList("CMAKE_PLATFORM_RUNTIME_PATH",
1629                                    this->RuntimeLinkDirs);
1630 }
1631 
1632 std::vector<std::string> const&
GetRuntimeSearchPath() const1633 cmComputeLinkInformation::GetRuntimeSearchPath() const
1634 {
1635   return this->OrderRuntimeSearchPath->GetOrderedDirectories();
1636 }
1637 
AddLibraryRuntimeInfo(std::string const & fullPath,cmGeneratorTarget const * target)1638 void cmComputeLinkInformation::AddLibraryRuntimeInfo(
1639   std::string const& fullPath, cmGeneratorTarget const* target)
1640 {
1641   // Ignore targets on Apple where install_name is not @rpath.
1642   // The dependenty library can be found with other means such as
1643   // @loader_path or full paths.
1644   if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
1645     if (!target->HasMacOSXRpathInstallNameDir(this->Config)) {
1646       return;
1647     }
1648   }
1649 
1650   // Libraries with unknown type must be handled using just the file
1651   // on disk.
1652   if (target->GetType() == cmStateEnums::UNKNOWN_LIBRARY) {
1653     this->AddLibraryRuntimeInfo(fullPath);
1654     return;
1655   }
1656 
1657   // Skip targets that are not shared libraries (modules cannot be linked).
1658   if (target->GetType() != cmStateEnums::SHARED_LIBRARY) {
1659     return;
1660   }
1661 
1662   // Try to get the soname of the library.  Only files with this name
1663   // could possibly conflict.
1664   std::string soName = target->GetSOName(this->Config);
1665   const char* soname = soName.empty() ? nullptr : soName.c_str();
1666 
1667   // Include this library in the runtime path ordering.
1668   this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath, soname);
1669   if (this->LinkWithRuntimePath) {
1670     this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath, soname);
1671   }
1672 }
1673 
AddLibraryRuntimeInfo(std::string const & fullPath)1674 void cmComputeLinkInformation::AddLibraryRuntimeInfo(
1675   std::string const& fullPath)
1676 {
1677   // Get the name of the library from the file name.
1678   bool is_shared_library = false;
1679   std::string file = cmSystemTools::GetFilenameName(fullPath);
1680 
1681   if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
1682     // Check that @rpath is part of the install name.
1683     // If it isn't, return.
1684     std::string soname;
1685     if (!cmSystemTools::GuessLibraryInstallName(fullPath, soname)) {
1686       return;
1687     }
1688 
1689     if (soname.find("@rpath") == std::string::npos) {
1690       return;
1691     }
1692   }
1693 
1694   is_shared_library = this->ExtractSharedLibraryName.find(file);
1695 
1696   if (!is_shared_library) {
1697     // On some platforms (AIX) a shared library may look static.
1698     if (this->ArchivesMayBeShared) {
1699       if (this->ExtractStaticLibraryName.find(file)) {
1700         // This is the name of a shared library or archive.
1701         is_shared_library = true;
1702       }
1703     }
1704   }
1705 
1706   // It could be an Apple framework
1707   if (!is_shared_library) {
1708     if (fullPath.find(".framework") != std::string::npos) {
1709       static cmsys::RegularExpression splitFramework(
1710         "^(.*)/(.*).framework/(.*)$");
1711       if (splitFramework.find(fullPath) &&
1712           (std::string::npos !=
1713            splitFramework.match(3).find(splitFramework.match(2)))) {
1714         is_shared_library = true;
1715       }
1716     }
1717   }
1718 
1719   if (!is_shared_library) {
1720     return;
1721   }
1722 
1723   // Include this library in the runtime path ordering.
1724   this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath);
1725   if (this->LinkWithRuntimePath) {
1726     this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath);
1727   }
1728 }
1729 
cmCLI_ExpandListUnique(std::string const & str,std::vector<std::string> & out,std::set<std::string> & emitted)1730 static void cmCLI_ExpandListUnique(std::string const& str,
1731                                    std::vector<std::string>& out,
1732                                    std::set<std::string>& emitted)
1733 {
1734   std::vector<std::string> tmp = cmExpandedList(str);
1735   for (std::string const& i : tmp) {
1736     if (emitted.insert(i).second) {
1737       out.push_back(i);
1738     }
1739   }
1740 }
1741 
GetRPath(std::vector<std::string> & runtimeDirs,bool for_install) const1742 void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
1743                                         bool for_install) const
1744 {
1745   // Select whether to generate runtime search directories.
1746   bool outputRuntime =
1747     !this->Makefile->IsOn("CMAKE_SKIP_RPATH") && !this->RuntimeFlag.empty();
1748 
1749   // Select whether to generate an rpath for the install tree or the
1750   // build tree.
1751   bool linking_for_install =
1752     (for_install ||
1753      this->Target->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH"));
1754   bool use_install_rpath =
1755     (outputRuntime && this->Target->HaveInstallTreeRPATH(this->Config) &&
1756      linking_for_install);
1757   bool use_build_rpath =
1758     (outputRuntime && this->Target->HaveBuildTreeRPATH(this->Config) &&
1759      !linking_for_install);
1760   bool use_link_rpath = outputRuntime && linking_for_install &&
1761     !this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH") &&
1762     this->Target->GetPropertyAsBool("INSTALL_RPATH_USE_LINK_PATH");
1763 
1764   // Select whether to use $ORIGIN in RPATHs for artifacts in the build tree.
1765   std::string const& originToken = this->Makefile->GetSafeDefinition(
1766     "CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN");
1767   std::string targetOutputDir = this->Target->GetDirectory(this->Config);
1768   bool use_relative_build_rpath =
1769     this->Target->GetPropertyAsBool("BUILD_RPATH_USE_ORIGIN") &&
1770     !originToken.empty() && !targetOutputDir.empty();
1771 
1772   // Construct the RPATH.
1773   std::set<std::string> emitted;
1774   if (use_install_rpath) {
1775     std::string install_rpath;
1776     this->Target->GetInstallRPATH(this->Config, install_rpath);
1777     cmCLI_ExpandListUnique(install_rpath, runtimeDirs, emitted);
1778   }
1779   if (use_build_rpath) {
1780     // Add directories explicitly specified by user
1781     std::string build_rpath;
1782     if (this->Target->GetBuildRPATH(this->Config, build_rpath)) {
1783       // This will not resolve entries to use $ORIGIN, the user is expected to
1784       // do that if necessary.
1785       cmCLI_ExpandListUnique(build_rpath, runtimeDirs, emitted);
1786     }
1787   }
1788   if (use_build_rpath || use_link_rpath) {
1789     std::string rootPath;
1790     if (cmValue sysrootLink =
1791           this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) {
1792       rootPath = *sysrootLink;
1793     } else {
1794       rootPath = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
1795     }
1796     cmValue stagePath = this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX");
1797     std::string const& installPrefix =
1798       this->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
1799     cmSystemTools::ConvertToUnixSlashes(rootPath);
1800     std::vector<std::string> const& rdirs = this->GetRuntimeSearchPath();
1801     std::string const& topBinaryDir =
1802       this->CMakeInstance->GetHomeOutputDirectory();
1803     for (std::string const& ri : rdirs) {
1804       // Put this directory in the rpath if using build-tree rpath
1805       // support or if using the link path as an rpath.
1806       if (use_build_rpath) {
1807         std::string d = ri;
1808         if (!rootPath.empty() && cmHasPrefix(d, rootPath)) {
1809           d.erase(0, rootPath.size());
1810         } else if (cmNonempty(stagePath) && cmHasPrefix(d, *stagePath)) {
1811           d.erase(0, (*stagePath).size());
1812           d = cmStrCat(installPrefix, '/', d);
1813           cmSystemTools::ConvertToUnixSlashes(d);
1814         } else if (use_relative_build_rpath) {
1815           // If expansion of the $ORIGIN token is supported and permitted per
1816           // policy, use relative paths in the RPATH.
1817           if (cmSystemTools::ComparePath(d, topBinaryDir) ||
1818               cmSystemTools::IsSubDirectory(d, topBinaryDir)) {
1819             d = cmSystemTools::RelativePath(targetOutputDir, d);
1820             if (!d.empty()) {
1821               d = cmStrCat(originToken, "/", d);
1822             } else {
1823               d = originToken;
1824             }
1825           }
1826         }
1827         if (emitted.insert(d).second) {
1828           runtimeDirs.push_back(std::move(d));
1829         }
1830       } else if (use_link_rpath) {
1831         // Do not add any path inside the source or build tree.
1832         std::string const& topSourceDir =
1833           this->CMakeInstance->GetHomeDirectory();
1834         if (!cmSystemTools::ComparePath(ri, topSourceDir) &&
1835             !cmSystemTools::ComparePath(ri, topBinaryDir) &&
1836             !cmSystemTools::IsSubDirectory(ri, topSourceDir) &&
1837             !cmSystemTools::IsSubDirectory(ri, topBinaryDir)) {
1838           std::string d = ri;
1839           if (!rootPath.empty() && cmHasPrefix(d, rootPath)) {
1840             d.erase(0, rootPath.size());
1841           } else if (cmNonempty(stagePath) && cmHasPrefix(d, *stagePath)) {
1842             d.erase(0, (*stagePath).size());
1843             d = cmStrCat(installPrefix, '/', d);
1844             cmSystemTools::ConvertToUnixSlashes(d);
1845           }
1846           if (emitted.insert(d).second) {
1847             runtimeDirs.push_back(std::move(d));
1848           }
1849         }
1850       }
1851     }
1852   }
1853 
1854   // Add runtime paths required by the languages to always be
1855   // present.  This is done even when skipping rpath support.
1856   {
1857     cmGeneratorTarget::LinkClosure const* lc =
1858       this->Target->GetLinkClosure(this->Config);
1859     for (std::string const& li : lc->Languages) {
1860       std::string useVar =
1861         "CMAKE_" + li + "_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH";
1862       if (this->Makefile->IsOn(useVar)) {
1863         std::string dirVar = "CMAKE_" + li + "_IMPLICIT_LINK_DIRECTORIES";
1864         if (cmValue dirs = this->Makefile->GetDefinition(dirVar)) {
1865           cmCLI_ExpandListUnique(*dirs, runtimeDirs, emitted);
1866         }
1867       }
1868     }
1869   }
1870 
1871   // Add runtime paths required by the platform to always be
1872   // present.  This is done even when skipping rpath support.
1873   cmCLI_ExpandListUnique(this->RuntimeAlways, runtimeDirs, emitted);
1874 }
1875 
GetRPathString(bool for_install) const1876 std::string cmComputeLinkInformation::GetRPathString(bool for_install) const
1877 {
1878   // Get the directories to use.
1879   std::vector<std::string> runtimeDirs;
1880   this->GetRPath(runtimeDirs, for_install);
1881 
1882   // Concatenate the paths.
1883   std::string rpath = cmJoin(runtimeDirs, this->GetRuntimeSep());
1884 
1885   // If the rpath will be replaced at install time, prepare space.
1886   if (!for_install && this->RuntimeUseChrpath) {
1887     if (!rpath.empty()) {
1888       // Add one trailing separator so the linker does not re-use the
1889       // rpath .dynstr entry for a symbol name that happens to match
1890       // the end of the rpath string.
1891       rpath += this->GetRuntimeSep();
1892     }
1893 
1894     // Make sure it is long enough to hold the replacement value.
1895     std::string::size_type minLength = this->GetChrpathString().length();
1896     while (rpath.length() < minLength) {
1897       rpath += this->GetRuntimeSep();
1898     }
1899   }
1900 
1901   return rpath;
1902 }
1903 
GetChrpathString() const1904 std::string cmComputeLinkInformation::GetChrpathString() const
1905 {
1906   if (!this->RuntimeUseChrpath) {
1907     return "";
1908   }
1909 
1910   return this->GetRPathString(true);
1911 }
1912