cmCPackFreeBSDGenerator.cxx 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  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 "cmCPackFreeBSDGenerator.h"
  4. #include "cmArchiveWrite.h"
  5. #include "cmCPackArchiveGenerator.h"
  6. #include "cmCPackLog.h"
  7. #include "cmGeneratedFileStream.h"
  8. #include "cmStringAlgorithms.h"
  9. #include "cmSystemTools.h"
  10. #include "cmWorkingDirectory.h"
  11. // Needed for ::open() and ::stat()
  12. #include <algorithm>
  13. #include <ostream>
  14. #include <utility>
  15. #include <vector>
  16. #include <fcntl.h>
  17. #include <pkg.h>
  18. #include <sys/stat.h>
  19. cmCPackFreeBSDGenerator::cmCPackFreeBSDGenerator()
  20. : cmCPackArchiveGenerator(cmArchiveWrite::CompressXZ, "paxr", ".txz")
  21. {
  22. }
  23. int cmCPackFreeBSDGenerator::InitializeInternal()
  24. {
  25. this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr/local");
  26. this->SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "0");
  27. return this->Superclass::InitializeInternal();
  28. }
  29. cmCPackFreeBSDGenerator::~cmCPackFreeBSDGenerator() = default;
  30. // This is a wrapper, for use only in stream-based output,
  31. // that will output a string in UCL escaped fashion (in particular,
  32. // quotes and backslashes are escaped). The list of characters
  33. // to escape is taken from https://github.com/vstakhov/libucl
  34. // (which is the reference implementation pkg(8) refers to).
  35. class EscapeQuotes
  36. {
  37. public:
  38. const std::string& value;
  39. EscapeQuotes(const std::string& s)
  40. : value(s)
  41. {
  42. }
  43. };
  44. // Output a string as "string" with escaping applied.
  45. cmGeneratedFileStream& operator<<(cmGeneratedFileStream& s,
  46. const EscapeQuotes& v)
  47. {
  48. s << '"';
  49. for (char c : v.value) {
  50. switch (c) {
  51. case '\n':
  52. s << "\\n";
  53. break;
  54. case '\r':
  55. s << "\\r";
  56. break;
  57. case '\b':
  58. s << "\\b";
  59. break;
  60. case '\t':
  61. s << "\\t";
  62. break;
  63. case '\f':
  64. s << "\\f";
  65. break;
  66. case '\\':
  67. s << "\\\\";
  68. break;
  69. case '"':
  70. s << "\\\"";
  71. break;
  72. default:
  73. s << c;
  74. break;
  75. }
  76. }
  77. s << '"';
  78. return s;
  79. }
  80. // The following classes are all helpers for writing out the UCL
  81. // manifest file (it also looks like JSON). ManifestKey just has
  82. // a (string-valued) key; subclasses add a specific kind of
  83. // value-type to the key, and implement write_value() to output
  84. // the corresponding UCL.
  85. class ManifestKey
  86. {
  87. public:
  88. std::string key;
  89. ManifestKey(std::string k)
  90. : key(std::move(k))
  91. {
  92. }
  93. virtual ~ManifestKey() = default;
  94. // Output the value associated with this key to the stream @p s.
  95. // Format is to be decided by subclasses.
  96. virtual void write_value(cmGeneratedFileStream& s) const = 0;
  97. };
  98. // Basic string-value (e.g. "name": "cmake")
  99. class ManifestKeyValue : public ManifestKey
  100. {
  101. public:
  102. std::string value;
  103. ManifestKeyValue(const std::string& k, std::string v)
  104. : ManifestKey(k)
  105. , value(std::move(v))
  106. {
  107. }
  108. void write_value(cmGeneratedFileStream& s) const override
  109. {
  110. s << EscapeQuotes(value);
  111. }
  112. };
  113. // List-of-strings values (e.g. "licenses": ["GPLv2", "LGPLv2"])
  114. class ManifestKeyListValue : public ManifestKey
  115. {
  116. public:
  117. using VList = std::vector<std::string>;
  118. VList value;
  119. ManifestKeyListValue(const std::string& k)
  120. : ManifestKey(k)
  121. {
  122. }
  123. ManifestKeyListValue& operator<<(const std::string& v)
  124. {
  125. value.push_back(v);
  126. return *this;
  127. }
  128. ManifestKeyListValue& operator<<(const std::vector<std::string>& v)
  129. {
  130. for (std::string const& e : v) {
  131. (*this) << e;
  132. }
  133. return *this;
  134. }
  135. void write_value(cmGeneratedFileStream& s) const override
  136. {
  137. bool with_comma = false;
  138. s << '[';
  139. for (std::string const& elem : value) {
  140. s << (with_comma ? ',' : ' ');
  141. s << EscapeQuotes(elem);
  142. with_comma = true;
  143. }
  144. s << " ]";
  145. }
  146. };
  147. // Deps: actually a dictionary, but we'll treat it as a
  148. // list so we only name the deps, and produce dictionary-
  149. // like output via write_value()
  150. class ManifestKeyDepsValue : public ManifestKeyListValue
  151. {
  152. public:
  153. ManifestKeyDepsValue(const std::string& k)
  154. : ManifestKeyListValue(k)
  155. {
  156. }
  157. void write_value(cmGeneratedFileStream& s) const override
  158. {
  159. s << "{\n";
  160. for (std::string const& elem : value) {
  161. s << " \"" << elem << R"(": {"origin": ")" << elem << "\"},\n";
  162. }
  163. s << '}';
  164. }
  165. };
  166. // Write one of the key-value classes (above) to the stream @p s
  167. cmGeneratedFileStream& operator<<(cmGeneratedFileStream& s,
  168. const ManifestKey& v)
  169. {
  170. s << '"' << v.key << "\": ";
  171. v.write_value(s);
  172. s << ",\n";
  173. return s;
  174. }
  175. // Look up variable; if no value is set, returns an empty string;
  176. // basically a wrapper that handles the NULL-ptr return from GetOption().
  177. std::string cmCPackFreeBSDGenerator::var_lookup(const char* var_name)
  178. {
  179. cmValue pv = this->GetOption(var_name);
  180. if (!pv) {
  181. return std::string();
  182. }
  183. return *pv;
  184. }
  185. // Produce UCL in the given @p manifest file for the common
  186. // manifest fields (common to the compact and regular formats),
  187. // by reading the CPACK_FREEBSD_* variables.
  188. void cmCPackFreeBSDGenerator::write_manifest_fields(
  189. cmGeneratedFileStream& manifest)
  190. {
  191. manifest << ManifestKeyValue("name",
  192. var_lookup("CPACK_FREEBSD_PACKAGE_NAME"));
  193. manifest << ManifestKeyValue("origin",
  194. var_lookup("CPACK_FREEBSD_PACKAGE_ORIGIN"));
  195. manifest << ManifestKeyValue("version",
  196. var_lookup("CPACK_FREEBSD_PACKAGE_VERSION"));
  197. manifest << ManifestKeyValue("maintainer",
  198. var_lookup("CPACK_FREEBSD_PACKAGE_MAINTAINER"));
  199. manifest << ManifestKeyValue("comment",
  200. var_lookup("CPACK_FREEBSD_PACKAGE_COMMENT"));
  201. manifest << ManifestKeyValue(
  202. "desc", var_lookup("CPACK_FREEBSD_PACKAGE_DESCRIPTION"));
  203. manifest << ManifestKeyValue("www", var_lookup("CPACK_FREEBSD_PACKAGE_WWW"));
  204. std::vector<std::string> licenses =
  205. cmExpandedList(var_lookup("CPACK_FREEBSD_PACKAGE_LICENSE"));
  206. std::string licenselogic("single");
  207. if (licenses.empty()) {
  208. cmSystemTools::SetFatalErrorOccured();
  209. } else if (licenses.size() > 1) {
  210. licenselogic = var_lookup("CPACK_FREEBSD_PACKAGE_LICENSE_LOGIC");
  211. }
  212. manifest << ManifestKeyValue("licenselogic", licenselogic);
  213. manifest << (ManifestKeyListValue("licenses") << licenses);
  214. std::vector<std::string> categories =
  215. cmExpandedList(var_lookup("CPACK_FREEBSD_PACKAGE_CATEGORIES"));
  216. manifest << (ManifestKeyListValue("categories") << categories);
  217. manifest << ManifestKeyValue("prefix", var_lookup("CMAKE_INSTALL_PREFIX"));
  218. std::vector<std::string> deps =
  219. cmExpandedList(var_lookup("CPACK_FREEBSD_PACKAGE_DEPS"));
  220. if (!deps.empty()) {
  221. manifest << (ManifestKeyDepsValue("deps") << deps);
  222. }
  223. }
  224. // Package only actual files; others are ignored (in particular,
  225. // intermediate subdirectories are ignored).
  226. static bool ignore_file(const std::string& filename)
  227. {
  228. struct stat statbuf;
  229. return stat(filename.c_str(), &statbuf) < 0 ||
  230. (statbuf.st_mode & S_IFMT) != S_IFREG;
  231. }
  232. // Write the given list of @p files to the manifest stream @p s,
  233. // as the UCL field "files" (which is dictionary-valued, to
  234. // associate filenames with hashes). All the files are transformed
  235. // to paths relative to @p toplevel, with a leading / (since the paths
  236. // in FreeBSD package files are supposed to be absolute).
  237. void write_manifest_files(cmGeneratedFileStream& s,
  238. const std::string& toplevel,
  239. const std::vector<std::string>& files)
  240. {
  241. s << "\"files\": {\n";
  242. for (std::string const& file : files) {
  243. s << " \"/" << cmSystemTools::RelativePath(toplevel, file) << "\": \""
  244. << "<sha256>"
  245. << "\",\n";
  246. }
  247. s << " },\n";
  248. }
  249. int cmCPackFreeBSDGenerator::PackageFiles()
  250. {
  251. if (!this->ReadListFile("Internal/CPack/CPackFreeBSD.cmake")) {
  252. cmCPackLogger(cmCPackLog::LOG_ERROR,
  253. "Error while execution CPackFreeBSD.cmake" << std::endl);
  254. return 0;
  255. }
  256. std::vector<std::string>::const_iterator fileIt;
  257. cmWorkingDirectory wd(toplevel);
  258. files.erase(std::remove_if(files.begin(), files.end(), ignore_file),
  259. files.end());
  260. std::string manifestname = toplevel + "/+MANIFEST";
  261. {
  262. cmGeneratedFileStream manifest(manifestname);
  263. manifest << "{\n";
  264. write_manifest_fields(manifest);
  265. write_manifest_files(manifest, toplevel, files);
  266. manifest << "}\n";
  267. }
  268. cmCPackLogger(cmCPackLog::LOG_DEBUG, "Toplevel: " << toplevel << std::endl);
  269. if (WantsComponentInstallation()) {
  270. // CASE 1 : COMPONENT ALL-IN-ONE package
  271. // If ALL COMPONENTS in ONE package has been requested
  272. // then the package file is unique and should be open here.
  273. if (componentPackageMethod == ONE_PACKAGE) {
  274. return PackageComponentsAllInOne();
  275. }
  276. // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one)
  277. // There will be 1 package for each component group
  278. // however one may require to ignore component group and
  279. // in this case you'll get 1 package for each component.
  280. return PackageComponents(componentPackageMethod ==
  281. ONE_PACKAGE_PER_COMPONENT);
  282. }
  283. std::string output_dir = cmSystemTools::CollapseFullPath("../", toplevel);
  284. pkg_create_from_manifest(output_dir.c_str(), ::TXZ, toplevel.c_str(),
  285. manifestname.c_str(), nullptr);
  286. std::string broken_suffix =
  287. cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), ".txz");
  288. for (std::string& name : packageFileNames) {
  289. cmCPackLogger(cmCPackLog::LOG_DEBUG, "Packagefile " << name << std::endl);
  290. if (cmHasSuffix(name, broken_suffix)) {
  291. name.replace(name.size() - broken_suffix.size(), std::string::npos,
  292. ".txz");
  293. break;
  294. }
  295. }
  296. return 1;
  297. }