cmCPackFreeBSDGenerator.cxx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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 (std::string::size_type i = 0; i < v.value.length(); ++i) {
  49. char c = v.value[i];
  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(const std::string& k)
  90. : key(k)
  91. {
  92. }
  93. virtual ~ManifestKey() {}
  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, const std::string& v)
  104. : ManifestKey(k)
  105. , value(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. typedef std::vector<std::string> VList;
  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 (VList::const_iterator it = v.begin(); it != v.end(); ++it) {
  131. (*this) << (*it);
  132. }
  133. return *this;
  134. }
  135. void write_value(cmGeneratedFileStream& s) const override
  136. {
  137. bool with_comma = false;
  138. s << '[';
  139. for (VList::const_iterator it = value.begin(); it != value.end(); ++it) {
  140. s << (with_comma ? ',' : ' ');
  141. s << EscapeQuotes(*it);
  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 (VList::const_iterator it = value.begin(); it != value.end(); ++it) {
  161. s << " \"" << *it << "\": {\"origin\": \"" << *it << "\"},\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. const char* pv = this->GetOption(var_name);
  180. if (!pv) {
  181. return std::string();
  182. } else {
  183. return pv;
  184. }
  185. }
  186. // Produce UCL in the given @p manifest file for the common
  187. // manifest fields (common to the compact and regular formats),
  188. // by reading the CPACK_FREEBSD_* variables.
  189. void cmCPackFreeBSDGenerator::write_manifest_fields(
  190. cmGeneratedFileStream& manifest)
  191. {
  192. manifest << ManifestKeyValue("name",
  193. var_lookup("CPACK_FREEBSD_PACKAGE_NAME"));
  194. manifest << ManifestKeyValue("origin",
  195. var_lookup("CPACK_FREEBSD_PACKAGE_ORIGIN"));
  196. manifest << ManifestKeyValue("version",
  197. var_lookup("CPACK_FREEBSD_PACKAGE_VERSION"));
  198. manifest << ManifestKeyValue("maintainer",
  199. var_lookup("CPACK_FREEBSD_PACKAGE_MAINTAINER"));
  200. manifest << ManifestKeyValue("comment",
  201. var_lookup("CPACK_FREEBSD_PACKAGE_COMMENT"));
  202. manifest << ManifestKeyValue(
  203. "desc", var_lookup("CPACK_FREEBSD_PACKAGE_DESCRIPTION"));
  204. manifest << ManifestKeyValue("www", var_lookup("CPACK_FREEBSD_PACKAGE_WWW"));
  205. std::vector<std::string> licenses;
  206. cmSystemTools::ExpandListArgument(
  207. var_lookup("CPACK_FREEBSD_PACKAGE_LICENSE"), licenses);
  208. std::string licenselogic("single");
  209. if (licenses.size() < 1) {
  210. cmSystemTools::SetFatalErrorOccured();
  211. } else if (licenses.size() > 1) {
  212. licenselogic = var_lookup("CPACK_FREEBSD_PACKAGE_LICENSE_LOGIC");
  213. }
  214. manifest << ManifestKeyValue("licenselogic", licenselogic);
  215. manifest << (ManifestKeyListValue("licenses") << licenses);
  216. std::vector<std::string> categories;
  217. cmSystemTools::ExpandListArgument(
  218. var_lookup("CPACK_FREEBSD_PACKAGE_CATEGORIES"), categories);
  219. manifest << (ManifestKeyListValue("categories") << categories);
  220. manifest << ManifestKeyValue("prefix", var_lookup("CMAKE_INSTALL_PREFIX"));
  221. std::vector<std::string> deps;
  222. cmSystemTools::ExpandListArgument(var_lookup("CPACK_FREEBSD_PACKAGE_DEPS"),
  223. deps);
  224. if (deps.size() > 0) {
  225. manifest << (ManifestKeyDepsValue("deps") << deps);
  226. }
  227. }
  228. // Package only actual files; others are ignored (in particular,
  229. // intermediate subdirectories are ignored).
  230. static bool ignore_file(const std::string& filename)
  231. {
  232. struct stat statbuf;
  233. if (!((stat(filename.c_str(), &statbuf) >= 0) &&
  234. ((statbuf.st_mode & S_IFMT) == S_IFREG))) {
  235. return true;
  236. }
  237. // May be other reasons to return false
  238. return false;
  239. }
  240. // Write the given list of @p files to the manifest stream @p s,
  241. // as the UCL field "files" (which is dictionary-valued, to
  242. // associate filenames with hashes). All the files are transformed
  243. // to paths relative to @p toplevel, with a leading / (since the paths
  244. // in FreeBSD package files are supposed to be absolute).
  245. void write_manifest_files(cmGeneratedFileStream& s,
  246. const std::string& toplevel,
  247. const std::vector<std::string>& files)
  248. {
  249. const char* c_toplevel = toplevel.c_str();
  250. std::vector<std::string>::const_iterator it;
  251. s << "\"files\": {\n";
  252. for (it = files.begin(); it != files.end(); ++it) {
  253. s << " \"/" << cmSystemTools::RelativePath(c_toplevel, it->c_str())
  254. << "\": \""
  255. << "<sha256>"
  256. << "\",\n";
  257. }
  258. s << " },\n";
  259. }
  260. static bool has_suffix(const std::string& str, const std::string& suffix)
  261. {
  262. return str.size() >= suffix.size() &&
  263. str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
  264. }
  265. int cmCPackFreeBSDGenerator::PackageFiles()
  266. {
  267. if (!this->ReadListFile("CPackFreeBSD.cmake")) {
  268. cmCPackLogger(cmCPackLog::LOG_ERROR,
  269. "Error while execution CPackFreeBSD.cmake" << std::endl);
  270. return 0;
  271. }
  272. std::vector<std::string>::const_iterator fileIt;
  273. std::string dir = cmSystemTools::GetCurrentWorkingDirectory();
  274. cmSystemTools::ChangeDirectory(toplevel);
  275. files.erase(std::remove_if(files.begin(), files.end(), ignore_file),
  276. files.end());
  277. std::string manifestname = toplevel + "/+MANIFEST";
  278. {
  279. cmGeneratedFileStream manifest(manifestname.c_str());
  280. manifest << "{\n";
  281. write_manifest_fields(manifest);
  282. write_manifest_files(manifest, toplevel, files);
  283. manifest << "}\n";
  284. }
  285. cmCPackLogger(cmCPackLog::LOG_DEBUG, "Toplevel: " << toplevel << std::endl);
  286. if (WantsComponentInstallation()) {
  287. // CASE 1 : COMPONENT ALL-IN-ONE package
  288. // If ALL COMPONENTS in ONE package has been requested
  289. // then the package file is unique and should be open here.
  290. if (componentPackageMethod == ONE_PACKAGE) {
  291. return PackageComponentsAllInOne();
  292. }
  293. // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one)
  294. // There will be 1 package for each component group
  295. // however one may require to ignore component group and
  296. // in this case you'll get 1 package for each component.
  297. return PackageComponents(componentPackageMethod ==
  298. ONE_PACKAGE_PER_COMPONENT);
  299. }
  300. std::string output_dir =
  301. cmSystemTools::CollapseCombinedPath(toplevel, "../");
  302. pkg_create_from_manifest(output_dir.c_str(), ::TXZ, toplevel.c_str(),
  303. manifestname.c_str(), NULL);
  304. std::string broken_suffix = std::string("-") +
  305. var_lookup("CPACK_TOPLEVEL_TAG") + std::string(GetOutputExtension());
  306. for (std::vector<std::string>::iterator it = packageFileNames.begin();
  307. it != packageFileNames.end(); ++it) {
  308. cmCPackLogger(cmCPackLog::LOG_DEBUG, "Packagefile " << *it << std::endl);
  309. if (has_suffix(*it, broken_suffix)) {
  310. it->replace(it->size() - broken_suffix.size(), std::string::npos,
  311. GetOutputExtension());
  312. break;
  313. }
  314. }
  315. cmSystemTools::ChangeDirectory(dir);
  316. return 1;
  317. }