cmFileInstaller.cxx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  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 "cmFileInstaller.h"
  4. #include "cmExecutionStatus.h"
  5. #include "cmFSPermissions.h"
  6. #include "cmMakefile.h"
  7. #include "cmSystemTools.h"
  8. #include "cm_sys_stat.h"
  9. #include <sstream>
  10. using namespace cmFSPermissions;
  11. cmFileInstaller::cmFileInstaller(cmExecutionStatus& status)
  12. : cmFileCopier(status, "INSTALL")
  13. , InstallType(cmInstallType_FILES)
  14. , Optional(false)
  15. , MessageAlways(false)
  16. , MessageLazy(false)
  17. , MessageNever(false)
  18. , DestDirLength(0)
  19. {
  20. // Installation does not use source permissions by default.
  21. this->UseSourcePermissions = false;
  22. // Check whether to copy files always or only if they have changed.
  23. std::string install_always;
  24. if (cmSystemTools::GetEnv("CMAKE_INSTALL_ALWAYS", install_always)) {
  25. this->Always = cmSystemTools::IsOn(install_always);
  26. }
  27. // Get the current manifest.
  28. this->Manifest =
  29. this->Makefile->GetSafeDefinition("CMAKE_INSTALL_MANIFEST_FILES");
  30. }
  31. cmFileInstaller::~cmFileInstaller()
  32. {
  33. // Save the updated install manifest.
  34. this->Makefile->AddDefinition("CMAKE_INSTALL_MANIFEST_FILES",
  35. this->Manifest);
  36. }
  37. void cmFileInstaller::ManifestAppend(std::string const& file)
  38. {
  39. if (!this->Manifest.empty()) {
  40. this->Manifest += ";";
  41. }
  42. this->Manifest += file.substr(this->DestDirLength);
  43. }
  44. std::string const& cmFileInstaller::ToName(std::string const& fromName)
  45. {
  46. return this->Rename.empty() ? fromName : this->Rename;
  47. }
  48. void cmFileInstaller::ReportCopy(const std::string& toFile, Type type,
  49. bool copy)
  50. {
  51. if (!this->MessageNever && (copy || !this->MessageLazy)) {
  52. std::string message = (copy ? "Installing: " : "Up-to-date: ");
  53. message += toFile;
  54. this->Makefile->DisplayStatus(message, -1);
  55. }
  56. if (type != TypeDir) {
  57. // Add the file to the manifest.
  58. this->ManifestAppend(toFile);
  59. }
  60. }
  61. bool cmFileInstaller::ReportMissing(const std::string& fromFile)
  62. {
  63. return (this->Optional || this->cmFileCopier::ReportMissing(fromFile));
  64. }
  65. bool cmFileInstaller::Install(const std::string& fromFile,
  66. const std::string& toFile)
  67. {
  68. // Support installing from empty source to make a directory.
  69. if (this->InstallType == cmInstallType_DIRECTORY && fromFile.empty()) {
  70. return this->InstallDirectory(fromFile, toFile, MatchProperties());
  71. }
  72. return this->cmFileCopier::Install(fromFile, toFile);
  73. }
  74. void cmFileInstaller::DefaultFilePermissions()
  75. {
  76. this->cmFileCopier::DefaultFilePermissions();
  77. // Add execute permissions based on the target type.
  78. switch (this->InstallType) {
  79. case cmInstallType_SHARED_LIBRARY:
  80. case cmInstallType_MODULE_LIBRARY:
  81. if (this->Makefile->IsOn("CMAKE_INSTALL_SO_NO_EXE")) {
  82. break;
  83. }
  84. CM_FALLTHROUGH;
  85. case cmInstallType_EXECUTABLE:
  86. case cmInstallType_PROGRAMS:
  87. this->FilePermissions |= mode_owner_execute;
  88. this->FilePermissions |= mode_group_execute;
  89. this->FilePermissions |= mode_world_execute;
  90. break;
  91. default:
  92. break;
  93. }
  94. }
  95. bool cmFileInstaller::Parse(std::vector<std::string> const& args)
  96. {
  97. if (!this->cmFileCopier::Parse(args)) {
  98. return false;
  99. }
  100. if (!this->Rename.empty()) {
  101. if (!this->FilesFromDir.empty()) {
  102. this->Status.SetError("INSTALL option RENAME may not be "
  103. "combined with FILES_FROM_DIR.");
  104. return false;
  105. }
  106. if (this->InstallType != cmInstallType_FILES &&
  107. this->InstallType != cmInstallType_PROGRAMS) {
  108. this->Status.SetError("INSTALL option RENAME may be used "
  109. "only with FILES or PROGRAMS.");
  110. return false;
  111. }
  112. if (this->Files.size() > 1) {
  113. this->Status.SetError("INSTALL option RENAME may be used "
  114. "only with one file.");
  115. return false;
  116. }
  117. }
  118. if (!this->HandleInstallDestination()) {
  119. return false;
  120. }
  121. if (((this->MessageAlways ? 1 : 0) + (this->MessageLazy ? 1 : 0) +
  122. (this->MessageNever ? 1 : 0)) > 1) {
  123. this->Status.SetError("INSTALL options MESSAGE_ALWAYS, "
  124. "MESSAGE_LAZY, and MESSAGE_NEVER "
  125. "are mutually exclusive.");
  126. return false;
  127. }
  128. return true;
  129. }
  130. bool cmFileInstaller::CheckKeyword(std::string const& arg)
  131. {
  132. if (arg == "TYPE") {
  133. if (this->CurrentMatchRule) {
  134. this->NotAfterMatch(arg);
  135. } else {
  136. this->Doing = DoingType;
  137. }
  138. } else if (arg == "FILES") {
  139. if (this->CurrentMatchRule) {
  140. this->NotAfterMatch(arg);
  141. } else {
  142. this->Doing = DoingFiles;
  143. }
  144. } else if (arg == "RENAME") {
  145. if (this->CurrentMatchRule) {
  146. this->NotAfterMatch(arg);
  147. } else {
  148. this->Doing = DoingRename;
  149. }
  150. } else if (arg == "OPTIONAL") {
  151. if (this->CurrentMatchRule) {
  152. this->NotAfterMatch(arg);
  153. } else {
  154. this->Doing = DoingNone;
  155. this->Optional = true;
  156. }
  157. } else if (arg == "MESSAGE_ALWAYS") {
  158. if (this->CurrentMatchRule) {
  159. this->NotAfterMatch(arg);
  160. } else {
  161. this->Doing = DoingNone;
  162. this->MessageAlways = true;
  163. }
  164. } else if (arg == "MESSAGE_LAZY") {
  165. if (this->CurrentMatchRule) {
  166. this->NotAfterMatch(arg);
  167. } else {
  168. this->Doing = DoingNone;
  169. this->MessageLazy = true;
  170. }
  171. } else if (arg == "MESSAGE_NEVER") {
  172. if (this->CurrentMatchRule) {
  173. this->NotAfterMatch(arg);
  174. } else {
  175. this->Doing = DoingNone;
  176. this->MessageNever = true;
  177. }
  178. } else if (arg == "PERMISSIONS") {
  179. if (this->CurrentMatchRule) {
  180. this->Doing = DoingPermissionsMatch;
  181. } else {
  182. // file(INSTALL) aliases PERMISSIONS to FILE_PERMISSIONS
  183. this->Doing = DoingPermissionsFile;
  184. this->UseGivenPermissionsFile = true;
  185. }
  186. } else if (arg == "DIR_PERMISSIONS") {
  187. if (this->CurrentMatchRule) {
  188. this->NotAfterMatch(arg);
  189. } else {
  190. // file(INSTALL) aliases DIR_PERMISSIONS to DIRECTORY_PERMISSIONS
  191. this->Doing = DoingPermissionsDir;
  192. this->UseGivenPermissionsDir = true;
  193. }
  194. } else if (arg == "COMPONENTS" || arg == "CONFIGURATIONS" ||
  195. arg == "PROPERTIES") {
  196. std::ostringstream e;
  197. e << "INSTALL called with old-style " << arg << " argument. "
  198. << "This script was generated with an older version of CMake. "
  199. << "Re-run this cmake version on your build tree.";
  200. this->Status.SetError(e.str());
  201. this->Doing = DoingError;
  202. } else {
  203. return this->cmFileCopier::CheckKeyword(arg);
  204. }
  205. return true;
  206. }
  207. bool cmFileInstaller::CheckValue(std::string const& arg)
  208. {
  209. switch (this->Doing) {
  210. case DoingType:
  211. if (!this->GetTargetTypeFromString(arg)) {
  212. this->Doing = DoingError;
  213. }
  214. break;
  215. case DoingRename:
  216. this->Rename = arg;
  217. break;
  218. default:
  219. return this->cmFileCopier::CheckValue(arg);
  220. }
  221. return true;
  222. }
  223. bool cmFileInstaller::GetTargetTypeFromString(const std::string& stype)
  224. {
  225. if (stype == "EXECUTABLE") {
  226. this->InstallType = cmInstallType_EXECUTABLE;
  227. } else if (stype == "FILE") {
  228. this->InstallType = cmInstallType_FILES;
  229. } else if (stype == "PROGRAM") {
  230. this->InstallType = cmInstallType_PROGRAMS;
  231. } else if (stype == "STATIC_LIBRARY") {
  232. this->InstallType = cmInstallType_STATIC_LIBRARY;
  233. } else if (stype == "SHARED_LIBRARY") {
  234. this->InstallType = cmInstallType_SHARED_LIBRARY;
  235. } else if (stype == "MODULE") {
  236. this->InstallType = cmInstallType_MODULE_LIBRARY;
  237. } else if (stype == "DIRECTORY") {
  238. this->InstallType = cmInstallType_DIRECTORY;
  239. } else {
  240. std::ostringstream e;
  241. e << "Option TYPE given unknown value \"" << stype << "\".";
  242. this->Status.SetError(e.str());
  243. return false;
  244. }
  245. return true;
  246. }
  247. bool cmFileInstaller::HandleInstallDestination()
  248. {
  249. std::string& destination = this->Destination;
  250. // allow for / to be a valid destination
  251. if (destination.size() < 2 && destination != "/") {
  252. this->Status.SetError("called with inappropriate arguments. "
  253. "No DESTINATION provided or .");
  254. return false;
  255. }
  256. std::string sdestdir;
  257. if (cmSystemTools::GetEnv("DESTDIR", sdestdir) && !sdestdir.empty()) {
  258. cmSystemTools::ConvertToUnixSlashes(sdestdir);
  259. char ch1 = destination[0];
  260. char ch2 = destination[1];
  261. char ch3 = 0;
  262. if (destination.size() > 2) {
  263. ch3 = destination[2];
  264. }
  265. int skip = 0;
  266. if (ch1 != '/') {
  267. int relative = 0;
  268. if (((ch1 >= 'a' && ch1 <= 'z') || (ch1 >= 'A' && ch1 <= 'Z')) &&
  269. ch2 == ':') {
  270. // Assume windows
  271. // let's do some destdir magic:
  272. skip = 2;
  273. if (ch3 != '/') {
  274. relative = 1;
  275. }
  276. } else {
  277. relative = 1;
  278. }
  279. if (relative) {
  280. // This is relative path on unix or windows. Since we are doing
  281. // destdir, this case does not make sense.
  282. this->Status.SetError(
  283. "called with relative DESTINATION. This "
  284. "does not make sense when using DESTDIR. Specify "
  285. "absolute path or remove DESTDIR environment variable.");
  286. return false;
  287. }
  288. } else {
  289. if (ch2 == '/') {
  290. // looks like a network path.
  291. std::string message =
  292. "called with network path DESTINATION. This "
  293. "does not make sense when using DESTDIR. Specify local "
  294. "absolute path or remove DESTDIR environment variable."
  295. "\nDESTINATION=\n";
  296. message += destination;
  297. this->Status.SetError(message);
  298. return false;
  299. }
  300. }
  301. destination = sdestdir + destination.substr(skip);
  302. this->DestDirLength = int(sdestdir.size());
  303. }
  304. // check if default dir creation permissions were set
  305. mode_t default_dir_mode_v = 0;
  306. mode_t* default_dir_mode = &default_dir_mode_v;
  307. if (!this->GetDefaultDirectoryPermissions(&default_dir_mode)) {
  308. return false;
  309. }
  310. if (this->InstallType != cmInstallType_DIRECTORY) {
  311. if (!cmSystemTools::FileExists(destination)) {
  312. if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) {
  313. std::string errstring = "cannot create directory: " + destination +
  314. ". Maybe need administrative privileges.";
  315. this->Status.SetError(errstring);
  316. return false;
  317. }
  318. }
  319. if (!cmSystemTools::FileIsDirectory(destination)) {
  320. std::string errstring =
  321. "INSTALL destination: " + destination + " is not a directory.";
  322. this->Status.SetError(errstring);
  323. return false;
  324. }
  325. }
  326. return true;
  327. }