cmCPackFreeBSDGenerator.cxx 10 KB

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