Imported Upstream version 3.11.2
[platform/upstream/cmake.git] / Source / cmAddLibraryCommand.cxx
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 "cmAddLibraryCommand.h"
4
5 #include <sstream>
6
7 #include "cmGeneratorExpression.h"
8 #include "cmGlobalGenerator.h"
9 #include "cmMakefile.h"
10 #include "cmState.h"
11 #include "cmStateTypes.h"
12 #include "cmSystemTools.h"
13 #include "cmTarget.h"
14 #include "cmake.h"
15
16 class cmExecutionStatus;
17
18 // cmLibraryCommand
19 bool cmAddLibraryCommand::InitialPass(std::vector<std::string> const& args,
20                                       cmExecutionStatus&)
21 {
22   if (args.empty()) {
23     this->SetError("called with incorrect number of arguments");
24     return false;
25   }
26   // Library type defaults to value of BUILD_SHARED_LIBS, if it exists,
27   // otherwise it defaults to static library.
28   cmStateEnums::TargetType type = cmStateEnums::SHARED_LIBRARY;
29   if (cmSystemTools::IsOff(
30         this->Makefile->GetDefinition("BUILD_SHARED_LIBS"))) {
31     type = cmStateEnums::STATIC_LIBRARY;
32   }
33   bool excludeFromAll = false;
34   bool importTarget = false;
35   bool importGlobal = false;
36
37   std::vector<std::string>::const_iterator s = args.begin();
38
39   std::string const& libName = *s;
40
41   ++s;
42
43   // If the second argument is "SHARED" or "STATIC", then it controls
44   // the type of library.  Otherwise, it is treated as a source or
45   // source list name. There may be two keyword arguments, check for them
46   bool haveSpecifiedType = false;
47   bool isAlias = false;
48   while (s != args.end()) {
49     std::string libType = *s;
50     if (libType == "STATIC") {
51       if (type == cmStateEnums::INTERFACE_LIBRARY) {
52         std::ostringstream e;
53         e << "INTERFACE library specified with conflicting STATIC type.";
54         this->SetError(e.str());
55         return false;
56       }
57       ++s;
58       type = cmStateEnums::STATIC_LIBRARY;
59       haveSpecifiedType = true;
60     } else if (libType == "SHARED") {
61       if (type == cmStateEnums::INTERFACE_LIBRARY) {
62         std::ostringstream e;
63         e << "INTERFACE library specified with conflicting SHARED type.";
64         this->SetError(e.str());
65         return false;
66       }
67       ++s;
68       type = cmStateEnums::SHARED_LIBRARY;
69       haveSpecifiedType = true;
70     } else if (libType == "MODULE") {
71       if (type == cmStateEnums::INTERFACE_LIBRARY) {
72         std::ostringstream e;
73         e << "INTERFACE library specified with conflicting MODULE type.";
74         this->SetError(e.str());
75         return false;
76       }
77       ++s;
78       type = cmStateEnums::MODULE_LIBRARY;
79       haveSpecifiedType = true;
80     } else if (libType == "OBJECT") {
81       if (type == cmStateEnums::INTERFACE_LIBRARY) {
82         std::ostringstream e;
83         e << "INTERFACE library specified with conflicting OBJECT type.";
84         this->SetError(e.str());
85         return false;
86       }
87       ++s;
88       type = cmStateEnums::OBJECT_LIBRARY;
89       haveSpecifiedType = true;
90     } else if (libType == "UNKNOWN") {
91       if (type == cmStateEnums::INTERFACE_LIBRARY) {
92         std::ostringstream e;
93         e << "INTERFACE library specified with conflicting UNKNOWN type.";
94         this->SetError(e.str());
95         return false;
96       }
97       ++s;
98       type = cmStateEnums::UNKNOWN_LIBRARY;
99       haveSpecifiedType = true;
100     } else if (libType == "ALIAS") {
101       if (type == cmStateEnums::INTERFACE_LIBRARY) {
102         std::ostringstream e;
103         e << "INTERFACE library specified with conflicting ALIAS type.";
104         this->SetError(e.str());
105         return false;
106       }
107       ++s;
108       isAlias = true;
109     } else if (libType == "INTERFACE") {
110       if (haveSpecifiedType) {
111         std::ostringstream e;
112         e << "INTERFACE library specified with conflicting/multiple types.";
113         this->SetError(e.str());
114         return false;
115       }
116       if (isAlias) {
117         std::ostringstream e;
118         e << "INTERFACE library specified with conflicting ALIAS type.";
119         this->SetError(e.str());
120         return false;
121       }
122       if (excludeFromAll) {
123         std::ostringstream e;
124         e << "INTERFACE library may not be used with EXCLUDE_FROM_ALL.";
125         this->SetError(e.str());
126         return false;
127       }
128       ++s;
129       type = cmStateEnums::INTERFACE_LIBRARY;
130       haveSpecifiedType = true;
131     } else if (*s == "EXCLUDE_FROM_ALL") {
132       if (type == cmStateEnums::INTERFACE_LIBRARY) {
133         std::ostringstream e;
134         e << "INTERFACE library may not be used with EXCLUDE_FROM_ALL.";
135         this->SetError(e.str());
136         return false;
137       }
138       ++s;
139       excludeFromAll = true;
140     } else if (*s == "IMPORTED") {
141       ++s;
142       importTarget = true;
143     } else if (importTarget && *s == "GLOBAL") {
144       ++s;
145       importGlobal = true;
146     } else if (type == cmStateEnums::INTERFACE_LIBRARY && *s == "GLOBAL") {
147       std::ostringstream e;
148       e << "GLOBAL option may only be used with IMPORTED libraries.";
149       this->SetError(e.str());
150       return false;
151     } else {
152       break;
153     }
154   }
155
156   if (type == cmStateEnums::INTERFACE_LIBRARY) {
157     if (s != args.end()) {
158       std::ostringstream e;
159       e << "INTERFACE library requires no source arguments.";
160       this->SetError(e.str());
161       return false;
162     }
163     if (importGlobal && !importTarget) {
164       std::ostringstream e;
165       e << "INTERFACE library specified as GLOBAL, but not as IMPORTED.";
166       this->SetError(e.str());
167       return false;
168     }
169   }
170
171   bool nameOk = cmGeneratorExpression::IsValidTargetName(libName) &&
172     !cmGlobalGenerator::IsReservedTarget(libName);
173
174   if (nameOk && !importTarget && !isAlias) {
175     nameOk = libName.find(':') == std::string::npos;
176   }
177   if (!nameOk && !this->Makefile->CheckCMP0037(libName, type)) {
178     return false;
179   }
180
181   if (isAlias) {
182     if (!cmGeneratorExpression::IsValidTargetName(libName)) {
183       this->SetError("Invalid name for ALIAS: " + libName);
184       return false;
185     }
186     if (excludeFromAll) {
187       this->SetError("EXCLUDE_FROM_ALL with ALIAS makes no sense.");
188       return false;
189     }
190     if (importTarget || importGlobal) {
191       this->SetError("IMPORTED with ALIAS is not allowed.");
192       return false;
193     }
194     if (args.size() != 3) {
195       std::ostringstream e;
196       e << "ALIAS requires exactly one target argument.";
197       this->SetError(e.str());
198       return false;
199     }
200
201     std::string const& aliasedName = *s;
202     if (this->Makefile->IsAlias(aliasedName)) {
203       std::ostringstream e;
204       e << "cannot create ALIAS target \"" << libName << "\" because target \""
205         << aliasedName << "\" is itself an ALIAS.";
206       this->SetError(e.str());
207       return false;
208     }
209     cmTarget* aliasedTarget =
210       this->Makefile->FindTargetToUse(aliasedName, true);
211     if (!aliasedTarget) {
212       std::ostringstream e;
213       e << "cannot create ALIAS target \"" << libName << "\" because target \""
214         << aliasedName << "\" does not already "
215                           "exist.";
216       this->SetError(e.str());
217       return false;
218     }
219     cmStateEnums::TargetType aliasedType = aliasedTarget->GetType();
220     if (aliasedType != cmStateEnums::SHARED_LIBRARY &&
221         aliasedType != cmStateEnums::STATIC_LIBRARY &&
222         aliasedType != cmStateEnums::MODULE_LIBRARY &&
223         aliasedType != cmStateEnums::OBJECT_LIBRARY &&
224         aliasedType != cmStateEnums::INTERFACE_LIBRARY) {
225       std::ostringstream e;
226       e << "cannot create ALIAS target \"" << libName << "\" because target \""
227         << aliasedName << "\" is not a library.";
228       this->SetError(e.str());
229       return false;
230     }
231     if (aliasedTarget->IsImported() &&
232         !aliasedTarget->IsImportedGloballyVisible()) {
233       std::ostringstream e;
234       e << "cannot create ALIAS target \"" << libName << "\" because target \""
235         << aliasedName << "\" is imported but not globally visible.";
236       this->SetError(e.str());
237       return false;
238     }
239     this->Makefile->AddAlias(libName, aliasedName);
240     return true;
241   }
242
243   if (importTarget && excludeFromAll) {
244     this->SetError("excludeFromAll with IMPORTED target makes no sense.");
245     return false;
246   }
247
248   /* ideally we should check whether for the linker language of the target
249     CMAKE_${LANG}_CREATE_SHARED_LIBRARY is defined and if not default to
250     STATIC. But at this point we know only the name of the target, but not
251     yet its linker language. */
252   if ((type == cmStateEnums::SHARED_LIBRARY ||
253        type == cmStateEnums::MODULE_LIBRARY) &&
254       !this->Makefile->GetState()->GetGlobalPropertyAsBool(
255         "TARGET_SUPPORTS_SHARED_LIBS")) {
256     std::ostringstream w;
257     w << "ADD_LIBRARY called with "
258       << (type == cmStateEnums::SHARED_LIBRARY ? "SHARED" : "MODULE")
259       << " option but the target platform does not support dynamic linking. "
260          "Building a STATIC library instead. This may lead to problems.";
261     this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
262     type = cmStateEnums::STATIC_LIBRARY;
263   }
264
265   // Handle imported target creation.
266   if (importTarget) {
267     // The IMPORTED signature requires a type to be specified explicitly.
268     if (!haveSpecifiedType) {
269       this->SetError("called with IMPORTED argument but no library type.");
270       return false;
271     }
272     if (type == cmStateEnums::OBJECT_LIBRARY) {
273       std::string reason;
274       if (!this->Makefile->GetGlobalGenerator()->HasKnownObjectFileLocation(
275             &reason)) {
276         this->Makefile->IssueMessage(
277           cmake::FATAL_ERROR,
278           "The OBJECT library type may not be used for IMPORTED libraries" +
279             reason + ".");
280         return true;
281       }
282     }
283     if (type == cmStateEnums::INTERFACE_LIBRARY) {
284       if (!cmGeneratorExpression::IsValidTargetName(libName)) {
285         std::ostringstream e;
286         e << "Invalid name for IMPORTED INTERFACE library target: " << libName;
287         this->SetError(e.str());
288         return false;
289       }
290     }
291
292     // Make sure the target does not already exist.
293     if (this->Makefile->FindTargetToUse(libName)) {
294       std::ostringstream e;
295       e << "cannot create imported target \"" << libName
296         << "\" because another target with the same name already exists.";
297       this->SetError(e.str());
298       return false;
299     }
300
301     // Create the imported target.
302     this->Makefile->AddImportedTarget(libName, type, importGlobal);
303     return true;
304   }
305
306   // A non-imported target may not have UNKNOWN type.
307   if (type == cmStateEnums::UNKNOWN_LIBRARY) {
308     this->Makefile->IssueMessage(
309       cmake::FATAL_ERROR,
310       "The UNKNOWN library type may be used only for IMPORTED libraries.");
311     return true;
312   }
313
314   // Enforce name uniqueness.
315   {
316     std::string msg;
317     if (!this->Makefile->EnforceUniqueName(libName, msg)) {
318       this->SetError(msg);
319       return false;
320     }
321   }
322
323   std::vector<std::string> srclists;
324
325   if (type == cmStateEnums::INTERFACE_LIBRARY) {
326     if (!cmGeneratorExpression::IsValidTargetName(libName) ||
327         libName.find("::") != std::string::npos) {
328       std::ostringstream e;
329       e << "Invalid name for INTERFACE library target: " << libName;
330       this->SetError(e.str());
331       return false;
332     }
333
334     this->Makefile->AddLibrary(libName, type, srclists, excludeFromAll);
335     return true;
336   }
337
338   srclists.insert(srclists.end(), s, args.end());
339
340   this->Makefile->AddLibrary(libName, type, srclists, excludeFromAll);
341
342   return true;
343 }