/src/CMake/Source/cmInstallGenerator.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmInstallGenerator.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <sstream> |
7 | | #include <utility> |
8 | | |
9 | | #include <cm/string_view> |
10 | | |
11 | | #include "cmDiagnostics.h" |
12 | | #include "cmGeneratorTarget.h" |
13 | | #include "cmGlobalGenerator.h" |
14 | | #include "cmLocalGenerator.h" |
15 | | #include "cmMakefile.h" |
16 | | #include "cmMessageType.h" |
17 | | #include "cmStringAlgorithms.h" |
18 | | #include "cmSystemTools.h" |
19 | | |
20 | | cmInstallGenerator::cmInstallGenerator( |
21 | | std::string destination, std::vector<std::string> const& configurations, |
22 | | std::string component, MessageLevel message, bool excludeFromAll, |
23 | | bool allComponents, cmDiagnosticContext context) |
24 | 0 | : cmScriptGenerator("CMAKE_INSTALL_CONFIG_NAME", configurations) |
25 | 0 | , Destination(std::move(destination)) |
26 | 0 | , Component(std::move(component)) |
27 | 0 | , Message(message) |
28 | 0 | , ExcludeFromAll(excludeFromAll) |
29 | 0 | , AllComponents(allComponents) |
30 | 0 | , Context{ std::move(context) } |
31 | 0 | { |
32 | 0 | } |
33 | | |
34 | 0 | cmInstallGenerator::~cmInstallGenerator() = default; |
35 | | |
36 | | bool cmInstallGenerator::HaveInstall() |
37 | 0 | { |
38 | 0 | return true; |
39 | 0 | } |
40 | | |
41 | | void cmInstallGenerator::CheckCMP0082(bool& haveSubdirectoryInstall, |
42 | | bool& haveInstallAfterSubdirectory) |
43 | 0 | { |
44 | 0 | if (haveSubdirectoryInstall) { |
45 | 0 | haveInstallAfterSubdirectory = true; |
46 | 0 | } |
47 | 0 | } |
48 | | |
49 | | void cmInstallGenerator::AddInstallRule( |
50 | | std::ostream& os, std::string const& dest, cmInstallType type, |
51 | | std::vector<std::string> const& files, bool optional /* = false */, |
52 | | char const* permissionsFile /* = nullptr */, |
53 | | char const* permissionsDir /* = nullptr */, |
54 | | char const* rename /* = nullptr */, char const* literalArgs /* = nullptr */, |
55 | | Indent indent, char const* filesVar /* = nullptr */) |
56 | 0 | { |
57 | | // Use the FILE command to install the file. |
58 | 0 | std::string stype; |
59 | 0 | switch (type) { |
60 | 0 | case cmInstallType_DIRECTORY: |
61 | 0 | stype = "DIRECTORY"; |
62 | 0 | break; |
63 | 0 | case cmInstallType_PROGRAMS: |
64 | 0 | stype = "PROGRAM"; |
65 | 0 | break; |
66 | 0 | case cmInstallType_EXECUTABLE: |
67 | 0 | stype = "EXECUTABLE"; |
68 | 0 | break; |
69 | 0 | case cmInstallType_STATIC_LIBRARY: |
70 | 0 | stype = "STATIC_LIBRARY"; |
71 | 0 | break; |
72 | 0 | case cmInstallType_SHARED_LIBRARY: |
73 | 0 | stype = "SHARED_LIBRARY"; |
74 | 0 | break; |
75 | 0 | case cmInstallType_MODULE_LIBRARY: |
76 | 0 | stype = "MODULE"; |
77 | 0 | break; |
78 | 0 | case cmInstallType_FILES: |
79 | 0 | stype = "FILE"; |
80 | 0 | break; |
81 | 0 | } |
82 | 0 | if (cmSystemTools::FileIsFullPath(dest)) { |
83 | 0 | if (!files.empty()) { |
84 | 0 | os << indent << "list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n"; |
85 | 0 | os << indent << " \""; |
86 | 0 | bool firstIteration = true; |
87 | 0 | for (std::string const& file : files) { |
88 | 0 | if (!firstIteration) { |
89 | 0 | os << ";"; |
90 | 0 | } |
91 | 0 | os << dest << "/"; |
92 | 0 | if (rename && *rename) { |
93 | 0 | os << rename; |
94 | 0 | } else { |
95 | 0 | os << cmSystemTools::GetFilenameNameView(file); |
96 | 0 | } |
97 | 0 | firstIteration = false; |
98 | 0 | } |
99 | 0 | os << "\")\n"; |
100 | 0 | } |
101 | 0 | if (filesVar) { |
102 | 0 | os << indent << "foreach(_cmake_abs_file IN LISTS " << filesVar << ")\n"; |
103 | 0 | os << indent.Next() |
104 | 0 | << "get_filename_component(_cmake_abs_file_name " |
105 | 0 | "\"${_cmake_abs_file}\" NAME)\n"; |
106 | 0 | os << indent.Next() << "list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES \"" |
107 | 0 | << dest << "/${_cmake_abs_file_name}\")\n"; |
108 | 0 | os << indent << "endforeach()\n"; |
109 | 0 | os << indent << "unset(_cmake_abs_file_name)\n"; |
110 | 0 | os << indent << "unset(_cmake_abs_file)\n"; |
111 | 0 | } |
112 | 0 | os << indent << "if(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n"; |
113 | 0 | os << indent.Next() |
114 | 0 | << "message(WARNING \"ABSOLUTE path INSTALL " |
115 | 0 | "DESTINATION : ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"; |
116 | 0 | os << indent << "endif()\n"; |
117 | |
|
118 | 0 | os << indent << "if(CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n"; |
119 | 0 | os << indent.Next() |
120 | 0 | << "message(FATAL_ERROR \"ABSOLUTE path INSTALL " |
121 | 0 | "DESTINATION forbidden (by caller): " |
122 | 0 | "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"; |
123 | 0 | os << indent << "endif()\n"; |
124 | 0 | } |
125 | 0 | std::string absDest = ConvertToAbsoluteDestination(dest); |
126 | 0 | os << indent << "file(INSTALL DESTINATION \"" << absDest << "\" TYPE " |
127 | 0 | << stype; |
128 | 0 | if (optional) { |
129 | 0 | os << " OPTIONAL"; |
130 | 0 | } |
131 | 0 | switch (this->Message) { |
132 | 0 | case MessageDefault: |
133 | 0 | break; |
134 | 0 | case MessageAlways: |
135 | 0 | os << " MESSAGE_ALWAYS"; |
136 | 0 | break; |
137 | 0 | case MessageLazy: |
138 | 0 | os << " MESSAGE_LAZY"; |
139 | 0 | break; |
140 | 0 | case MessageNever: |
141 | 0 | os << " MESSAGE_NEVER"; |
142 | 0 | break; |
143 | 0 | } |
144 | 0 | if (permissionsFile && *permissionsFile) { |
145 | 0 | os << " PERMISSIONS" << permissionsFile; |
146 | 0 | } |
147 | 0 | if (permissionsDir && *permissionsDir) { |
148 | 0 | os << " DIR_PERMISSIONS" << permissionsDir; |
149 | 0 | } |
150 | 0 | if (rename && *rename) { |
151 | 0 | os << " RENAME \"" << rename << "\""; |
152 | 0 | } |
153 | 0 | os << " FILES"; |
154 | 0 | if (files.size() == 1) { |
155 | 0 | os << " \"" << files[0] << "\""; |
156 | 0 | } else { |
157 | 0 | for (std::string const& f : files) { |
158 | 0 | os << "\n" << indent << " \"" << f << "\""; |
159 | 0 | } |
160 | 0 | if (filesVar) { |
161 | 0 | os << " ${" << filesVar << "}"; |
162 | 0 | } |
163 | 0 | os << "\n" << indent << " "; |
164 | 0 | if (!(literalArgs && *literalArgs)) { |
165 | 0 | os << " "; |
166 | 0 | } |
167 | 0 | } |
168 | 0 | if (literalArgs && *literalArgs) { |
169 | 0 | os << literalArgs; |
170 | 0 | } |
171 | 0 | os << ")\n"; |
172 | 0 | } |
173 | | |
174 | | std::string cmInstallGenerator::CreateComponentTest( |
175 | | std::string const& component, bool excludeFromAll, bool allComponents) |
176 | 0 | { |
177 | 0 | if (allComponents) { |
178 | 0 | if (excludeFromAll) { |
179 | 0 | return "CMAKE_INSTALL_COMPONENT"; |
180 | 0 | } |
181 | 0 | return {}; |
182 | 0 | } |
183 | | |
184 | 0 | std::string result = "CMAKE_INSTALL_COMPONENT STREQUAL \""; |
185 | 0 | result += component; |
186 | 0 | result += "\""; |
187 | 0 | if (!excludeFromAll) { |
188 | 0 | result += " OR NOT CMAKE_INSTALL_COMPONENT"; |
189 | 0 | } |
190 | |
|
191 | 0 | return result; |
192 | 0 | } |
193 | | |
194 | | void cmInstallGenerator::GenerateScript(std::ostream& os) |
195 | 0 | { |
196 | | // Track indentation. |
197 | 0 | Indent indent; |
198 | |
|
199 | 0 | std::string componentTest = this->CreateComponentTest( |
200 | 0 | this->Component, this->ExcludeFromAll, this->AllComponents); |
201 | | |
202 | | // Begin this block of installation. |
203 | 0 | if (!componentTest.empty()) { |
204 | 0 | os << indent << "if(" << componentTest << ")\n"; |
205 | 0 | } |
206 | | |
207 | | // Generate the script possibly with per-configuration code. |
208 | 0 | this->GenerateScriptConfigs(os, |
209 | 0 | this->AllComponents ? indent : indent.Next()); |
210 | | |
211 | | // End this block of installation. |
212 | 0 | if (!componentTest.empty()) { |
213 | 0 | os << indent << "endif()\n\n"; |
214 | 0 | } |
215 | 0 | } |
216 | | |
217 | | bool cmInstallGenerator::InstallsForConfig(std::string const& config) |
218 | 0 | { |
219 | 0 | return this->GeneratesForConfig(config); |
220 | 0 | } |
221 | | |
222 | | std::string cmInstallGenerator::ConvertToAbsoluteDestination( |
223 | | std::string const& dest) |
224 | 0 | { |
225 | 0 | if (dest == ".") { |
226 | 0 | return "${CMAKE_INSTALL_PREFIX}"; |
227 | 0 | } |
228 | | |
229 | 0 | std::string result; |
230 | 0 | if (!dest.empty() && !cmSystemTools::FileIsFullPath(dest)) { |
231 | 0 | result = "${CMAKE_INSTALL_PREFIX}/"; |
232 | 0 | } |
233 | 0 | result += dest; |
234 | 0 | return result; |
235 | 0 | } |
236 | | |
237 | | void cmInstallGenerator::CheckAbsoluteDestination( |
238 | | std::string const& dest, cmLocalGenerator const* lg) const |
239 | 0 | { |
240 | 0 | if (!cmSystemTools::FileIsFullPath(dest)) { |
241 | 0 | return; |
242 | 0 | } |
243 | 0 | lg->IssueDiagnostic( |
244 | 0 | cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION, |
245 | 0 | cmStrCat("INSTALL command given absolute DESTINATION path:\n ", dest), |
246 | 0 | this->Context); |
247 | 0 | } |
248 | | |
249 | | cmInstallGenerator::MessageLevel cmInstallGenerator::SelectMessageLevel( |
250 | | cmMakefile* mf, bool never) |
251 | 0 | { |
252 | 0 | if (never) { |
253 | 0 | return MessageNever; |
254 | 0 | } |
255 | 0 | std::string m = mf->GetSafeDefinition("CMAKE_INSTALL_MESSAGE"); |
256 | 0 | if (m == "ALWAYS") { |
257 | 0 | return MessageAlways; |
258 | 0 | } |
259 | 0 | if (m == "LAZY") { |
260 | 0 | return MessageLazy; |
261 | 0 | } |
262 | 0 | if (m == "NEVER") { |
263 | 0 | return MessageNever; |
264 | 0 | } |
265 | 0 | return MessageDefault; |
266 | 0 | } |
267 | | |
268 | | cmDiagnosticContext cmInstallGenerator::CaptureContext(cmMakefile const& mf) |
269 | 0 | { |
270 | 0 | cmDiagnosticContext context{ mf.GetBacktrace() }; |
271 | 0 | context.RecordDiagnostic(cmDiagnostics::CMD_AUTHOR, mf.GetStateSnapshot()); |
272 | 0 | context.RecordDiagnostic(cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION, |
273 | 0 | mf.GetStateSnapshot()); |
274 | 0 | return context; |
275 | 0 | } |
276 | | |
277 | | bool cmInstallGenerator::ResolveBuildDepends( |
278 | | cmLocalGenerator* lg, std::string const& signature, |
279 | | std::vector<std::string> const& names, BuildDependsEntries& entries) |
280 | 0 | { |
281 | 0 | entries = BuildDependsEntries(); |
282 | 0 | cmGlobalGenerator* gg = lg->GetGlobalGenerator(); |
283 | 0 | std::vector<std::string> const configs = |
284 | 0 | lg->GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); |
285 | 0 | auto const isArtifact = [gg, &configs](std::string const& file) { |
286 | 0 | return std::any_of(configs.begin(), configs.end(), |
287 | 0 | [gg, &file](std::string const& config) { |
288 | 0 | return gg->FindArtifactOwningTarget(file, config); |
289 | 0 | }); |
290 | 0 | }; |
291 | 0 | for (std::string const& name : names) { |
292 | 0 | if (cmGeneratorTarget const* target = lg->FindGeneratorTargetToUse(name)) { |
293 | 0 | entries.Targets.push_back(target); |
294 | 0 | continue; |
295 | 0 | } |
296 | | // A relative path is interpreted with respect to the current source |
297 | | // directory if the file exists there, and otherwise the current binary |
298 | | // directory. |
299 | 0 | std::string file = lg->GetRealFileDependency(name); |
300 | 0 | if (gg->IsGeneratedByBuild(file) || isArtifact(file) || |
301 | 0 | cmSystemTools::FileExists(file)) { |
302 | 0 | entries.Files.push_back(std::move(file)); |
303 | 0 | } else { |
304 | 0 | lg->IssueMessage( |
305 | 0 | MessageType::FATAL_ERROR, |
306 | 0 | cmStrCat("install(", signature, ") BUILD_DEPENDS entry \"", name, |
307 | 0 | "\" is neither a target nor a file that exists or is " |
308 | 0 | "generated by the build."), |
309 | 0 | this->GetBacktrace()); |
310 | 0 | return false; |
311 | 0 | } |
312 | 0 | } |
313 | 0 | return true; |
314 | 0 | } |
315 | | |
316 | | void cmInstallGenerator::AddBuildDependsDependencies( |
317 | | cmLocalGenerator* lg, std::string const& config, |
318 | | BuildDependsEntries const& entries, |
319 | | std::vector<BuildDependency>& dependencies) |
320 | 0 | { |
321 | 0 | for (cmGeneratorTarget const* target : entries.Targets) { |
322 | 0 | dependencies.push_back({ target, BuildDependencyType::Normal }); |
323 | 0 | } |
324 | 0 | AddGeneratedFileDependencies(lg->GetGlobalGenerator(), config, nullptr, |
325 | 0 | entries.Files, dependencies); |
326 | 0 | } |
327 | | |
328 | | std::string cmInstallGenerator::GetDestDirPath(std::string const& file) |
329 | 0 | { |
330 | | // Construct the path of the file on disk after installation on |
331 | | // which tweaks may be performed. |
332 | 0 | std::string toDestDirPath = "$ENV{DESTDIR}"; |
333 | 0 | if (file[0] != '/' && file[0] != '$') { |
334 | 0 | toDestDirPath += "/"; |
335 | 0 | } |
336 | 0 | toDestDirPath += file; |
337 | 0 | return toDestDirPath; |
338 | 0 | } |
339 | | |
340 | | void cmInstallGenerator::AddGeneratedFileDependencies( |
341 | | cmGlobalGenerator* gg, std::string const& config, |
342 | | cmGeneratorTarget const* target, std::vector<std::string> const& files, |
343 | | std::vector<BuildDependency>& dependencies) |
344 | 0 | { |
345 | 0 | for (std::string const& file : files) { |
346 | 0 | if (cmGeneratorTarget const* owner = gg->FindOutputOwningTarget(file)) { |
347 | 0 | dependencies.push_back( |
348 | 0 | { owner, BuildDependencyType::GeneratedFile, file }); |
349 | 0 | } else if (cmGeneratorTarget const* linker = |
350 | 0 | gg->FindArtifactOwningTarget(file, config)) { |
351 | 0 | dependencies.push_back({ linker, BuildDependencyType::Normal }); |
352 | 0 | } else if (gg->IsGeneratedByBuild(file)) { |
353 | 0 | dependencies.push_back( |
354 | 0 | { target, BuildDependencyType::GeneratedFile, file }); |
355 | 0 | } |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | | void cmInstallGenerator::AddTweak(std::ostream& os, Indent indent, |
360 | | std::string const& config, |
361 | | std::string const& file, |
362 | | TweakMethod const& tweak) |
363 | 0 | { |
364 | 0 | std::ostringstream tw; |
365 | 0 | tweak(tw, indent.Next(), config, file); |
366 | 0 | std::string tws = tw.str(); |
367 | 0 | if (!tws.empty()) { |
368 | 0 | os << indent << "if(EXISTS \"" << file << "\" AND\n" |
369 | 0 | << indent << " NOT IS_SYMLINK \"" << file << "\")\n"; |
370 | 0 | os << tws; |
371 | 0 | os << indent << "endif()\n"; |
372 | 0 | } |
373 | 0 | } |
374 | | |
375 | | void cmInstallGenerator::AddTweak(std::ostream& os, Indent indent, |
376 | | std::string const& config, |
377 | | std::string const& dir, |
378 | | std::vector<std::string> const& files, |
379 | | TweakMethod const& tweak) |
380 | 0 | { |
381 | 0 | if (files.size() == 1) { |
382 | | // Tweak a single file. |
383 | 0 | AddTweak(os, indent, config, GetDestDirPath(cmStrCat(dir, files[0])), |
384 | 0 | tweak); |
385 | 0 | } else { |
386 | | // Generate a foreach loop to tweak multiple files. |
387 | 0 | std::ostringstream tw; |
388 | 0 | AddTweak(tw, indent.Next(), config, "${file}", tweak); |
389 | 0 | std::string tws = tw.str(); |
390 | 0 | if (!tws.empty()) { |
391 | 0 | Indent indent2 = indent.Next().Next(); |
392 | 0 | os << indent << "foreach(file\n"; |
393 | 0 | for (std::string const& f : files) { |
394 | 0 | os << indent2 << "\"" << GetDestDirPath(cmStrCat(dir, f)) << "\"\n"; |
395 | 0 | } |
396 | 0 | os << indent2 << ")\n"; |
397 | 0 | os << tws; |
398 | 0 | os << indent << "endforeach()\n"; |
399 | 0 | } |
400 | 0 | } |
401 | 0 | } |