Imported Upstream version 3.11.2
[platform/upstream/cmake.git] / Source / cmGeneratorExpressionEvaluationFile.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 "cmGeneratorExpressionEvaluationFile.h"
4
5 #include "cmsys/FStream.hxx"
6 #include <memory> // IWYU pragma: keep
7 #include <sstream>
8 #include <utility>
9
10 #include "cmGeneratedFileStream.h"
11 #include "cmGlobalGenerator.h"
12 #include "cmListFileCache.h"
13 #include "cmLocalGenerator.h"
14 #include "cmMakefile.h"
15 #include "cmSourceFile.h"
16 #include "cmSourceFileLocationKind.h"
17 #include "cmSystemTools.h"
18 #include "cmake.h"
19
20 cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile(
21   const std::string& input,
22   std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr,
23   std::unique_ptr<cmCompiledGeneratorExpression> condition,
24   bool inputIsContent, cmPolicies::PolicyStatus policyStatusCMP0070)
25   : Input(input)
26   , OutputFileExpr(std::move(outputFileExpr))
27   , Condition(std::move(condition))
28   , InputIsContent(inputIsContent)
29   , PolicyStatusCMP0070(policyStatusCMP0070)
30 {
31 }
32
33 void cmGeneratorExpressionEvaluationFile::Generate(
34   cmLocalGenerator* lg, const std::string& config, const std::string& lang,
35   cmCompiledGeneratorExpression* inputExpression,
36   std::map<std::string, std::string>& outputFiles, mode_t perm)
37 {
38   std::string rawCondition = this->Condition->GetInput();
39   if (!rawCondition.empty()) {
40     std::string condResult = this->Condition->Evaluate(
41       lg, config, false, nullptr, nullptr, nullptr, lang);
42     if (condResult == "0") {
43       return;
44     }
45     if (condResult != "1") {
46       std::ostringstream e;
47       e << "Evaluation file condition \"" << rawCondition
48         << "\" did "
49            "not evaluate to valid content. Got \""
50         << condResult << "\".";
51       lg->IssueMessage(cmake::FATAL_ERROR, e.str());
52       return;
53     }
54   }
55
56   std::string outputFileName = this->OutputFileExpr->Evaluate(
57     lg, config, false, nullptr, nullptr, nullptr, lang);
58   const std::string outputContent = inputExpression->Evaluate(
59     lg, config, false, nullptr, nullptr, nullptr, lang);
60
61   if (cmSystemTools::FileIsFullPath(outputFileName)) {
62     outputFileName = cmSystemTools::CollapseFullPath(outputFileName);
63   } else {
64     outputFileName = this->FixRelativePath(outputFileName, PathForOutput, lg);
65   }
66
67   std::map<std::string, std::string>::iterator it =
68     outputFiles.find(outputFileName);
69
70   if (it != outputFiles.end()) {
71     if (it->second == outputContent) {
72       return;
73     }
74     std::ostringstream e;
75     e << "Evaluation file to be written multiple times with different "
76          "content. "
77          "This is generally caused by the content evaluating the "
78          "configuration type, language, or location of object files:\n "
79       << outputFileName;
80     lg->IssueMessage(cmake::FATAL_ERROR, e.str());
81     return;
82   }
83
84   lg->GetMakefile()->AddCMakeOutputFile(outputFileName);
85   this->Files.push_back(outputFileName);
86   outputFiles[outputFileName] = outputContent;
87
88   cmGeneratedFileStream fout(outputFileName.c_str());
89   fout.SetCopyIfDifferent(true);
90   fout << outputContent;
91   if (fout.Close() && perm) {
92     cmSystemTools::SetPermissions(outputFileName.c_str(), perm);
93   }
94 }
95
96 void cmGeneratorExpressionEvaluationFile::CreateOutputFile(
97   cmLocalGenerator* lg, std::string const& config)
98 {
99   std::vector<std::string> enabledLanguages;
100   cmGlobalGenerator* gg = lg->GetGlobalGenerator();
101   gg->GetEnabledLanguages(enabledLanguages);
102
103   for (std::string const& le : enabledLanguages) {
104     std::string name = this->OutputFileExpr->Evaluate(
105       lg, config, false, nullptr, nullptr, nullptr, le);
106     cmSourceFile* sf = lg->GetMakefile()->GetOrCreateSource(
107       name, false, cmSourceFileLocationKind::Known);
108     // Tell TraceDependencies that the file is not expected to exist
109     // on disk yet.  We generate it after that runs.
110     sf->SetProperty("GENERATED", "1");
111
112     // Tell the build system generators that there is no build rule
113     // to generate the file.
114     sf->SetProperty("__CMAKE_GENERATED_BY_CMAKE", "1");
115
116     gg->SetFilenameTargetDepends(
117       sf, this->OutputFileExpr->GetSourceSensitiveTargets());
118   }
119 }
120
121 void cmGeneratorExpressionEvaluationFile::Generate(cmLocalGenerator* lg)
122 {
123   mode_t perm = 0;
124   std::string inputContent;
125   if (this->InputIsContent) {
126     inputContent = this->Input;
127   } else {
128     std::string inputFileName = this->Input;
129     if (cmSystemTools::FileIsFullPath(inputFileName)) {
130       inputFileName = cmSystemTools::CollapseFullPath(inputFileName);
131     } else {
132       inputFileName = this->FixRelativePath(inputFileName, PathForInput, lg);
133     }
134     lg->GetMakefile()->AddCMakeDependFile(inputFileName);
135     cmSystemTools::GetPermissions(inputFileName.c_str(), perm);
136     cmsys::ifstream fin(inputFileName.c_str());
137     if (!fin) {
138       std::ostringstream e;
139       e << "Evaluation file \"" << inputFileName << "\" cannot be read.";
140       lg->IssueMessage(cmake::FATAL_ERROR, e.str());
141       return;
142     }
143
144     std::string line;
145     std::string sep;
146     while (cmSystemTools::GetLineFromStream(fin, line)) {
147       inputContent += sep + line;
148       sep = "\n";
149     }
150     inputContent += sep;
151   }
152
153   cmListFileBacktrace lfbt = this->OutputFileExpr->GetBacktrace();
154   cmGeneratorExpression contentGE(lfbt);
155   std::unique_ptr<cmCompiledGeneratorExpression> inputExpression =
156     contentGE.Parse(inputContent);
157
158   std::map<std::string, std::string> outputFiles;
159
160   std::vector<std::string> allConfigs;
161   lg->GetMakefile()->GetConfigurations(allConfigs);
162
163   if (allConfigs.empty()) {
164     allConfigs.emplace_back();
165   }
166
167   std::vector<std::string> enabledLanguages;
168   cmGlobalGenerator* gg = lg->GetGlobalGenerator();
169   gg->GetEnabledLanguages(enabledLanguages);
170
171   for (std::string const& le : enabledLanguages) {
172     for (std::string const& li : allConfigs) {
173       this->Generate(lg, li, le, inputExpression.get(), outputFiles, perm);
174       if (cmSystemTools::GetFatalErrorOccured()) {
175         return;
176       }
177     }
178   }
179 }
180
181 std::string cmGeneratorExpressionEvaluationFile::FixRelativePath(
182   std::string const& relativePath, PathRole role, cmLocalGenerator* lg)
183 {
184   std::string resultPath;
185   switch (this->PolicyStatusCMP0070) {
186     case cmPolicies::WARN: {
187       std::string arg;
188       switch (role) {
189         case PathForInput:
190           arg = "INPUT";
191           break;
192         case PathForOutput:
193           arg = "OUTPUT";
194           break;
195       }
196       std::ostringstream w;
197       /* clang-format off */
198       w <<
199         cmPolicies::GetPolicyWarning(cmPolicies::CMP0070) << "\n"
200         "file(GENERATE) given relative " << arg << " path:\n"
201         "  " << relativePath << "\n"
202         "This is not defined behavior unless CMP0070 is set to NEW.  "
203         "For compatibility with older versions of CMake, the previous "
204         "undefined behavior will be used."
205         ;
206       /* clang-format on */
207       lg->IssueMessage(cmake::AUTHOR_WARNING, w.str());
208     }
209       CM_FALLTHROUGH;
210     case cmPolicies::OLD:
211       // OLD behavior is to use the relative path unchanged,
212       // which ends up being used relative to the working dir.
213       resultPath = relativePath;
214       break;
215     case cmPolicies::REQUIRED_IF_USED:
216     case cmPolicies::REQUIRED_ALWAYS:
217     case cmPolicies::NEW:
218       // NEW behavior is to interpret the relative path with respect
219       // to the current source or binary directory.
220       switch (role) {
221         case PathForInput:
222           resultPath = cmSystemTools::CollapseFullPath(
223             relativePath, lg->GetCurrentSourceDirectory());
224           break;
225         case PathForOutput:
226           resultPath = cmSystemTools::CollapseFullPath(
227             relativePath, lg->GetCurrentBinaryDirectory());
228           break;
229       }
230       break;
231   }
232   return resultPath;
233 }