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 "cmIncludeCommand.h"
4 
5 #include <map>
6 #include <sstream>
7 #include <utility>
8 
9 #include "cmExecutionStatus.h"
10 #include "cmGlobalGenerator.h"
11 #include "cmMakefile.h"
12 #include "cmMessageType.h"
13 #include "cmPolicies.h"
14 #include "cmStringAlgorithms.h"
15 #include "cmSystemTools.h"
16 
17 // cmIncludeCommand
cmIncludeCommand(std::vector<std::string> const & args,cmExecutionStatus & status)18 bool cmIncludeCommand(std::vector<std::string> const& args,
19                       cmExecutionStatus& status)
20 {
21   static std::map<std::string, cmPolicies::PolicyID> DeprecatedModules;
22   if (DeprecatedModules.empty()) {
23     DeprecatedModules["Documentation"] = cmPolicies::CMP0106;
24     DeprecatedModules["WriteCompilerDetectionHeader"] = cmPolicies::CMP0120;
25   }
26 
27   if (args.empty() || args.size() > 4) {
28     status.SetError("called with wrong number of arguments.  "
29                     "include() only takes one file.");
30     return false;
31   }
32   bool optional = false;
33   bool noPolicyScope = false;
34   std::string fname = args[0];
35   std::string resultVarName;
36 
37   for (unsigned int i = 1; i < args.size(); i++) {
38     if (args[i] == "OPTIONAL") {
39       if (optional) {
40         status.SetError("called with invalid arguments: OPTIONAL used twice");
41         return false;
42       }
43       optional = true;
44     } else if (args[i] == "RESULT_VARIABLE") {
45       if (!resultVarName.empty()) {
46         status.SetError("called with invalid arguments: "
47                         "only one result variable allowed");
48         return false;
49       }
50       if (++i < args.size()) {
51         resultVarName = args[i];
52       } else {
53         status.SetError("called with no value for RESULT_VARIABLE.");
54         return false;
55       }
56     } else if (args[i] == "NO_POLICY_SCOPE") {
57       noPolicyScope = true;
58     } else if (i > 1) // compat.: in previous cmake versions the second
59                       // parameter was ignored if it wasn't "OPTIONAL"
60     {
61       std::string errorText =
62         cmStrCat("called with invalid argument: ", args[i]);
63       status.SetError(errorText);
64       return false;
65     }
66   }
67 
68   if (fname.empty()) {
69     status.GetMakefile().IssueMessage(
70       MessageType::AUTHOR_WARNING,
71       "include() given empty file name (ignored).");
72     return true;
73   }
74 
75   if (!cmSystemTools::FileIsFullPath(fname)) {
76     bool system = false;
77     // Not a path. Maybe module.
78     std::string module = cmStrCat(fname, ".cmake");
79     std::string mfile = status.GetMakefile().GetModulesFile(module, system);
80 
81     if (system) {
82       auto ModulePolicy = DeprecatedModules.find(fname);
83       if (ModulePolicy != DeprecatedModules.end()) {
84         cmPolicies::PolicyStatus PolicyStatus =
85           status.GetMakefile().GetPolicyStatus(ModulePolicy->second);
86         switch (PolicyStatus) {
87           case cmPolicies::WARN: {
88             status.GetMakefile().IssueMessage(
89               MessageType::AUTHOR_WARNING,
90               cmStrCat(cmPolicies::GetPolicyWarning(ModulePolicy->second),
91                        "\n"));
92             CM_FALLTHROUGH;
93           }
94           case cmPolicies::OLD:
95             break;
96           case cmPolicies::REQUIRED_IF_USED:
97           case cmPolicies::REQUIRED_ALWAYS:
98           case cmPolicies::NEW:
99             mfile = "";
100             break;
101         }
102       }
103     }
104 
105     if (!mfile.empty()) {
106       fname = mfile;
107     }
108   }
109 
110   std::string fname_abs = cmSystemTools::CollapseFullPath(
111     fname, status.GetMakefile().GetCurrentSourceDirectory());
112 
113   cmGlobalGenerator* gg = status.GetMakefile().GetGlobalGenerator();
114   if (gg->IsExportedTargetsFile(fname_abs)) {
115     const char* modal = nullptr;
116     std::ostringstream e;
117     MessageType messageType = MessageType::AUTHOR_WARNING;
118 
119     switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0024)) {
120       case cmPolicies::WARN:
121         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0024) << "\n";
122         modal = "should";
123         CM_FALLTHROUGH;
124       case cmPolicies::OLD:
125         break;
126       case cmPolicies::REQUIRED_IF_USED:
127       case cmPolicies::REQUIRED_ALWAYS:
128       case cmPolicies::NEW:
129         modal = "may";
130         messageType = MessageType::FATAL_ERROR;
131     }
132     if (modal) {
133       e << "The file\n  " << fname_abs
134         << "\nwas generated by the export() "
135            "command.  It "
136         << modal
137         << " not be used as the argument to the "
138            "include() command.  Use ALIAS targets instead to refer to targets "
139            "by alternative names.\n";
140       status.GetMakefile().IssueMessage(messageType, e.str());
141       if (messageType == MessageType::FATAL_ERROR) {
142         return false;
143       }
144     }
145     gg->CreateGenerationObjects();
146     gg->GenerateImportFile(fname_abs);
147   }
148 
149   std::string listFile = cmSystemTools::CollapseFullPath(
150     fname, status.GetMakefile().GetCurrentSourceDirectory());
151 
152   const bool fileDoesnotExist = !cmSystemTools::FileExists(listFile);
153   const bool fileIsDirectory = cmSystemTools::FileIsDirectory(listFile);
154   if (fileDoesnotExist || fileIsDirectory) {
155     if (!resultVarName.empty()) {
156       status.GetMakefile().AddDefinition(resultVarName, "NOTFOUND");
157     }
158     if (optional) {
159       return true;
160     }
161     if (fileDoesnotExist) {
162       status.SetError(cmStrCat("could not find requested file:\n  ", fname));
163       return false;
164     }
165     if (fileIsDirectory) {
166       status.SetError(cmStrCat("requested file is a directory:\n  ", fname));
167       return false;
168     }
169   }
170 
171   bool readit =
172     status.GetMakefile().ReadDependentFile(listFile, noPolicyScope);
173 
174   // add the location of the included file if a result variable was given
175   if (!resultVarName.empty()) {
176     status.GetMakefile().AddDefinition(
177       resultVarName, readit ? fname_abs.c_str() : "NOTFOUND");
178   }
179 
180   if (!optional && !readit && !cmSystemTools::GetFatalErrorOccured()) {
181     std::string m = cmStrCat("could not load requested file:\n  ", fname);
182     status.SetError(m);
183     return false;
184   }
185   return true;
186 }
187