| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file Copyright.txt or https://cmake.org/licensing for details. */ | |
| #include "cmAddLibraryCommand.h" | |
| #include "cmExecutionStatus.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmPolicies.h" | |
| #include "cmState.h" | |
| #include "cmStateTypes.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmValue.h" | |
| bool cmAddLibraryCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| if (args.empty()) { | |
| status.SetError("called with incorrect number of arguments"); | |
| return false; | |
| } | |
| cmMakefile& mf = status.GetMakefile(); | |
| // Library type defaults to value of BUILD_SHARED_LIBS, if it exists, | |
| // otherwise it defaults to static library. | |
| cmStateEnums::TargetType type = cmStateEnums::SHARED_LIBRARY; | |
| if (mf.GetDefinition("BUILD_SHARED_LIBS").IsOff()) { | |
| type = cmStateEnums::STATIC_LIBRARY; | |
| } | |
| bool excludeFromAll = false; | |
| bool importTarget = false; | |
| bool importGlobal = false; | |
| auto s = args.begin(); | |
| std::string const& libName = *s; | |
| ++s; | |
| // If the second argument is "SHARED" or "STATIC", then it controls | |
| // the type of library. Otherwise, it is treated as a source or | |
| // source list name. There may be two keyword arguments, check for them | |
| bool haveSpecifiedType = false; | |
| bool isAlias = false; | |
| while (s != args.end()) { | |
| std::string libType = *s; | |
| if (libType == "STATIC") { | |
| if (type == cmStateEnums::INTERFACE_LIBRARY) { | |
| status.SetError( | |
| "INTERFACE library specified with conflicting STATIC type."); | |
| return false; | |
| } | |
| ++s; | |
| type = cmStateEnums::STATIC_LIBRARY; | |
| haveSpecifiedType = true; | |
| } else if (libType == "SHARED") { | |
| if (type == cmStateEnums::INTERFACE_LIBRARY) { | |
| status.SetError( | |
| "INTERFACE library specified with conflicting SHARED type."); | |
| return false; | |
| } | |
| ++s; | |
| type = cmStateEnums::SHARED_LIBRARY; | |
| haveSpecifiedType = true; | |
| } else if (libType == "MODULE") { | |
| if (type == cmStateEnums::INTERFACE_LIBRARY) { | |
| status.SetError( | |
| "INTERFACE library specified with conflicting MODULE type."); | |
| return false; | |
| } | |
| ++s; | |
| type = cmStateEnums::MODULE_LIBRARY; | |
| haveSpecifiedType = true; | |
| } else if (libType == "OBJECT") { | |
| if (type == cmStateEnums::INTERFACE_LIBRARY) { | |
| status.SetError( | |
| "INTERFACE library specified with conflicting OBJECT type."); | |
| return false; | |
| } | |
| ++s; | |
| type = cmStateEnums::OBJECT_LIBRARY; | |
| haveSpecifiedType = true; | |
| } else if (libType == "UNKNOWN") { | |
| if (type == cmStateEnums::INTERFACE_LIBRARY) { | |
| status.SetError( | |
| "INTERFACE library specified with conflicting UNKNOWN type."); | |
| return false; | |
| } | |
| ++s; | |
| type = cmStateEnums::UNKNOWN_LIBRARY; | |
| haveSpecifiedType = true; | |
| } else if (libType == "ALIAS") { | |
| if (type == cmStateEnums::INTERFACE_LIBRARY) { | |
| status.SetError( | |
| "INTERFACE library specified with conflicting ALIAS type."); | |
| return false; | |
| } | |
| ++s; | |
| isAlias = true; | |
| } else if (libType == "INTERFACE") { | |
| if (haveSpecifiedType) { | |
| status.SetError( | |
| "INTERFACE library specified with conflicting/multiple types."); | |
| return false; | |
| } | |
| if (isAlias) { | |
| status.SetError( | |
| "INTERFACE library specified with conflicting ALIAS type."); | |
| return false; | |
| } | |
| ++s; | |
| type = cmStateEnums::INTERFACE_LIBRARY; | |
| haveSpecifiedType = true; | |
| } else if (*s == "EXCLUDE_FROM_ALL") { | |
| ++s; | |
| excludeFromAll = true; | |
| } else if (*s == "IMPORTED") { | |
| ++s; | |
| importTarget = true; | |
| } else if (importTarget && *s == "GLOBAL") { | |
| ++s; | |
| importGlobal = true; | |
| } else if (type == cmStateEnums::INTERFACE_LIBRARY && *s == "GLOBAL") { | |
| status.SetError( | |
| "GLOBAL option may only be used with IMPORTED libraries."); | |
| return false; | |
| } else { | |
| break; | |
| } | |
| } | |
| if (importTarget && !importGlobal) { | |
| importGlobal = mf.IsImportedTargetGlobalScope(); | |
| } | |
| if (type == cmStateEnums::INTERFACE_LIBRARY) { | |
| if (importGlobal && !importTarget) { | |
| status.SetError( | |
| "INTERFACE library specified as GLOBAL, but not as IMPORTED."); | |
| return false; | |
| } | |
| } | |
| bool nameOk = cmGeneratorExpression::IsValidTargetName(libName) && | |
| !cmGlobalGenerator::IsReservedTarget(libName); | |
| if (nameOk && !importTarget && !isAlias) { | |
| nameOk = libName.find(':') == std::string::npos; | |
| } | |
| if (!nameOk) { | |
| mf.IssueInvalidTargetNameError(libName); | |
| return false; | |
| } | |
| if (isAlias) { | |
| if (!cmGeneratorExpression::IsValidTargetName(libName)) { | |
| status.SetError("Invalid name for ALIAS: " + libName); | |
| return false; | |
| } | |
| if (excludeFromAll) { | |
| status.SetError("EXCLUDE_FROM_ALL with ALIAS makes no sense."); | |
| return false; | |
| } | |
| if (importTarget || importGlobal) { | |
| status.SetError("IMPORTED with ALIAS is not allowed."); | |
| return false; | |
| } | |
| if (args.size() != 3) { | |
| status.SetError("ALIAS requires exactly one target argument."); | |
| return false; | |
| } | |
| if (mf.GetPolicyStatus(cmPolicies::CMP0107) == cmPolicies::NEW) { | |
| // Make sure the target does not already exist. | |
| if (mf.FindTargetToUse(libName)) { | |
| status.SetError(cmStrCat( | |
| "cannot create ALIAS target \"", libName, | |
| "\" because another target with the same name already exists.")); | |
| return false; | |
| } | |
| } | |
| std::string const& aliasedName = *s; | |
| if (mf.IsAlias(aliasedName)) { | |
| status.SetError(cmStrCat("cannot create ALIAS target \"", libName, | |
| "\" because target \"", aliasedName, | |
| "\" is itself an ALIAS.")); | |
| return false; | |
| } | |
| cmTarget* aliasedTarget = | |
| mf.FindTargetToUse(aliasedName, { cmStateEnums::TargetDomain::NATIVE }); | |
| if (!aliasedTarget) { | |
| status.SetError(cmStrCat("cannot create ALIAS target \"", libName, | |
| "\" because target \"", aliasedName, | |
| "\" does not already exist.")); | |
| return false; | |
| } | |
| cmStateEnums::TargetType aliasedType = aliasedTarget->GetType(); | |
| if (aliasedType != cmStateEnums::SHARED_LIBRARY && | |
| aliasedType != cmStateEnums::STATIC_LIBRARY && | |
| aliasedType != cmStateEnums::MODULE_LIBRARY && | |
| aliasedType != cmStateEnums::OBJECT_LIBRARY && | |
| aliasedType != cmStateEnums::INTERFACE_LIBRARY && | |
| !(aliasedType == cmStateEnums::UNKNOWN_LIBRARY && | |
| aliasedTarget->IsImported())) { | |
| status.SetError(cmStrCat("cannot create ALIAS target \"", libName, | |
| "\" because target \"", aliasedName, | |
| "\" is not a library.")); | |
| return false; | |
| } | |
| mf.AddAlias(libName, aliasedName, | |
| !aliasedTarget->IsImported() || | |
| aliasedTarget->IsImportedGloballyVisible()); | |
| return true; | |
| } | |
| if (importTarget && excludeFromAll) { | |
| status.SetError("excludeFromAll with IMPORTED target makes no sense."); | |
| return false; | |
| } | |
| /* ideally we should check whether for the linker language of the target | |
| CMAKE_${LANG}_CREATE_SHARED_LIBRARY is defined and if not default to | |
| STATIC. But at this point we know only the name of the target, but not | |
| yet its linker language. */ | |
| if ((type == cmStateEnums::SHARED_LIBRARY || | |
| type == cmStateEnums::MODULE_LIBRARY) && | |
| !mf.GetState()->GetGlobalPropertyAsBool("TARGET_SUPPORTS_SHARED_LIBS")) { | |
| switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0164)) { | |
| case cmPolicies::WARN: | |
| mf.IssueMessage( | |
| MessageType::AUTHOR_WARNING, | |
| cmStrCat( | |
| "ADD_LIBRARY called with ", | |
| (type == cmStateEnums::SHARED_LIBRARY ? "SHARED" : "MODULE"), | |
| " option but the target platform does not support dynamic " | |
| "linking. ", | |
| "Building a STATIC library instead. This may lead to problems.")); | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| type = cmStateEnums::STATIC_LIBRARY; | |
| break; | |
| case cmPolicies::NEW: | |
| mf.IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat( | |
| "ADD_LIBRARY called with ", | |
| (type == cmStateEnums::SHARED_LIBRARY ? "SHARED" : "MODULE"), | |
| " option but the target platform does not support dynamic " | |
| "linking.")); | |
| cmSystemTools::SetFatalErrorOccurred(); | |
| return false; | |
| default: | |
| break; | |
| } | |
| } | |
| // Handle imported target creation. | |
| if (importTarget) { | |
| // The IMPORTED signature requires a type to be specified explicitly. | |
| if (!haveSpecifiedType) { | |
| status.SetError("called with IMPORTED argument but no library type."); | |
| return false; | |
| } | |
| if (type == cmStateEnums::INTERFACE_LIBRARY) { | |
| if (!cmGeneratorExpression::IsValidTargetName(libName)) { | |
| status.SetError(cmStrCat( | |
| "Invalid name for IMPORTED INTERFACE library target: ", libName)); | |
| return false; | |
| } | |
| } | |
| // Make sure the target does not already exist. | |
| if (mf.FindTargetToUse(libName)) { | |
| status.SetError(cmStrCat( | |
| "cannot create imported target \"", libName, | |
| "\" because another target with the same name already exists.")); | |
| return false; | |
| } | |
| // Create the imported target. | |
| mf.AddImportedTarget(libName, type, importGlobal); | |
| return true; | |
| } | |
| // A non-imported target may not have UNKNOWN type. | |
| if (type == cmStateEnums::UNKNOWN_LIBRARY) { | |
| mf.IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "The UNKNOWN library type may be used only for IMPORTED libraries."); | |
| return true; | |
| } | |
| // Enforce name uniqueness. | |
| { | |
| std::string msg; | |
| if (!mf.EnforceUniqueName(libName, msg)) { | |
| status.SetError(msg); | |
| return false; | |
| } | |
| } | |
| if (type == cmStateEnums::INTERFACE_LIBRARY) { | |
| if (!cmGeneratorExpression::IsValidTargetName(libName) || | |
| libName.find("::") != std::string::npos) { | |
| status.SetError( | |
| cmStrCat("Invalid name for INTERFACE library target: ", libName)); | |
| return false; | |
| } | |
| } | |
| std::vector<std::string> srcs(s, args.end()); | |
| mf.AddLibrary(libName, type, srcs, excludeFromAll); | |
| return true; | |
| } |