| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmGeneratorExpressionEvaluationFile.h" |
| |
| #include "cmsys/FStream.hxx" |
| #include <memory> // IWYU pragma: keep |
| #include <sstream> |
| #include <utility> |
| |
| #include "cmGeneratedFileStream.h" |
| #include "cmGlobalGenerator.h" |
| #include "cmListFileCache.h" |
| #include "cmLocalGenerator.h" |
| #include "cmMakefile.h" |
| #include "cmSourceFile.h" |
| #include "cmSourceFileLocationKind.h" |
| #include "cmSystemTools.h" |
| #include "cmake.h" |
| |
| cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile( |
| const std::string& input, |
| std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr, |
| std::unique_ptr<cmCompiledGeneratorExpression> condition, |
| bool inputIsContent, cmPolicies::PolicyStatus policyStatusCMP0070) |
| : Input(input) |
| , OutputFileExpr(std::move(outputFileExpr)) |
| , Condition(std::move(condition)) |
| , InputIsContent(inputIsContent) |
| , PolicyStatusCMP0070(policyStatusCMP0070) |
| { |
| } |
| |
| void cmGeneratorExpressionEvaluationFile::Generate( |
| cmLocalGenerator* lg, const std::string& config, const std::string& lang, |
| cmCompiledGeneratorExpression* inputExpression, |
| std::map<std::string, std::string>& outputFiles, mode_t perm) |
| { |
| std::string rawCondition = this->Condition->GetInput(); |
| if (!rawCondition.empty()) { |
| std::string condResult = this->Condition->Evaluate( |
| lg, config, false, nullptr, nullptr, nullptr, lang); |
| if (condResult == "0") { |
| return; |
| } |
| if (condResult != "1") { |
| std::ostringstream e; |
| e << "Evaluation file condition \"" << rawCondition |
| << "\" did " |
| "not evaluate to valid content. Got \"" |
| << condResult << "\"."; |
| lg->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| return; |
| } |
| } |
| |
| std::string outputFileName = this->OutputFileExpr->Evaluate( |
| lg, config, false, nullptr, nullptr, nullptr, lang); |
| const std::string outputContent = inputExpression->Evaluate( |
| lg, config, false, nullptr, nullptr, nullptr, lang); |
| |
| if (cmSystemTools::FileIsFullPath(outputFileName)) { |
| outputFileName = cmSystemTools::CollapseFullPath(outputFileName); |
| } else { |
| outputFileName = this->FixRelativePath(outputFileName, PathForOutput, lg); |
| } |
| |
| std::map<std::string, std::string>::iterator it = |
| outputFiles.find(outputFileName); |
| |
| if (it != outputFiles.end()) { |
| if (it->second == outputContent) { |
| return; |
| } |
| std::ostringstream e; |
| e << "Evaluation file to be written multiple times with different " |
| "content. " |
| "This is generally caused by the content evaluating the " |
| "configuration type, language, or location of object files:\n " |
| << outputFileName; |
| lg->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| return; |
| } |
| |
| lg->GetMakefile()->AddCMakeOutputFile(outputFileName); |
| this->Files.push_back(outputFileName); |
| outputFiles[outputFileName] = outputContent; |
| |
| cmGeneratedFileStream fout(outputFileName.c_str()); |
| fout.SetCopyIfDifferent(true); |
| fout << outputContent; |
| if (fout.Close() && perm) { |
| cmSystemTools::SetPermissions(outputFileName.c_str(), perm); |
| } |
| } |
| |
| void cmGeneratorExpressionEvaluationFile::CreateOutputFile( |
| cmLocalGenerator* lg, std::string const& config) |
| { |
| std::vector<std::string> enabledLanguages; |
| cmGlobalGenerator* gg = lg->GetGlobalGenerator(); |
| gg->GetEnabledLanguages(enabledLanguages); |
| |
| for (std::string const& le : enabledLanguages) { |
| std::string name = this->OutputFileExpr->Evaluate( |
| lg, config, false, nullptr, nullptr, nullptr, le); |
| cmSourceFile* sf = lg->GetMakefile()->GetOrCreateSource( |
| name, false, cmSourceFileLocationKind::Known); |
| sf->SetProperty("GENERATED", "1"); |
| |
| gg->SetFilenameTargetDepends( |
| sf, this->OutputFileExpr->GetSourceSensitiveTargets()); |
| } |
| } |
| |
| void cmGeneratorExpressionEvaluationFile::Generate(cmLocalGenerator* lg) |
| { |
| mode_t perm = 0; |
| std::string inputContent; |
| if (this->InputIsContent) { |
| inputContent = this->Input; |
| } else { |
| std::string inputFileName = this->Input; |
| if (cmSystemTools::FileIsFullPath(inputFileName)) { |
| inputFileName = cmSystemTools::CollapseFullPath(inputFileName); |
| } else { |
| inputFileName = this->FixRelativePath(inputFileName, PathForInput, lg); |
| } |
| lg->GetMakefile()->AddCMakeDependFile(inputFileName); |
| cmSystemTools::GetPermissions(inputFileName.c_str(), perm); |
| cmsys::ifstream fin(inputFileName.c_str()); |
| if (!fin) { |
| std::ostringstream e; |
| e << "Evaluation file \"" << inputFileName << "\" cannot be read."; |
| lg->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| return; |
| } |
| |
| std::string line; |
| std::string sep; |
| while (cmSystemTools::GetLineFromStream(fin, line)) { |
| inputContent += sep + line; |
| sep = "\n"; |
| } |
| inputContent += sep; |
| } |
| |
| cmListFileBacktrace lfbt = this->OutputFileExpr->GetBacktrace(); |
| cmGeneratorExpression contentGE(lfbt); |
| std::unique_ptr<cmCompiledGeneratorExpression> inputExpression = |
| contentGE.Parse(inputContent); |
| |
| std::map<std::string, std::string> outputFiles; |
| |
| std::vector<std::string> allConfigs; |
| lg->GetMakefile()->GetConfigurations(allConfigs); |
| |
| if (allConfigs.empty()) { |
| allConfigs.emplace_back(); |
| } |
| |
| std::vector<std::string> enabledLanguages; |
| cmGlobalGenerator* gg = lg->GetGlobalGenerator(); |
| gg->GetEnabledLanguages(enabledLanguages); |
| |
| for (std::string const& le : enabledLanguages) { |
| for (std::string const& li : allConfigs) { |
| this->Generate(lg, li, le, inputExpression.get(), outputFiles, perm); |
| if (cmSystemTools::GetFatalErrorOccured()) { |
| return; |
| } |
| } |
| } |
| } |
| |
| std::string cmGeneratorExpressionEvaluationFile::FixRelativePath( |
| std::string const& relativePath, PathRole role, cmLocalGenerator* lg) |
| { |
| std::string resultPath; |
| switch (this->PolicyStatusCMP0070) { |
| case cmPolicies::WARN: { |
| std::string arg; |
| switch (role) { |
| case PathForInput: |
| arg = "INPUT"; |
| break; |
| case PathForOutput: |
| arg = "OUTPUT"; |
| break; |
| } |
| std::ostringstream w; |
| /* clang-format off */ |
| w << |
| cmPolicies::GetPolicyWarning(cmPolicies::CMP0070) << "\n" |
| "file(GENERATE) given relative " << arg << " path:\n" |
| " " << relativePath << "\n" |
| "This is not defined behavior unless CMP0070 is set to NEW. " |
| "For compatibility with older versions of CMake, the previous " |
| "undefined behavior will be used." |
| ; |
| /* clang-format on */ |
| lg->IssueMessage(cmake::AUTHOR_WARNING, w.str()); |
| } |
| CM_FALLTHROUGH; |
| case cmPolicies::OLD: |
| // OLD behavior is to use the relative path unchanged, |
| // which ends up being used relative to the working dir. |
| resultPath = relativePath; |
| break; |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| case cmPolicies::NEW: |
| // NEW behavior is to interpret the relative path with respect |
| // to the current source or binary directory. |
| switch (role) { |
| case PathForInput: |
| resultPath = cmSystemTools::CollapseFullPath( |
| relativePath, lg->GetCurrentSourceDirectory()); |
| break; |
| case PathForOutput: |
| resultPath = cmSystemTools::CollapseFullPath( |
| relativePath, lg->GetCurrentBinaryDirectory()); |
| break; |
| } |
| break; |
| } |
| return resultPath; |
| } |