kibi-swipl-linux-x64-gnu 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (550) hide show
  1. package/README.md +29 -0
  2. package/bin/swipl +0 -0
  3. package/bin/swipl-win +0 -0
  4. package/build-manifest.json +70 -0
  5. package/lib/cmake/swipl/SWIPLConfig.cmake +21 -0
  6. package/lib/cmake/swipl/SWIPLConfigVersion.cmake +65 -0
  7. package/lib/cmake/swipl/SWIPLTargets-release.cmake +28 -0
  8. package/lib/cmake/swipl/SWIPLTargets.cmake +109 -0
  9. package/lib/swipl/ABI +1 -0
  10. package/lib/swipl/LICENSE +41 -0
  11. package/lib/swipl/README.md +116 -0
  12. package/lib/swipl/app/README.md +25 -0
  13. package/lib/swipl/app/app.pl +87 -0
  14. package/lib/swipl/app/pack.pl +350 -0
  15. package/lib/swipl/app/qlf.pl +521 -0
  16. package/lib/swipl/app/splfr.pl +177 -0
  17. package/lib/swipl/app/sys.pl +310 -0
  18. package/lib/swipl/bin/swipl.home +1 -0
  19. package/lib/swipl/boot/apply.pl +73 -0
  20. package/lib/swipl/boot/attvar.pl +344 -0
  21. package/lib/swipl/boot/autoload.pl +1226 -0
  22. package/lib/swipl/boot/bags.pl +283 -0
  23. package/lib/swipl/boot/dcg.pl +388 -0
  24. package/lib/swipl/boot/dicts.pl +193 -0
  25. package/lib/swipl/boot/dwim.pl +485 -0
  26. package/lib/swipl/boot/engines.pl +152 -0
  27. package/lib/swipl/boot/expand.pl +1628 -0
  28. package/lib/swipl/boot/gc.pl +65 -0
  29. package/lib/swipl/boot/history.pl +304 -0
  30. package/lib/swipl/boot/init.pl +4682 -0
  31. package/lib/swipl/boot/iri.pl +64 -0
  32. package/lib/swipl/boot/license.pl +390 -0
  33. package/lib/swipl/boot/load.pl +81 -0
  34. package/lib/swipl/boot/messages.pl +2521 -0
  35. package/lib/swipl/boot/packs.pl +306 -0
  36. package/lib/swipl/boot/predopts.pl +154 -0
  37. package/lib/swipl/boot/qlf.pl +141 -0
  38. package/lib/swipl/boot/rc.pl +263 -0
  39. package/lib/swipl/boot/syspred.pl +1634 -0
  40. package/lib/swipl/boot/tabling.pl +2321 -0
  41. package/lib/swipl/boot/threads.pl +42 -0
  42. package/lib/swipl/boot/toplevel.pl +2248 -0
  43. package/lib/swipl/boot/topvars.pl +261 -0
  44. package/lib/swipl/boot.prc +0 -0
  45. package/lib/swipl/cmake/swipl.cmake +131 -0
  46. package/lib/swipl/customize/README.md +23 -0
  47. package/lib/swipl/customize/edit +101 -0
  48. package/lib/swipl/customize/init.pl +164 -0
  49. package/lib/swipl/demo/README.md +33 -0
  50. package/lib/swipl/demo/likes.pl +44 -0
  51. package/lib/swipl/doc/packages/examples/chr/bool.chr +281 -0
  52. package/lib/swipl/doc/packages/examples/chr/chrdif.chr +87 -0
  53. package/lib/swipl/doc/packages/examples/chr/chrfreeze.chr +6 -0
  54. package/lib/swipl/doc/packages/examples/chr/family.chr +116 -0
  55. package/lib/swipl/doc/packages/examples/chr/fib.chr +24 -0
  56. package/lib/swipl/doc/packages/examples/chr/fibonacci.chr +31 -0
  57. package/lib/swipl/doc/packages/examples/chr/gcd.chr +28 -0
  58. package/lib/swipl/doc/packages/examples/chr/leq.chr +34 -0
  59. package/lib/swipl/doc/packages/examples/chr/listdom.chr +138 -0
  60. package/lib/swipl/doc/packages/examples/chr/primes.chr +29 -0
  61. package/lib/swipl/doc/packages/examples/http/README.md +98 -0
  62. package/lib/swipl/doc/packages/examples/http/calc.pl +92 -0
  63. package/lib/swipl/doc/packages/examples/http/demo_body.pl +232 -0
  64. package/lib/swipl/doc/packages/examples/http/demo_client.pl +165 -0
  65. package/lib/swipl/doc/packages/examples/http/demo_daemon.pl +38 -0
  66. package/lib/swipl/doc/packages/examples/http/demo_files.pl +50 -0
  67. package/lib/swipl/doc/packages/examples/http/demo_hello.pl +15 -0
  68. package/lib/swipl/doc/packages/examples/http/demo_login.pl +280 -0
  69. package/lib/swipl/doc/packages/examples/http/demo_openid.pl +177 -0
  70. package/lib/swipl/doc/packages/examples/http/demo_pwp.pl +20 -0
  71. package/lib/swipl/doc/packages/examples/http/demo_rest.pl +56 -0
  72. package/lib/swipl/doc/packages/examples/http/demo_threads.pl +52 -0
  73. package/lib/swipl/doc/packages/examples/http/linux-init-script +126 -0
  74. package/lib/swipl/doc/packages/examples/http/pwp/context.pwp +23 -0
  75. package/lib/swipl/doc/packages/examples/http/pwp/index.pwp +25 -0
  76. package/lib/swipl/doc/packages/examples/http/pwp/pwp1.pwp +12 -0
  77. package/lib/swipl/doc/packages/examples/http/pwp/pwp2.pwp +10 -0
  78. package/lib/swipl/doc/packages/examples/http/pwp/pwp3.pwp +25 -0
  79. package/lib/swipl/doc/packages/examples/http/pwp/pwp4.pwp +23 -0
  80. package/lib/swipl/doc/packages/examples/http/pwp/pwp5.pwp +11 -0
  81. package/lib/swipl/doc/packages/examples/http/pwp/pwp6.pwp +3 -0
  82. package/lib/swipl/doc/packages/examples/http/pwp/pwp7.pwp +3 -0
  83. package/lib/swipl/doc/packages/examples/http/pwp/pwp8.pwp +3 -0
  84. package/lib/swipl/doc/packages/examples/http/pwp/pwpdb.pl +12 -0
  85. package/lib/swipl/doc/packages/examples/http/systemd-script.service +18 -0
  86. package/lib/swipl/doc/packages/examples/http/upstart-script.conf +20 -0
  87. package/lib/swipl/doc/packages/examples/plunit/read.pl +17 -0
  88. package/lib/swipl/doc/packages/examples/plunit/simple.pl +33 -0
  89. package/lib/swipl/doc/packages/examples/ssl/client.pl +83 -0
  90. package/lib/swipl/doc/packages/examples/ssl/etc/README +52 -0
  91. package/lib/swipl/doc/packages/examples/ssl/etc/client/client-cert.p12 +0 -0
  92. package/lib/swipl/doc/packages/examples/ssl/etc/client/client-cert.pem +85 -0
  93. package/lib/swipl/doc/packages/examples/ssl/etc/client/client-key.pem +30 -0
  94. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/cacert.pem +82 -0
  95. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/careq.pem +18 -0
  96. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/crlnumber +1 -0
  97. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/index.txt +3 -0
  98. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/index.txt.attr +1 -0
  99. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/newcerts/9DC08797A0F63F7C.pem +85 -0
  100. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/newcerts/9DC08797A0F63F7D.pem +85 -0
  101. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/private/cakey.pem +30 -0
  102. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/serial +1 -0
  103. package/lib/swipl/doc/packages/examples/ssl/etc/server/server-cert.pem +85 -0
  104. package/lib/swipl/doc/packages/examples/ssl/etc/server/server-key.pem +30 -0
  105. package/lib/swipl/doc/packages/examples/ssl/https.pl +267 -0
  106. package/lib/swipl/doc/packages/examples/ssl/server.pl +106 -0
  107. package/lib/swipl/include/SWI-Prolog.h +1544 -0
  108. package/lib/swipl/include/SWI-Stream.h +537 -0
  109. package/lib/swipl/include/Yap/YapInterface.h +283 -0
  110. package/lib/swipl/include/sicstus/sicstus.h +452 -0
  111. package/lib/swipl/lib/x86_64-linux/cgi.so +0 -0
  112. package/lib/swipl/lib/x86_64-linux/crypt.so +0 -0
  113. package/lib/swipl/lib/x86_64-linux/crypto4pl.so +0 -0
  114. package/lib/swipl/lib/x86_64-linux/double_metaphone.so +0 -0
  115. package/lib/swipl/lib/x86_64-linux/files.so +0 -0
  116. package/lib/swipl/lib/x86_64-linux/hashstream.so +0 -0
  117. package/lib/swipl/lib/x86_64-linux/http_stream.so +0 -0
  118. package/lib/swipl/lib/x86_64-linux/isub.so +0 -0
  119. package/lib/swipl/lib/x86_64-linux/json.so +0 -0
  120. package/lib/swipl/lib/x86_64-linux/libswipl.so.10 +0 -0
  121. package/lib/swipl/lib/x86_64-linux/mallocinfo.so +0 -0
  122. package/lib/swipl/lib/x86_64-linux/md54pl.so +0 -0
  123. package/lib/swipl/lib/x86_64-linux/memfile.so +0 -0
  124. package/lib/swipl/lib/x86_64-linux/ntriples.so +0 -0
  125. package/lib/swipl/lib/x86_64-linux/pcre4pl.so +0 -0
  126. package/lib/swipl/lib/x86_64-linux/porter_stem.so +0 -0
  127. package/lib/swipl/lib/x86_64-linux/process.so +0 -0
  128. package/lib/swipl/lib/x86_64-linux/prolog_stream.so +0 -0
  129. package/lib/swipl/lib/x86_64-linux/rdf_db.so +0 -0
  130. package/lib/swipl/lib/x86_64-linux/readutil.so +0 -0
  131. package/lib/swipl/lib/x86_64-linux/rlimit.so +0 -0
  132. package/lib/swipl/lib/x86_64-linux/sched.so +0 -0
  133. package/lib/swipl/lib/x86_64-linux/sgml2pl.so +0 -0
  134. package/lib/swipl/lib/x86_64-linux/sha4pl.so +0 -0
  135. package/lib/swipl/lib/x86_64-linux/snowball.so +0 -0
  136. package/lib/swipl/lib/x86_64-linux/socket.so +0 -0
  137. package/lib/swipl/lib/x86_64-linux/ssl4pl.so +0 -0
  138. package/lib/swipl/lib/x86_64-linux/streaminfo.so +0 -0
  139. package/lib/swipl/lib/x86_64-linux/syslog.so +0 -0
  140. package/lib/swipl/lib/x86_64-linux/time.so +0 -0
  141. package/lib/swipl/lib/x86_64-linux/turtle.so +0 -0
  142. package/lib/swipl/lib/x86_64-linux/uid.so +0 -0
  143. package/lib/swipl/lib/x86_64-linux/unix.so +0 -0
  144. package/lib/swipl/lib/x86_64-linux/uri.so +0 -0
  145. package/lib/swipl/lib/x86_64-linux/websocket.so +0 -0
  146. package/lib/swipl/lib/x86_64-linux/zlib4pl.so +0 -0
  147. package/lib/swipl/library/INDEX.pl +1020 -0
  148. package/lib/swipl/library/aggregate.pl +755 -0
  149. package/lib/swipl/library/ansi_term.pl +653 -0
  150. package/lib/swipl/library/apply.pl +426 -0
  151. package/lib/swipl/library/apply_macros.pl +497 -0
  152. package/lib/swipl/library/arithmetic.pl +320 -0
  153. package/lib/swipl/library/assoc.pl +522 -0
  154. package/lib/swipl/library/atom.pl +169 -0
  155. package/lib/swipl/library/backcomp.pl +688 -0
  156. package/lib/swipl/library/base32.pl +291 -0
  157. package/lib/swipl/library/base64.pl +441 -0
  158. package/lib/swipl/library/broadcast.pl +161 -0
  159. package/lib/swipl/library/build/cmake.pl +137 -0
  160. package/lib/swipl/library/build/conan.pl +232 -0
  161. package/lib/swipl/library/build/make.pl +168 -0
  162. package/lib/swipl/library/build/tools.pl +825 -0
  163. package/lib/swipl/library/charsio.pl +195 -0
  164. package/lib/swipl/library/check.pl +1097 -0
  165. package/lib/swipl/library/check_installation.pl +912 -0
  166. package/lib/swipl/library/clp/INDEX.pl +157 -0
  167. package/lib/swipl/library/clp/bounds.pl +2036 -0
  168. package/lib/swipl/library/clp/clp_distinct.pl +263 -0
  169. package/lib/swipl/library/clp/clp_events.pl +92 -0
  170. package/lib/swipl/library/clp/clpb.pl +1896 -0
  171. package/lib/swipl/library/clp/clpfd.pl +7906 -0
  172. package/lib/swipl/library/clp/simplex.pl +1580 -0
  173. package/lib/swipl/library/codesio.pl +182 -0
  174. package/lib/swipl/library/coinduction.pl +183 -0
  175. package/lib/swipl/library/console_input.pl +289 -0
  176. package/lib/swipl/library/csv.pl +611 -0
  177. package/lib/swipl/library/ctypes.pl +161 -0
  178. package/lib/swipl/library/date.pl +370 -0
  179. package/lib/swipl/library/dcg/INDEX.pl +35 -0
  180. package/lib/swipl/library/dcg/basics.pl +514 -0
  181. package/lib/swipl/library/dcg/high_order.pl +227 -0
  182. package/lib/swipl/library/debug.pl +499 -0
  183. package/lib/swipl/library/dialect/bim.pl +186 -0
  184. package/lib/swipl/library/dialect/commons.pl +77 -0
  185. package/lib/swipl/library/dialect/eclipse/test_util_iso.pl +355 -0
  186. package/lib/swipl/library/dialect/hprolog/format.pl +44 -0
  187. package/lib/swipl/library/dialect/hprolog.pl +314 -0
  188. package/lib/swipl/library/dialect/ifprolog.pl +1317 -0
  189. package/lib/swipl/library/dialect/iso/iso_predicates.pl +277 -0
  190. package/lib/swipl/library/dialect/sicstus/README.TXT +1 -0
  191. package/lib/swipl/library/dialect/sicstus/arrays.pl +121 -0
  192. package/lib/swipl/library/dialect/sicstus/block.pl +278 -0
  193. package/lib/swipl/library/dialect/sicstus/lists.pl +167 -0
  194. package/lib/swipl/library/dialect/sicstus/ordsets.pl +52 -0
  195. package/lib/swipl/library/dialect/sicstus/sockets.pl +174 -0
  196. package/lib/swipl/library/dialect/sicstus/system.pl +262 -0
  197. package/lib/swipl/library/dialect/sicstus/terms.pl +57 -0
  198. package/lib/swipl/library/dialect/sicstus/timeout.pl +92 -0
  199. package/lib/swipl/library/dialect/sicstus.pl +582 -0
  200. package/lib/swipl/library/dialect/sicstus4/aggregate.pl +60 -0
  201. package/lib/swipl/library/dialect/sicstus4/between.pl +62 -0
  202. package/lib/swipl/library/dialect/sicstus4/clpfd.pl +75 -0
  203. package/lib/swipl/library/dialect/sicstus4/file_systems.pl +413 -0
  204. package/lib/swipl/library/dialect/sicstus4/lists.pl +274 -0
  205. package/lib/swipl/library/dialect/sicstus4/ordsets.pl +54 -0
  206. package/lib/swipl/library/dialect/sicstus4/samsort.pl +86 -0
  207. package/lib/swipl/library/dialect/sicstus4/sets.pl +124 -0
  208. package/lib/swipl/library/dialect/sicstus4/sockets.pl +164 -0
  209. package/lib/swipl/library/dialect/sicstus4/system.pl +80 -0
  210. package/lib/swipl/library/dialect/sicstus4/terms.pl +89 -0
  211. package/lib/swipl/library/dialect/sicstus4/timeout.pl +43 -0
  212. package/lib/swipl/library/dialect/sicstus4/types.pl +103 -0
  213. package/lib/swipl/library/dialect/sicstus4.pl +200 -0
  214. package/lib/swipl/library/dialect/swi/syspred_options.pl +250 -0
  215. package/lib/swipl/library/dialect/xsb/README.md +20 -0
  216. package/lib/swipl/library/dialect/xsb/basics.pl +318 -0
  217. package/lib/swipl/library/dialect/xsb/consult.pl +87 -0
  218. package/lib/swipl/library/dialect/xsb/curr_sym.pl +57 -0
  219. package/lib/swipl/library/dialect/xsb/error_handler.pl +111 -0
  220. package/lib/swipl/library/dialect/xsb/gensym.pl +53 -0
  221. package/lib/swipl/library/dialect/xsb/gpp.pl +138 -0
  222. package/lib/swipl/library/dialect/xsb/intern.pl +48 -0
  223. package/lib/swipl/library/dialect/xsb/lists.pl +47 -0
  224. package/lib/swipl/library/dialect/xsb/machine.pl +295 -0
  225. package/lib/swipl/library/dialect/xsb/ordsets.pl +53 -0
  226. package/lib/swipl/library/dialect/xsb/setof.pl +54 -0
  227. package/lib/swipl/library/dialect/xsb/source.pl +308 -0
  228. package/lib/swipl/library/dialect/xsb/standard.pl +169 -0
  229. package/lib/swipl/library/dialect/xsb/storage.pl +70 -0
  230. package/lib/swipl/library/dialect/xsb/string.pl +76 -0
  231. package/lib/swipl/library/dialect/xsb/thread.pl +62 -0
  232. package/lib/swipl/library/dialect/xsb/timed_call.pl +158 -0
  233. package/lib/swipl/library/dialect/xsb.pl +688 -0
  234. package/lib/swipl/library/dialect/yap/README.TXT +8 -0
  235. package/lib/swipl/library/dialect/yap.pl +283 -0
  236. package/lib/swipl/library/dialect.pl +123 -0
  237. package/lib/swipl/library/dicts.pl +399 -0
  238. package/lib/swipl/library/dif.pl +426 -0
  239. package/lib/swipl/library/edinburgh.pl +138 -0
  240. package/lib/swipl/library/edit.pl +783 -0
  241. package/lib/swipl/library/error.pl +495 -0
  242. package/lib/swipl/library/exceptions.pl +231 -0
  243. package/lib/swipl/library/explain.pl +543 -0
  244. package/lib/swipl/library/ext/RDF/INDEX.pl +25 -0
  245. package/lib/swipl/library/ext/RDF/rdf.pl +450 -0
  246. package/lib/swipl/library/ext/RDF/rdf_parser.pl +649 -0
  247. package/lib/swipl/library/ext/RDF/rdf_triple.pl +448 -0
  248. package/lib/swipl/library/ext/RDF/rdf_write.pl +701 -0
  249. package/lib/swipl/library/ext/RDF/rewrite_term.pl +177 -0
  250. package/lib/swipl/library/ext/chr/chr/a_star.pl +80 -0
  251. package/lib/swipl/library/ext/chr/chr/binomialheap.pl +142 -0
  252. package/lib/swipl/library/ext/chr/chr/builtins.pl +629 -0
  253. package/lib/swipl/library/ext/chr/chr/chr_compiler_errors.pl +179 -0
  254. package/lib/swipl/library/ext/chr/chr/chr_compiler_options.pl +382 -0
  255. package/lib/swipl/library/ext/chr/chr/chr_compiler_utility.pl +338 -0
  256. package/lib/swipl/library/ext/chr/chr/chr_debug.pl +59 -0
  257. package/lib/swipl/library/ext/chr/chr/chr_hashtable_store.pl +425 -0
  258. package/lib/swipl/library/ext/chr/chr/chr_integertable_store.pl +139 -0
  259. package/lib/swipl/library/ext/chr/chr/chr_messages.pl +176 -0
  260. package/lib/swipl/library/ext/chr/chr/chr_op.pl +53 -0
  261. package/lib/swipl/library/ext/chr/chr/chr_runtime.pl +1016 -0
  262. package/lib/swipl/library/ext/chr/chr/chr_translate.pl +25424 -0
  263. package/lib/swipl/library/ext/chr/chr/clean_code.pl +252 -0
  264. package/lib/swipl/library/ext/chr/chr/find.pl +78 -0
  265. package/lib/swipl/library/ext/chr/chr/guard_entailment.pl +19432 -0
  266. package/lib/swipl/library/ext/chr/chr/listmap.pl +108 -0
  267. package/lib/swipl/library/ext/chr/chr/pairlist.pl +78 -0
  268. package/lib/swipl/library/ext/chr/chr.pl +686 -0
  269. package/lib/swipl/library/ext/clib/INDEX.pl +158 -0
  270. package/lib/swipl/library/ext/clib/cgi.pl +86 -0
  271. package/lib/swipl/library/ext/clib/crypt.pl +44 -0
  272. package/lib/swipl/library/ext/clib/filesex.pl +644 -0
  273. package/lib/swipl/library/ext/clib/hash_stream.pl +119 -0
  274. package/lib/swipl/library/ext/clib/mallocinfo.pl +138 -0
  275. package/lib/swipl/library/ext/clib/md5.pl +68 -0
  276. package/lib/swipl/library/ext/clib/memfile.pl +61 -0
  277. package/lib/swipl/library/ext/clib/process.pl +573 -0
  278. package/lib/swipl/library/ext/clib/prolog_server.pl +157 -0
  279. package/lib/swipl/library/ext/clib/prolog_stream.pl +80 -0
  280. package/lib/swipl/library/ext/clib/rlimit.pl +54 -0
  281. package/lib/swipl/library/ext/clib/sched.pl +85 -0
  282. package/lib/swipl/library/ext/clib/sha.pl +162 -0
  283. package/lib/swipl/library/ext/clib/socket.pl +1102 -0
  284. package/lib/swipl/library/ext/clib/streaminfo.pl +103 -0
  285. package/lib/swipl/library/ext/clib/streampool.pl +136 -0
  286. package/lib/swipl/library/ext/clib/syslog.pl +180 -0
  287. package/lib/swipl/library/ext/clib/time.pl +194 -0
  288. package/lib/swipl/library/ext/clib/udp_broadcast.pl +975 -0
  289. package/lib/swipl/library/ext/clib/uid.pl +220 -0
  290. package/lib/swipl/library/ext/clib/unix.pl +317 -0
  291. package/lib/swipl/library/ext/clib/uri.pl +530 -0
  292. package/lib/swipl/library/ext/clib/uuid.pl +207 -0
  293. package/lib/swipl/library/ext/http/http/INDEX.pl +266 -0
  294. package/lib/swipl/library/ext/http/http/README.md +34 -0
  295. package/lib/swipl/library/ext/http/http/ax.pl +189 -0
  296. package/lib/swipl/library/ext/http/http/dcg_basics.pl +49 -0
  297. package/lib/swipl/library/ext/http/http/graphql.pl +1367 -0
  298. package/lib/swipl/library/ext/http/http/html_decl.pl +416 -0
  299. package/lib/swipl/library/ext/http/http/html_head.pl +654 -0
  300. package/lib/swipl/library/ext/http/http/html_quasiquotations.pl +120 -0
  301. package/lib/swipl/library/ext/http/http/html_write.pl +1425 -0
  302. package/lib/swipl/library/ext/http/http/htmx.pl +139 -0
  303. package/lib/swipl/library/ext/http/http/http_authenticate.pl +364 -0
  304. package/lib/swipl/library/ext/http/http/http_client.pl +412 -0
  305. package/lib/swipl/library/ext/http/http/http_cookie.pl +245 -0
  306. package/lib/swipl/library/ext/http/http/http_cors.pl +176 -0
  307. package/lib/swipl/library/ext/http/http/http_digest.pl +820 -0
  308. package/lib/swipl/library/ext/http/http/http_dirindex.pl +296 -0
  309. package/lib/swipl/library/ext/http/http/http_dispatch.pl +1552 -0
  310. package/lib/swipl/library/ext/http/http/http_dyn_workers.pl +214 -0
  311. package/lib/swipl/library/ext/http/http/http_error.pl +126 -0
  312. package/lib/swipl/library/ext/http/http/http_exception.pl +225 -0
  313. package/lib/swipl/library/ext/http/http/http_files.pl +163 -0
  314. package/lib/swipl/library/ext/http/http/http_header.pl +2820 -0
  315. package/lib/swipl/library/ext/http/http/http_hook.pl +106 -0
  316. package/lib/swipl/library/ext/http/http/http_host.pl +204 -0
  317. package/lib/swipl/library/ext/http/http/http_load.pl +113 -0
  318. package/lib/swipl/library/ext/http/http/http_log.pl +738 -0
  319. package/lib/swipl/library/ext/http/http/http_multipart_plugin.pl +232 -0
  320. package/lib/swipl/library/ext/http/http/http_open.pl +1821 -0
  321. package/lib/swipl/library/ext/http/http/http_openid.pl +1561 -0
  322. package/lib/swipl/library/ext/http/http/http_parameters.pl +504 -0
  323. package/lib/swipl/library/ext/http/http/http_path.pl +380 -0
  324. package/lib/swipl/library/ext/http/http/http_proxy.pl +80 -0
  325. package/lib/swipl/library/ext/http/http/http_pwp.pl +351 -0
  326. package/lib/swipl/library/ext/http/http/http_redis_plugin.pl +332 -0
  327. package/lib/swipl/library/ext/http/http/http_server.pl +105 -0
  328. package/lib/swipl/library/ext/http/http/http_server_files.pl +126 -0
  329. package/lib/swipl/library/ext/http/http/http_server_health.pl +186 -0
  330. package/lib/swipl/library/ext/http/http/http_session.pl +1040 -0
  331. package/lib/swipl/library/ext/http/http/http_sgml_plugin.pl +137 -0
  332. package/lib/swipl/library/ext/http/http/http_stream.pl +376 -0
  333. package/lib/swipl/library/ext/http/http/http_unix_daemon.pl +989 -0
  334. package/lib/swipl/library/ext/http/http/http_wrapper.pl +559 -0
  335. package/lib/swipl/library/ext/http/http/hub.pl +600 -0
  336. package/lib/swipl/library/ext/http/http/js_write.pl +461 -0
  337. package/lib/swipl/library/ext/http/http/mimepack.pl +224 -0
  338. package/lib/swipl/library/ext/http/http/mimetype.pl +276 -0
  339. package/lib/swipl/library/ext/http/http/term_html.pl +797 -0
  340. package/lib/swipl/library/ext/http/http/thread_httpd.pl +1181 -0
  341. package/lib/swipl/library/ext/http/http/web/css/dirindex.css +24 -0
  342. package/lib/swipl/library/ext/http/http/web/css/openid.css +57 -0
  343. package/lib/swipl/library/ext/http/http/web/icons/back.png +0 -0
  344. package/lib/swipl/library/ext/http/http/web/icons/c.png +0 -0
  345. package/lib/swipl/library/ext/http/http/web/icons/compressed.png +0 -0
  346. package/lib/swipl/library/ext/http/http/web/icons/folder.png +0 -0
  347. package/lib/swipl/library/ext/http/http/web/icons/generic.png +0 -0
  348. package/lib/swipl/library/ext/http/http/web/icons/layout.png +0 -0
  349. package/lib/swipl/library/ext/http/http/web/icons/openid-logo-square.png +0 -0
  350. package/lib/swipl/library/ext/http/http/web/icons/openid-logo-tiny.png +0 -0
  351. package/lib/swipl/library/ext/http/http/websocket.pl +729 -0
  352. package/lib/swipl/library/ext/http/http/yadis.pl +205 -0
  353. package/lib/swipl/library/ext/json/INDEX.pl +36 -0
  354. package/lib/swipl/library/ext/json/http/INDEX.pl +16 -0
  355. package/lib/swipl/library/ext/json/http/http_json.pl +490 -0
  356. package/lib/swipl/library/ext/json/http/js_grammar.pl +53 -0
  357. package/lib/swipl/library/ext/json/http/json.pl +48 -0
  358. package/lib/swipl/library/ext/json/http/json_convert.pl +48 -0
  359. package/lib/swipl/library/ext/json/json.pl +1215 -0
  360. package/lib/swipl/library/ext/json/json_convert.pl +681 -0
  361. package/lib/swipl/library/ext/json/json_grammar.pl +349 -0
  362. package/lib/swipl/library/ext/json/json_rpc_client.pl +423 -0
  363. package/lib/swipl/library/ext/json/json_rpc_common.pl +104 -0
  364. package/lib/swipl/library/ext/json/json_rpc_server.pl +489 -0
  365. package/lib/swipl/library/ext/json/json_schema.pl +2111 -0
  366. package/lib/swipl/library/ext/nlp/INDEX.pl +15 -0
  367. package/lib/swipl/library/ext/nlp/double_metaphone.pl +86 -0
  368. package/lib/swipl/library/ext/nlp/isub.pl +162 -0
  369. package/lib/swipl/library/ext/nlp/porter_stem.pl +50 -0
  370. package/lib/swipl/library/ext/nlp/snowball.pl +97 -0
  371. package/lib/swipl/library/ext/pcre/INDEX.pl +17 -0
  372. package/lib/swipl/library/ext/pcre/pcre.pl +974 -0
  373. package/lib/swipl/library/ext/plunit/INDEX.pl +20 -0
  374. package/lib/swipl/library/ext/plunit/plunit.pl +2631 -0
  375. package/lib/swipl/library/ext/plunit/test_wizard.pl +249 -0
  376. package/lib/swipl/library/ext/semweb/semweb/INDEX.pl +288 -0
  377. package/lib/swipl/library/ext/semweb/semweb/rdf11.pl +2106 -0
  378. package/lib/swipl/library/ext/semweb/semweb/rdf11_containers.pl +299 -0
  379. package/lib/swipl/library/ext/semweb/semweb/rdf_cache.pl +278 -0
  380. package/lib/swipl/library/ext/semweb/semweb/rdf_compare.pl +132 -0
  381. package/lib/swipl/library/ext/semweb/semweb/rdf_db.pl +3468 -0
  382. package/lib/swipl/library/ext/semweb/semweb/rdf_edit.pl +1145 -0
  383. package/lib/swipl/library/ext/semweb/semweb/rdf_http_plugin.pl +274 -0
  384. package/lib/swipl/library/ext/semweb/semweb/rdf_library.pl +1021 -0
  385. package/lib/swipl/library/ext/semweb/semweb/rdf_litindex.pl +1035 -0
  386. package/lib/swipl/library/ext/semweb/semweb/rdf_ntriples.pl +422 -0
  387. package/lib/swipl/library/ext/semweb/semweb/rdf_persistency.pl +1576 -0
  388. package/lib/swipl/library/ext/semweb/semweb/rdf_portray.pl +182 -0
  389. package/lib/swipl/library/ext/semweb/semweb/rdf_prefixes.pl +681 -0
  390. package/lib/swipl/library/ext/semweb/semweb/rdf_sandbox.pl +153 -0
  391. package/lib/swipl/library/ext/semweb/semweb/rdf_turtle.pl +54 -0
  392. package/lib/swipl/library/ext/semweb/semweb/rdf_turtle_write.pl +1587 -0
  393. package/lib/swipl/library/ext/semweb/semweb/rdf_zlib_plugin.pl +52 -0
  394. package/lib/swipl/library/ext/semweb/semweb/rdfa.pl +1510 -0
  395. package/lib/swipl/library/ext/semweb/semweb/rdfs.pl +475 -0
  396. package/lib/swipl/library/ext/semweb/semweb/sparql_client.pl +643 -0
  397. package/lib/swipl/library/ext/semweb/semweb/turtle.pl +455 -0
  398. package/lib/swipl/library/ext/sgml/DTD/HTML4.dcl +88 -0
  399. package/lib/swipl/library/ext/sgml/DTD/HTML4.dtd +1092 -0
  400. package/lib/swipl/library/ext/sgml/DTD/HTML4.soc +6 -0
  401. package/lib/swipl/library/ext/sgml/DTD/HTML5.dtd +906 -0
  402. package/lib/swipl/library/ext/sgml/DTD/HTMLlat1.ent +195 -0
  403. package/lib/swipl/library/ext/sgml/DTD/HTMLspec.ent +77 -0
  404. package/lib/swipl/library/ext/sgml/DTD/HTMLsym.ent +241 -0
  405. package/lib/swipl/library/ext/sgml/INDEX.pl +64 -0
  406. package/lib/swipl/library/ext/sgml/c14n2.pl +203 -0
  407. package/lib/swipl/library/ext/sgml/iso_639.pl +631 -0
  408. package/lib/swipl/library/ext/sgml/pwp.pl +705 -0
  409. package/lib/swipl/library/ext/sgml/sgml.pl +815 -0
  410. package/lib/swipl/library/ext/sgml/sgml_write.pl +1003 -0
  411. package/lib/swipl/library/ext/sgml/xpath.pl +620 -0
  412. package/lib/swipl/library/ext/sgml/xsdp_types.pl +232 -0
  413. package/lib/swipl/library/ext/ssl/INDEX.pl +60 -0
  414. package/lib/swipl/library/ext/ssl/crypto.pl +1015 -0
  415. package/lib/swipl/library/ext/ssl/http/INDEX.pl +5 -0
  416. package/lib/swipl/library/ext/ssl/http/http_ssl_plugin.pl +296 -0
  417. package/lib/swipl/library/ext/ssl/saml.pl +609 -0
  418. package/lib/swipl/library/ext/ssl/ssl.pl +647 -0
  419. package/lib/swipl/library/ext/ssl/xmldsig.pl +391 -0
  420. package/lib/swipl/library/ext/ssl/xmlenc.pl +354 -0
  421. package/lib/swipl/library/ext/zlib/INDEX.pl +8 -0
  422. package/lib/swipl/library/ext/zlib/zlib.pl +112 -0
  423. package/lib/swipl/library/fastrw.pl +105 -0
  424. package/lib/swipl/library/files.pl +80 -0
  425. package/lib/swipl/library/gensym.pl +111 -0
  426. package/lib/swipl/library/git.pl +1027 -0
  427. package/lib/swipl/library/hashtable.pl +365 -0
  428. package/lib/swipl/library/heaps.pl +252 -0
  429. package/lib/swipl/library/hotfix.pl +239 -0
  430. package/lib/swipl/library/http/jquery.pl +103 -0
  431. package/lib/swipl/library/http/web/js/jquery-3.6.0.min.js +2 -0
  432. package/lib/swipl/library/increval.pl +217 -0
  433. package/lib/swipl/library/intercept.pl +263 -0
  434. package/lib/swipl/library/iostream.pl +237 -0
  435. package/lib/swipl/library/iri_scheme/file.pl +70 -0
  436. package/lib/swipl/library/lazy_lists.pl +481 -0
  437. package/lib/swipl/library/listing.pl +1362 -0
  438. package/lib/swipl/library/lists.pl +860 -0
  439. package/lib/swipl/library/lynx/INDEX.pl +14 -0
  440. package/lib/swipl/library/lynx/format.pl +380 -0
  441. package/lib/swipl/library/lynx/html_style.pl +129 -0
  442. package/lib/swipl/library/lynx/html_text.pl +865 -0
  443. package/lib/swipl/library/lynx/pldoc_style.pl +87 -0
  444. package/lib/swipl/library/macros.pl +451 -0
  445. package/lib/swipl/library/main.pl +1200 -0
  446. package/lib/swipl/library/make.pl +197 -0
  447. package/lib/swipl/library/modules.pl +129 -0
  448. package/lib/swipl/library/nb_rbtrees.pl +267 -0
  449. package/lib/swipl/library/nb_set.pl +203 -0
  450. package/lib/swipl/library/obfuscate.pl +115 -0
  451. package/lib/swipl/library/occurs.pl +241 -0
  452. package/lib/swipl/library/operators.pl +182 -0
  453. package/lib/swipl/library/option.pl +388 -0
  454. package/lib/swipl/library/optparse.pl +966 -0
  455. package/lib/swipl/library/ordsets.pl +518 -0
  456. package/lib/swipl/library/oset.pl +160 -0
  457. package/lib/swipl/library/pairs.pl +187 -0
  458. package/lib/swipl/library/persistency.pl +713 -0
  459. package/lib/swipl/library/pio.pl +47 -0
  460. package/lib/swipl/library/portray_text.pl +277 -0
  461. package/lib/swipl/library/pprint.pl +940 -0
  462. package/lib/swipl/library/predicate_options.pl +975 -0
  463. package/lib/swipl/library/prolog_autoload.pl +233 -0
  464. package/lib/swipl/library/prolog_breakpoints.pl +396 -0
  465. package/lib/swipl/library/prolog_clause.pl +1104 -0
  466. package/lib/swipl/library/prolog_code.pl +416 -0
  467. package/lib/swipl/library/prolog_codewalk.pl +1248 -0
  468. package/lib/swipl/library/prolog_colour.pl +3301 -0
  469. package/lib/swipl/library/prolog_colour.qlf +0 -0
  470. package/lib/swipl/library/prolog_config.pl +154 -0
  471. package/lib/swipl/library/prolog_coverage.pl +1333 -0
  472. package/lib/swipl/library/prolog_debug.pl +404 -0
  473. package/lib/swipl/library/prolog_deps.pl +675 -0
  474. package/lib/swipl/library/prolog_evaluable.pl +234 -0
  475. package/lib/swipl/library/prolog_format.pl +219 -0
  476. package/lib/swipl/library/prolog_history.pl +161 -0
  477. package/lib/swipl/library/prolog_jiti.pl +333 -0
  478. package/lib/swipl/library/prolog_locale.pl +61 -0
  479. package/lib/swipl/library/prolog_metainference.pl +307 -0
  480. package/lib/swipl/library/prolog_pack.pl +3906 -0
  481. package/lib/swipl/library/prolog_profile.pl +346 -0
  482. package/lib/swipl/library/prolog_qlfmake.pl +533 -0
  483. package/lib/swipl/library/prolog_source.pl +1188 -0
  484. package/lib/swipl/library/prolog_stack.pl +794 -0
  485. package/lib/swipl/library/prolog_trace.pl +365 -0
  486. package/lib/swipl/library/prolog_versions.pl +347 -0
  487. package/lib/swipl/library/prolog_wrap.pl +164 -0
  488. package/lib/swipl/library/prolog_xref.pl +3090 -0
  489. package/lib/swipl/library/prolog_xref.qlf +0 -0
  490. package/lib/swipl/library/pure_input.pl +266 -0
  491. package/lib/swipl/library/qpforeign.pl +695 -0
  492. package/lib/swipl/library/qsave.pl +1516 -0
  493. package/lib/swipl/library/quasi_quotations.pl +300 -0
  494. package/lib/swipl/library/quintus.pl +437 -0
  495. package/lib/swipl/library/random.pl +459 -0
  496. package/lib/swipl/library/rbtrees.pl +1153 -0
  497. package/lib/swipl/library/readln.pl +256 -0
  498. package/lib/swipl/library/readutil.pl +304 -0
  499. package/lib/swipl/library/record.pl +544 -0
  500. package/lib/swipl/library/rwlocks.pl +172 -0
  501. package/lib/swipl/library/sandbox.pl +1384 -0
  502. package/lib/swipl/library/semweb/dc.rdfs +198 -0
  503. package/lib/swipl/library/semweb/eor.rdfs +95 -0
  504. package/lib/swipl/library/semweb/owl.owl +287 -0
  505. package/lib/swipl/library/semweb/rdf_library.ttl +90 -0
  506. package/lib/swipl/library/semweb/rdfs.rdfs +251 -0
  507. package/lib/swipl/library/settings.pl +755 -0
  508. package/lib/swipl/library/shell.pl +462 -0
  509. package/lib/swipl/library/shlib.pl +670 -0
  510. package/lib/swipl/library/solution_sequences.pl +439 -0
  511. package/lib/swipl/library/sort.pl +119 -0
  512. package/lib/swipl/library/statistics.pl +541 -0
  513. package/lib/swipl/library/streams.pl +118 -0
  514. package/lib/swipl/library/strings.pl +469 -0
  515. package/lib/swipl/library/system.pl +95 -0
  516. package/lib/swipl/library/tables.pl +381 -0
  517. package/lib/swipl/library/tableutil.pl +771 -0
  518. package/lib/swipl/library/tabling.pl +42 -0
  519. package/lib/swipl/library/terms.pl +457 -0
  520. package/lib/swipl/library/theme/auto.pl +61 -0
  521. package/lib/swipl/library/theme/dark.pl +455 -0
  522. package/lib/swipl/library/theme/light.pl +45 -0
  523. package/lib/swipl/library/thread.pl +810 -0
  524. package/lib/swipl/library/thread_pool.pl +508 -0
  525. package/lib/swipl/library/threadutil.pl +574 -0
  526. package/lib/swipl/library/tty.pl +316 -0
  527. package/lib/swipl/library/ugraphs.pl +644 -0
  528. package/lib/swipl/library/unicode/INDEX.pl +6 -0
  529. package/lib/swipl/library/unicode/blocks.pl +414 -0
  530. package/lib/swipl/library/url.pl +1129 -0
  531. package/lib/swipl/library/utf8.pl +145 -0
  532. package/lib/swipl/library/varnumbers.pl +221 -0
  533. package/lib/swipl/library/vm.pl +251 -0
  534. package/lib/swipl/library/wfs.pl +205 -0
  535. package/lib/swipl/library/when.pl +263 -0
  536. package/lib/swipl/library/writef.pl +278 -0
  537. package/lib/swipl/library/www_browser.pl +251 -0
  538. package/lib/swipl/library/yall.pl +574 -0
  539. package/lib/swipl/library/zip.pl +211 -0
  540. package/lib/swipl/swipl.home +1 -0
  541. package/lib/vendor/libcrypto.so.3 +0 -0
  542. package/lib/vendor/libpcre2-8.so.0 +0 -0
  543. package/lib/vendor/libssl.so.3 +0 -0
  544. package/lib/vendor/libz.so.1 +0 -0
  545. package/licenses/OpenSSL-LICENSE.txt +177 -0
  546. package/licenses/PCRE2-COPYING +5 -0
  547. package/licenses/README.md +12 -0
  548. package/licenses/SWI-Prolog-LICENSE +41 -0
  549. package/licenses/zlib-LICENSE +22 -0
  550. package/package.json +27 -0
@@ -0,0 +1,3906 @@
1
+ /* Part of SWI-Prolog
2
+
3
+ Author: Jan Wielemaker
4
+ E-mail: jan@swi-prolog.org
5
+ WWW: https://www.swi-prolog.org
6
+ Copyright (c) 2012-2024, VU University Amsterdam
7
+ CWI, Amsterdam
8
+ SWI-Prolog Solutions b.v.
9
+ All rights reserved.
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions
13
+ are met:
14
+
15
+ 1. Redistributions of source code must retain the above copyright
16
+ notice, this list of conditions and the following disclaimer.
17
+
18
+ 2. Redistributions in binary form must reproduce the above copyright
19
+ notice, this list of conditions and the following disclaimer in
20
+ the documentation and/or other materials provided with the
21
+ distribution.
22
+
23
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27
+ COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33
+ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
+ POSSIBILITY OF SUCH DAMAGE.
35
+ */
36
+
37
+ :- module(prolog_pack,
38
+ [ pack_list_installed/0,
39
+ pack_info/1, % +Name
40
+ pack_list/1, % +Keyword
41
+ pack_list/2, % +Query, +Options
42
+ pack_search/1, % +Keyword
43
+ pack_install/1, % +Name
44
+ pack_install/2, % +Name, +Options
45
+ pack_install_local/3, % :Spec, +Dir, +Options
46
+ pack_upgrade/1, % +Name
47
+ pack_rebuild/1, % +Name
48
+ pack_rebuild/0, % All packages
49
+ pack_remove/1, % +Name
50
+ pack_remove/2, % +Name, +Options
51
+ pack_publish/2, % +URL, +Options
52
+ pack_property/2 % ?Name, ?Property
53
+ ]).
54
+ :- use_module(library(apply)).
55
+ :- use_module(library(error)).
56
+ :- use_module(library(option)).
57
+ :- use_module(library(readutil)).
58
+ :- use_module(library(lists)).
59
+ :- use_module(library(filesex)).
60
+ :- use_module(library(xpath)).
61
+ :- use_module(library(settings)).
62
+ :- use_module(library(uri)).
63
+ :- use_module(library(dcg/basics)).
64
+ :- use_module(library(dcg/high_order)).
65
+ :- use_module(library(http/http_open)).
66
+ :- use_module(library(json)).
67
+ :- use_module(library(http/http_client), []).
68
+ :- use_module(library(debug), [assertion/1]).
69
+ :- use_module(library(pairs),
70
+ [pairs_keys/2, map_list_to_pairs/3, pairs_values/2]).
71
+ :- autoload(library(git)).
72
+ :- autoload(library(sgml)).
73
+ :- autoload(library(sha)).
74
+ :- autoload(library(build/tools)).
75
+ :- autoload(library(ansi_term), [ansi_format/3]).
76
+ :- autoload(library(pprint), [print_term/2]).
77
+ :- autoload(library(prolog_versions), [require_version/3, cmp_versions/3]).
78
+ :- autoload(library(ugraphs), [vertices_edges_to_ugraph/3, ugraph_layers/2]).
79
+ :- autoload(library(process), [process_which/2]).
80
+ :- autoload(library(aggregate), [aggregate_all/3]).
81
+
82
+ :- meta_predicate
83
+ pack_install_local(2, +, +).
84
+
85
+ /** <module> A package manager for Prolog
86
+
87
+ The library(prolog_pack) provides the SWI-Prolog package manager. This
88
+ library lets you inspect installed packages, install packages, remove
89
+ packages, etc. This library complemented by the built-in predicates such
90
+ as attach_packs/2 that makes installed packages available as libraries.
91
+
92
+ The important functionality of this library is encapsulated in the _app_
93
+ `pack`. For help, run
94
+
95
+ swipl pack help
96
+ */
97
+
98
+ /*******************************
99
+ * CONSTANTS *
100
+ *******************************/
101
+
102
+ :- setting(server, atom, 'https://www.swi-prolog.org/pack/',
103
+ 'Server to exchange pack information').
104
+
105
+
106
+ /*******************************
107
+ * LOCAL DECLARATIONS *
108
+ *******************************/
109
+
110
+ :- op(900, xfx, @). % Token@Version
111
+
112
+ :- meta_predicate det_if(0,0).
113
+
114
+ /*******************************
115
+ * PACKAGE INFO *
116
+ *******************************/
117
+
118
+ %! current_pack(?Pack) is nondet.
119
+ %! current_pack(?Pack, ?Dir) is nondet.
120
+ %
121
+ % True if Pack is a currently installed pack.
122
+
123
+ current_pack(Pack) :-
124
+ current_pack(Pack, _).
125
+
126
+ current_pack(Pack, Dir) :-
127
+ '$pack':pack(Pack, Dir).
128
+
129
+ %! pack_list_installed is det.
130
+ %
131
+ % List currently installed packages and report possible dependency
132
+ % issues.
133
+
134
+ pack_list_installed :-
135
+ pack_list('', [installed(true)]),
136
+ validate_dependencies.
137
+
138
+ %! pack_info(+Pack)
139
+ %
140
+ % Print more detailed information about Pack.
141
+
142
+ pack_info(Name) :-
143
+ pack_info(info, Name).
144
+
145
+ pack_info(Level, Name) :-
146
+ must_be(atom, Name),
147
+ findall(Info, pack_info(Name, Level, Info), Infos0),
148
+ ( Infos0 == []
149
+ -> print_message(warning, pack(no_pack_installed(Name))),
150
+ fail
151
+ ; true
152
+ ),
153
+ findall(Def, pack_default(Level, Infos, Def), Defs),
154
+ append(Infos0, Defs, Infos1),
155
+ sort(Infos1, Infos),
156
+ show_info(Name, Infos, [info(Level)]).
157
+
158
+
159
+ show_info(_Name, _Properties, Options) :-
160
+ option(silent(true), Options),
161
+ !.
162
+ show_info(_Name, _Properties, Options) :-
163
+ option(show_info(false), Options),
164
+ !.
165
+ show_info(Name, Properties, Options) :-
166
+ option(info(list), Options),
167
+ !,
168
+ memberchk(title(Title), Properties),
169
+ memberchk(version(Version), Properties),
170
+ format('i ~w@~w ~28|- ~w~n', [Name, Version, Title]).
171
+ show_info(Name, Properties, _) :-
172
+ !,
173
+ print_property_value('Package'-'~w', [Name]),
174
+ findall(Term, pack_level_info(info, Term, _, _), Terms),
175
+ maplist(print_property(Properties), Terms).
176
+
177
+ print_property(_, nl) :-
178
+ !,
179
+ format('~n').
180
+ print_property(Properties, Term) :-
181
+ findall(Term, member(Term, Properties), Terms),
182
+ Terms \== [],
183
+ !,
184
+ pack_level_info(_, Term, LabelFmt, _Def),
185
+ ( LabelFmt = Label-FmtElem
186
+ -> true
187
+ ; Label = LabelFmt,
188
+ FmtElem = '~w'
189
+ ),
190
+ multi_valued(Terms, FmtElem, FmtList, Values),
191
+ atomic_list_concat(FmtList, ', ', Fmt),
192
+ print_property_value(Label-Fmt, Values).
193
+ print_property(_, _).
194
+
195
+ multi_valued([H], LabelFmt, [LabelFmt], Values) :-
196
+ !,
197
+ H =.. [_|Values].
198
+ multi_valued([H|T], LabelFmt, [LabelFmt|LT], Values) :-
199
+ H =.. [_|VH],
200
+ append(VH, MoreValues, Values),
201
+ multi_valued(T, LabelFmt, LT, MoreValues).
202
+
203
+
204
+ pvalue_column(31).
205
+ print_property_value(Prop-Fmt, Values) :-
206
+ !,
207
+ pvalue_column(C),
208
+ ansi_format(comment, '% ~w:~t~*|', [Prop, C]),
209
+ ansi_format(code, Fmt, Values),
210
+ ansi_format([], '~n', []).
211
+
212
+ pack_info(Name, Level, Info) :-
213
+ '$pack':pack(Name, BaseDir),
214
+ pack_dir_info(BaseDir, Level, Info).
215
+
216
+ pack_dir_info(BaseDir, Level, Info) :-
217
+ ( Info = directory(BaseDir)
218
+ ; pack_info_term(BaseDir, Info)
219
+ ),
220
+ pack_level_info(Level, Info, _Format, _Default).
221
+
222
+ :- public pack_level_info/4. % used by web-server
223
+
224
+ pack_level_info(_, title(_), 'Title', '<no title>').
225
+ pack_level_info(_, version(_), 'Installed version', '<unknown>').
226
+ pack_level_info(info, automatic(_), 'Automatic (dependency only)', -).
227
+ pack_level_info(info, directory(_), 'Installed in directory', -).
228
+ pack_level_info(info, link(_), 'Installed as link to'-'~w', -).
229
+ pack_level_info(info, built(_,_), 'Built on'-'~w for SWI-Prolog ~w', -).
230
+ pack_level_info(info, author(_, _), 'Author'-'~w <~w>', -).
231
+ pack_level_info(info, maintainer(_, _), 'Maintainer'-'~w <~w>', -).
232
+ pack_level_info(info, packager(_, _), 'Packager'-'~w <~w>', -).
233
+ pack_level_info(info, home(_), 'Home page', -).
234
+ pack_level_info(info, download(_), 'Download URL', -).
235
+ pack_level_info(_, provides(_), 'Provides', -).
236
+ pack_level_info(_, requires(_), 'Requires', -).
237
+ pack_level_info(_, conflicts(_), 'Conflicts with', -).
238
+ pack_level_info(_, replaces(_), 'Replaces packages', -).
239
+ pack_level_info(info, library(_), 'Provided libraries', -).
240
+ pack_level_info(info, autoload(_), 'Autoload', -).
241
+
242
+ pack_default(Level, Infos, Def) :-
243
+ pack_level_info(Level, ITerm, _Format, Def),
244
+ Def \== (-),
245
+ \+ memberchk(ITerm, Infos).
246
+
247
+ %! pack_info_term(+PackDir, ?Info) is nondet.
248
+ %
249
+ % True when Info is meta-data for the package PackName.
250
+
251
+ pack_info_term(BaseDir, Info) :-
252
+ directory_file_path(BaseDir, 'pack.pl', InfoFile),
253
+ catch(
254
+ term_in_file(valid_term(pack_info_term), InfoFile, Info),
255
+ error(existence_error(source_sink, InfoFile), _),
256
+ ( print_message(error, pack(no_meta_data(BaseDir))),
257
+ fail
258
+ )).
259
+ pack_info_term(BaseDir, library(Lib)) :-
260
+ atom_concat(BaseDir, '/prolog/', LibDir),
261
+ atom_concat(LibDir, '*.pl', Pattern),
262
+ expand_file_name(Pattern, Files),
263
+ maplist(atom_concat(LibDir), Plain, Files),
264
+ convlist(base_name, Plain, Libs),
265
+ member(Lib, Libs),
266
+ Lib \== 'INDEX'.
267
+ pack_info_term(BaseDir, autoload(true)) :-
268
+ atom_concat(BaseDir, '/prolog/INDEX.pl', IndexFile),
269
+ exists_file(IndexFile).
270
+ pack_info_term(BaseDir, automatic(Boolean)) :-
271
+ once(pack_status_dir(BaseDir, automatic(Boolean))).
272
+ pack_info_term(BaseDir, built(Arch, Prolog)) :-
273
+ pack_status_dir(BaseDir, built(Arch, Prolog, _How)).
274
+ pack_info_term(BaseDir, link(Dest)) :-
275
+ read_link(BaseDir, _, Dest).
276
+
277
+ base_name(File, Base) :-
278
+ file_name_extension(Base, pl, File).
279
+
280
+ %! term_in_file(:Valid, +File, -Term) is nondet.
281
+ %
282
+ % True when Term appears in file and call(Valid, Term) is true.
283
+
284
+ :- meta_predicate
285
+ term_in_file(1, +, -).
286
+
287
+ term_in_file(Valid, File, Term) :-
288
+ exists_file(File),
289
+ setup_call_cleanup(
290
+ open(File, read, In, [encoding(utf8)]),
291
+ term_in_stream(Valid, In, Term),
292
+ close(In)).
293
+
294
+ term_in_stream(Valid, In, Term) :-
295
+ repeat,
296
+ read_term(In, Term0, []),
297
+ ( Term0 == end_of_file
298
+ -> !, fail
299
+ ; Term = Term0,
300
+ call(Valid, Term0)
301
+ ).
302
+
303
+ :- meta_predicate
304
+ valid_term(1,+).
305
+
306
+ valid_term(Type, Term) :-
307
+ Term =.. [Name|Args],
308
+ same_length(Args, Types),
309
+ Decl =.. [Name|Types],
310
+ ( call(Type, Decl)
311
+ -> maplist(valid_info_arg, Types, Args)
312
+ ; print_message(warning, pack(invalid_term(Type, Term))),
313
+ fail
314
+ ).
315
+
316
+ valid_info_arg(Type, Arg) :-
317
+ must_be(Type, Arg).
318
+
319
+ %! pack_info_term(?Term) is nondet.
320
+ %
321
+ % True when Term describes name and arguments of a valid package
322
+ % info term.
323
+
324
+ pack_info_term(name(atom)). % Synopsis
325
+ pack_info_term(title(atom)).
326
+ pack_info_term(keywords(list(atom))).
327
+ pack_info_term(description(list(atom))).
328
+ pack_info_term(version(version)).
329
+ pack_info_term(author(atom, email_or_url_or_empty)). % Persons
330
+ pack_info_term(maintainer(atom, email_or_url)).
331
+ pack_info_term(packager(atom, email_or_url)).
332
+ pack_info_term(pack_version(nonneg)). % Package convention version
333
+ pack_info_term(home(atom)). % Home page
334
+ pack_info_term(download(atom)). % Source
335
+ pack_info_term(provides(atom)). % Dependencies
336
+ pack_info_term(requires(dependency)).
337
+ pack_info_term(conflicts(dependency)). % Conflicts with package
338
+ pack_info_term(replaces(atom)). % Replaces another package
339
+ pack_info_term(autoload(boolean)). % Default installation options
340
+
341
+ :- multifile
342
+ error:has_type/2.
343
+
344
+ error:has_type(version, Version) :-
345
+ atom(Version),
346
+ is_version(Version).
347
+ error:has_type(email_or_url, Address) :-
348
+ atom(Address),
349
+ ( sub_atom(Address, _, _, _, @)
350
+ -> true
351
+ ; uri_is_global(Address)
352
+ ).
353
+ error:has_type(email_or_url_or_empty, Address) :-
354
+ ( Address == ''
355
+ -> true
356
+ ; error:has_type(email_or_url, Address)
357
+ ).
358
+ error:has_type(dependency, Value) :-
359
+ is_dependency(Value).
360
+
361
+ is_version(Version) :-
362
+ split_string(Version, ".", "", Parts),
363
+ maplist(number_string, _, Parts).
364
+
365
+ is_dependency(Var) :-
366
+ var(Var),
367
+ !,
368
+ fail.
369
+ is_dependency(Token) :-
370
+ atom(Token),
371
+ !.
372
+ is_dependency(Term) :-
373
+ compound(Term),
374
+ compound_name_arguments(Term, Op, [Token,Version]),
375
+ atom(Token),
376
+ cmp(Op, _),
377
+ is_version(Version),
378
+ !.
379
+ is_dependency(PrologToken) :-
380
+ is_prolog_token(PrologToken).
381
+
382
+ cmp(<, @<).
383
+ cmp(=<, @=<).
384
+ cmp(==, ==).
385
+ cmp(>=, @>=).
386
+ cmp(>, @>).
387
+
388
+
389
+ /*******************************
390
+ * SEARCH *
391
+ *******************************/
392
+
393
+ %! pack_list(+Query) is det.
394
+ %! pack_list(+Query, +Options) is det.
395
+ %! pack_search(+Query) is det.
396
+ %
397
+ % Query package server and installed packages and display results.
398
+ % Query is matches case-insensitively against the name and title of
399
+ % known and installed packages. For each matching package, a single
400
+ % line is displayed that provides:
401
+ %
402
+ % - Installation status
403
+ % - __p__: package, not installed
404
+ % - __i__: installed package; up-to-date with public version
405
+ % - __a__: as __i__, but installed only as dependency
406
+ % - __U__: installed package; can be upgraded
407
+ % - __A__: installed package; newer than publically available
408
+ % - __l__: installed package; not on server
409
+ % - Name@Version
410
+ % - Name@Version(ServerVersion)
411
+ % - Title
412
+ %
413
+ % Options processed:
414
+ %
415
+ % - installed(true)
416
+ % Only list packages that are locally installed. Contacts the
417
+ % server to compare our local version to the latest available
418
+ % version.
419
+ % - outdated(true)
420
+ % Only list packages that need to be updated. This option
421
+ % implies installed(true).
422
+ % - server(Server|false)
423
+ % If `false`, do not contact the server. This implies
424
+ % installed(true). Otherwise, use the given pack server.
425
+ %
426
+ % Hint: ``?- pack_list('').`` lists all known packages.
427
+ %
428
+ % The predicates pack_list/1 and pack_search/1 are synonyms. Both
429
+ % contact the package server at https://www.swi-prolog.org to find
430
+ % available packages. Contacting the server can be avoided using the
431
+ % server(false) option.
432
+
433
+ pack_list(Query) :-
434
+ pack_list(Query, []).
435
+
436
+ pack_search(Query) :-
437
+ pack_list(Query, []).
438
+
439
+ pack_list(Query, Options) :-
440
+ ( option(installed(true), Options)
441
+ ; option(outdated(true), Options)
442
+ ; option(server(false), Options)
443
+ ),
444
+ !,
445
+ local_search(Query, Local),
446
+ maplist(arg(1), Local, Packs),
447
+ ( option(server(false), Options)
448
+ -> Hits = []
449
+ ; query_pack_server(info(Packs), true(Hits), Options)
450
+ ),
451
+ list_hits(Hits, Local, Options).
452
+ pack_list(Query, Options) :-
453
+ query_pack_server(search(Query), Result, Options),
454
+ ( Result == false
455
+ -> ( local_search(Query, Packs),
456
+ Packs \== []
457
+ -> forall(member(pack(Pack, Stat, Title, Version, _), Packs),
458
+ format('~w ~w@~w ~28|- ~w~n',
459
+ [Stat, Pack, Version, Title]))
460
+ ; print_message(warning, pack(search_no_matches(Query)))
461
+ )
462
+ ; Result = true(Hits), % Hits = list(pack(Name, p, Title, Version, URL))
463
+ local_search(Query, Local),
464
+ list_hits(Hits, Local, [])
465
+ ).
466
+
467
+ list_hits(Hits, Local, Options) :-
468
+ append(Hits, Local, All),
469
+ sort(All, Sorted),
470
+ join_status(Sorted, Packs0),
471
+ include(filtered(Options), Packs0, Packs),
472
+ maplist(list_hit(Options), Packs).
473
+
474
+ filtered(Options, pack(_,Tag,_,_,_)) :-
475
+ option(outdated(true), Options),
476
+ !,
477
+ Tag == 'U'.
478
+ filtered(_, _).
479
+
480
+ list_hit(_Options, pack(Pack, Tag, Title, Version, _URL)) =>
481
+ list_tag(Tag),
482
+ ansi_format(code, '~w', [Pack]),
483
+ format('@'),
484
+ list_version(Tag, Version),
485
+ format('~35|- ', []),
486
+ ansi_format(comment, '~w~n', [Title]).
487
+
488
+ list_tag(Tag) :-
489
+ tag_color(Tag, Color),
490
+ ansi_format(Color, '~w ', [Tag]).
491
+
492
+ list_version(Tag, VersionI-VersionS) =>
493
+ tag_color(Tag, Color),
494
+ ansi_format(Color, '~w', [VersionI]),
495
+ ansi_format(bold, '(~w)', [VersionS]).
496
+ list_version(_Tag, Version) =>
497
+ ansi_format([], '~w', [Version]).
498
+
499
+ tag_color('U', warning) :- !.
500
+ tag_color('A', comment) :- !.
501
+ tag_color(_, []).
502
+
503
+ %! join_status(+PacksIn, -PacksOut) is det.
504
+ %
505
+ % Combine local and remote information to assess the status of each
506
+ % package. PacksOut is a list of pack(Name, Status, Version, URL). If
507
+ % the versions do not match, `Version` is
508
+ % `VersionInstalled-VersionRemote` and similar for thee URL.
509
+
510
+ join_status([], []).
511
+ join_status([ pack(Pack, i, Title, Version, URL),
512
+ pack(Pack, p, Title, Version, _)
513
+ | T0
514
+ ],
515
+ [ pack(Pack, Tag, Title, Version, URL)
516
+ | T
517
+ ]) :-
518
+ !,
519
+ ( pack_status(Pack, automatic(true))
520
+ -> Tag = a
521
+ ; Tag = i
522
+ ),
523
+ join_status(T0, T).
524
+ join_status([ pack(Pack, i, Title, VersionI, URLI),
525
+ pack(Pack, p, _, VersionS, URLS)
526
+ | T0
527
+ ],
528
+ [ pack(Pack, Tag, Title, VersionI-VersionS, URLI-URLS)
529
+ | T
530
+ ]) :-
531
+ !,
532
+ version_sort_key(VersionI, VDI),
533
+ version_sort_key(VersionS, VDS),
534
+ ( VDI @< VDS
535
+ -> Tag = 'U'
536
+ ; Tag = 'A'
537
+ ),
538
+ join_status(T0, T).
539
+ join_status([ pack(Pack, i, Title, VersionI, URL)
540
+ | T0
541
+ ],
542
+ [ pack(Pack, l, Title, VersionI, URL)
543
+ | T
544
+ ]) :-
545
+ !,
546
+ join_status(T0, T).
547
+ join_status([H|T0], [H|T]) :-
548
+ join_status(T0, T).
549
+
550
+ %! local_search(+Query, -Packs:list(atom)) is det.
551
+ %
552
+ % Search locally installed packs.
553
+
554
+ local_search(Query, Packs) :-
555
+ findall(Pack, matching_installed_pack(Query, Pack), Packs).
556
+
557
+ matching_installed_pack(Query, pack(Pack, i, Title, Version, URL)) :-
558
+ current_pack(Pack),
559
+ findall(Term,
560
+ ( pack_info(Pack, _, Term),
561
+ search_info(Term)
562
+ ), Info),
563
+ ( sub_atom_icasechk(Pack, _, Query)
564
+ -> true
565
+ ; memberchk(title(Title), Info),
566
+ sub_atom_icasechk(Title, _, Query)
567
+ ),
568
+ option(title(Title), Info, '<no title>'),
569
+ option(version(Version), Info, '<no version>'),
570
+ option(download(URL), Info, '<no download url>').
571
+
572
+ search_info(title(_)).
573
+ search_info(version(_)).
574
+ search_info(download(_)).
575
+
576
+
577
+ /*******************************
578
+ * INSTALL *
579
+ *******************************/
580
+
581
+ %! pack_install(+Spec:atom) is det.
582
+ %! pack_install(+SpecOrList, +Options) is det.
583
+ %
584
+ % Install one or more packs from SpecOrList. SpecOrList is a single
585
+ % specification or a list of specifications. A specification is one of
586
+ %
587
+ % * A pack name. This queries the pack repository
588
+ % at https://www.swi-prolog.org
589
+ % * Archive file name
590
+ % * A http(s) URL of an archive file name. This URL may contain a
591
+ % star (*) for the version. In this case pack_install/1 asks
592
+ % for the directory content and selects the latest version.
593
+ % * An https GIT URL
594
+ % * A local directory name given as ``file://`` URL
595
+ % * `'.'`, in which case a relative symlink is created to the
596
+ % current directory (all other options for Spec make a copy
597
+ % of the files). Installation using a symlink is normally
598
+ % used during development of a pack.
599
+ %
600
+ % Processes the options below. Default options as would be used by
601
+ % pack_install/1 are used to complete the provided Options. Note that
602
+ % pack_install/2 can be used through the SWI-Prolog command line app
603
+ % `pack` as below. Most of the options of this predicate are available
604
+ % as command line options.
605
+ %
606
+ % swipl pack install <name>
607
+ %
608
+ % Options:
609
+ %
610
+ % * url(+URL)
611
+ % Source for downloading the package
612
+ % * pack_directory(+Dir)
613
+ % Directory into which to install the package.
614
+ % * global(+Boolean)
615
+ % If `true`, install in the XDG common application data path,
616
+ % making the pack accessible to everyone. If `false`, install in
617
+ % the XDG user application data path, making the pack accessible
618
+ % for the current user only. If the option is absent, use the
619
+ % first existing and writable directory. If that doesn't exist
620
+ % find locations where it can be created and prompt the user to do
621
+ % so.
622
+ % * insecure(+Boolean)
623
+ % When `true` (default `false`), do not perform any checks on SSL
624
+ % certificates when downloading using `https`.
625
+ % * interactive(+Boolean)
626
+ % Use default answer without asking the user if there
627
+ % is a default action.
628
+ % * silent(+Boolean)
629
+ % If `true` (default false), suppress informational progress
630
+ % messages.
631
+ % * upgrade(+Boolean)
632
+ % If `true` (default `false`), upgrade package if it is already
633
+ % installed.
634
+ % * rebuild(Condition)
635
+ % Rebuild the foreign components. Condition is one of
636
+ % `if_absent` (default, do nothing if the directory with foreign
637
+ % resources exists), `make` (run `make`) or `true` (run `make
638
+ % distclean` followed by the default configure and build steps).
639
+ % * test(Boolean)
640
+ % If `true` (default), run the pack tests.
641
+ % * git(+Boolean)
642
+ % If `true` (default `false` unless `URL` ends with ``.git``),
643
+ % assume the URL is a GIT repository.
644
+ % * link(+Boolean)
645
+ % Can be used if the installation source is a local directory
646
+ % and the file system supports symbolic links. In this case
647
+ % the system adds the current directory to the pack registration
648
+ % using a symbolic link and performs the local installation steps.
649
+ % * version(+Version)
650
+ % Demand the pack to satisfy some version requirement. Version
651
+ % is as defined by require_version/3. For example `'1.5'` is the
652
+ % same as `>=('1.5')`.
653
+ % * branch(+Branch)
654
+ % When installing from a git repository, clone this branch.
655
+ % * commit(+Commit)
656
+ % When installing from a git repository, checkout this commit.
657
+ % Commit is either a hash, a tag, a branch or `'HEAD'`.
658
+ % * build_type(+Type)
659
+ % When building using CMake, use ``-DCMAKE_BUILD_TYPE=Type``.
660
+ % Default is the build type of Prolog or ``Release``.
661
+ % * register(+Boolean)
662
+ % If `true` (default), register packages as downloaded after
663
+ % performing the download. This contacts the server with the
664
+ % meta-data of each pack that was downloaded. The server will
665
+ % either register the location as a new version or increment
666
+ % the download count. The server stores the IP address of the
667
+ % client. Subsequent downloads of the same version from the
668
+ % same IP address are ignored.
669
+ % * server(+URL)
670
+ % Pack server to contact. Default is the setting
671
+ % `prolog_pack:server`, by default set to
672
+ % ``https://www.swi-prolog.org/pack/``
673
+ %
674
+ % Non-interactive installation can be established using the option
675
+ % interactive(false). It is adviced to install from a particular
676
+ % _trusted_ URL instead of the plain pack name for unattented
677
+ % operation.
678
+
679
+ pack_install(Spec) :-
680
+ pack_default_options(Spec, Pack, [], Options),
681
+ pack_install(Pack, [pack(Pack)|Options]).
682
+
683
+ pack_install(Specs, Options) :-
684
+ is_list(Specs),
685
+ !,
686
+ maplist(pack_options(Options), Specs, Pairs),
687
+ pack_install_dir(PackTopDir, Options),
688
+ pack_install_set(Pairs, PackTopDir, Options).
689
+ pack_install(Spec, Options) :-
690
+ pack_default_options(Spec, Pack, Options, DefOptions),
691
+ ( option(already_installed(Installed), DefOptions)
692
+ -> print_message(informational, pack(already_installed(Installed)))
693
+ ; merge_options(Options, DefOptions, PackOptions),
694
+ pack_install_dir(PackTopDir, PackOptions),
695
+ pack_install_set([Pack-PackOptions], PackTopDir, Options)
696
+ ).
697
+
698
+ pack_options(Options, Spec, Pack-PackOptions) :-
699
+ pack_default_options(Spec, Pack, Options, DefOptions),
700
+ merge_options(Options, DefOptions, PackOptions).
701
+
702
+ %! pack_default_options(+Spec, -Pack, +OptionsIn, -Options) is det.
703
+ %
704
+ % Establish the pack name (Pack) and install options from a
705
+ % specification and options (OptionsIn) provided by the user. Cases:
706
+ %
707
+ % 1. Already installed. We must pass that as pack_default_options/4
708
+ % is called twice from pack_install/2.
709
+ % 2. Install from a URL due to a url(URL) option. Determine whether
710
+ % the URL is a GIT repository, get the version and pack from the
711
+ % URL.
712
+ % 3. Install a local archive file. Extract the pack and version from
713
+ % the archive name.
714
+ % 4. Install from a git URL. Determines the pack, sets git(true)
715
+ % and adds the URL as option.
716
+ % 5. Install from a directory. Get the info from the `packs.pl`
717
+ % file.
718
+ % 6. Install from `'.'`. Create a symlink to make the current dir
719
+ % accessible as a pack.
720
+ % 7. Install from a non-git URL
721
+ % Determine pack and version.
722
+ % 8. Pack name. Query the server to find candidate packs and
723
+ % select an adequate pack.
724
+
725
+
726
+ pack_default_options(_Spec, Pack, OptsIn, Options) :- % (1)
727
+ option(already_installed(pack(Pack,_Version)), OptsIn),
728
+ !,
729
+ Options = OptsIn.
730
+ pack_default_options(_Spec, Pack, OptsIn, Options) :- % (2)
731
+ option(url(URL), OptsIn),
732
+ !,
733
+ ( option(git(_), OptsIn)
734
+ -> Options = OptsIn
735
+ ; git_url(URL, Pack)
736
+ -> Options = [git(true)|OptsIn]
737
+ ; Options = OptsIn
738
+ ),
739
+ ( nonvar(Pack)
740
+ -> true
741
+ ; option(pack(Pack), Options)
742
+ -> true
743
+ ; pack_version_file(Pack, _Version, URL)
744
+ ).
745
+ pack_default_options(Archive, Pack, OptsIn, Options) :- % (3)
746
+ must_be(atom, Archive),
747
+ \+ uri_is_global(Archive),
748
+ expand_file_name(Archive, [File]),
749
+ exists_file(File),
750
+ !,
751
+ ( pack_version_file(Pack, Version, File)
752
+ -> uri_file_name(FileURL, File),
753
+ merge_options([url(FileURL), version(Version)], OptsIn, Options)
754
+ ; domain_error(pack_file_name, Archive)
755
+ ).
756
+ pack_default_options(URL, Pack, OptsIn, Options) :- % (4)
757
+ git_url(URL, Pack),
758
+ !,
759
+ merge_options([git(true), url(URL)], OptsIn, Options).
760
+ pack_default_options(FileURL, Pack, _, Options) :- % (5)
761
+ uri_file_name(FileURL, Dir),
762
+ exists_directory(Dir),
763
+ pack_info_term(Dir, name(Pack)),
764
+ !,
765
+ ( pack_info_term(Dir, version(Version))
766
+ -> uri_file_name(DirURL, Dir),
767
+ Options = [url(DirURL), version(Version)]
768
+ ; throw(error(existence_error(key, version, Dir),_))
769
+ ).
770
+ pack_default_options('.', Pack, OptsIn, Options) :- % (6)
771
+ pack_info_term('.', name(Pack)),
772
+ !,
773
+ working_directory(Dir, Dir),
774
+ ( pack_info_term(Dir, version(Version))
775
+ -> uri_file_name(DirURL, Dir),
776
+ NewOptions = [url(DirURL), version(Version) | Options1],
777
+ ( current_prolog_flag(windows, true)
778
+ -> Options1 = []
779
+ ; Options1 = [link(true), rebuild(make)]
780
+ ),
781
+ merge_options(NewOptions, OptsIn, Options)
782
+ ; throw(error(existence_error(key, version, Dir),_))
783
+ ).
784
+ pack_default_options(URL, Pack, OptsIn, Options) :- % (7)
785
+ pack_version_file(Pack, Version, URL),
786
+ download_url(URL),
787
+ !,
788
+ available_download_versions(URL, Available, Options),
789
+ Available = [URLVersion-LatestURL|_],
790
+ NewOptions = [url(LatestURL)|VersionOptions],
791
+ version_options(Version, URLVersion, Available, VersionOptions),
792
+ merge_options(NewOptions, OptsIn, Options).
793
+ pack_default_options(Pack, Pack, Options, Options) :- % (8)
794
+ \+ uri_is_global(Pack).
795
+
796
+ version_options(Version, Version, _, [version(Version)]) :- !.
797
+ version_options(Version, _, Available, [versions(Available)]) :-
798
+ sub_atom(Version, _, _, _, *),
799
+ !.
800
+ version_options(_, _, _, []).
801
+
802
+ %! pack_install_dir(-PackDir, +Options) is det.
803
+ %
804
+ % Determine the directory below which to install new packs. This find
805
+ % or creates a writeable directory. Options:
806
+ %
807
+ % - pack_directory(+PackDir)
808
+ % Use PackDir. PackDir is created if it does not exist.
809
+ % - global(+Boolean)
810
+ % If `true`, find a writeable global directory based on the
811
+ % file search path `common_app_data`. If `false`, find a
812
+ % user-specific writeable directory based on `user_app_data`
813
+ % - If neither of the above is given, use the search path
814
+ % `pack`.
815
+ %
816
+ % If no writeable directory is found, generate possible location where
817
+ % this directory can be created and ask the user to create one of
818
+ % them.
819
+
820
+ pack_install_dir(PackDir, Options) :-
821
+ option(pack_directory(PackDir), Options),
822
+ ensure_directory(PackDir),
823
+ !.
824
+ pack_install_dir(PackDir, Options) :-
825
+ base_alias(Alias, Options),
826
+ absolute_file_name(Alias, PackDir,
827
+ [ file_type(directory),
828
+ access(write),
829
+ file_errors(fail)
830
+ ]),
831
+ !.
832
+ pack_install_dir(PackDir, Options) :-
833
+ pack_create_install_dir(PackDir, Options).
834
+
835
+ base_alias(Alias, Options) :-
836
+ option(global(true), Options),
837
+ !,
838
+ Alias = common_app_data(pack).
839
+ base_alias(Alias, Options) :-
840
+ option(global(false), Options),
841
+ !,
842
+ Alias = user_app_data(pack).
843
+ base_alias(Alias, _Options) :-
844
+ Alias = pack('.').
845
+
846
+ pack_create_install_dir(PackDir, Options) :-
847
+ base_alias(Alias, Options),
848
+ findall(Candidate = create_dir(Candidate),
849
+ ( absolute_file_name(Alias, Candidate, [solutions(all)]),
850
+ \+ exists_file(Candidate),
851
+ \+ exists_directory(Candidate),
852
+ file_directory_name(Candidate, Super),
853
+ ( exists_directory(Super)
854
+ -> access_file(Super, write)
855
+ ; true
856
+ )
857
+ ),
858
+ Candidates0),
859
+ list_to_set(Candidates0, Candidates), % keep order
860
+ pack_create_install_dir(Candidates, PackDir, Options).
861
+
862
+ pack_create_install_dir(Candidates, PackDir, Options) :-
863
+ Candidates = [Default=_|_],
864
+ !,
865
+ append(Candidates, [cancel=cancel], Menu),
866
+ menu(pack(create_pack_dir), Menu, Default, Selected, Options),
867
+ Selected \== cancel,
868
+ ( catch(make_directory_path(Selected), E,
869
+ (print_message(warning, E), fail))
870
+ -> PackDir = Selected
871
+ ; delete(Candidates, PackDir=create_dir(PackDir), Remaining),
872
+ pack_create_install_dir(Remaining, PackDir, Options)
873
+ ).
874
+ pack_create_install_dir(_, _, _) :-
875
+ print_message(error, pack(cannot_create_dir(pack(.)))),
876
+ fail.
877
+
878
+ %! pack_unpack_from_local(+Source, +PackTopDir, +Name, -PackDir, +Options)
879
+ %
880
+ % Unpack a package from a local media. If Source is a directory,
881
+ % either copy or link the directory. Else, Source must be an archive
882
+ % file. Options:
883
+ %
884
+ % - link(+Boolean)
885
+ % If the source is a directory, link or copy the directory?
886
+ % - upgrade(true)
887
+ % If the target is already there, wipe it and make a clean
888
+ % install.
889
+
890
+ pack_unpack_from_local(Source0, PackTopDir, Name, PackDir, Options) :-
891
+ exists_directory(Source0),
892
+ remove_slash(Source0, Source),
893
+ !,
894
+ directory_file_path(PackTopDir, Name, PackDir),
895
+ ( option(link(true), Options)
896
+ -> ( same_file(Source, PackDir)
897
+ -> true
898
+ ; remove_existing_pack(PackDir, Options),
899
+ atom_concat(PackTopDir, '/', PackTopDirS),
900
+ relative_file_name(Source, PackTopDirS, RelPath),
901
+ link_file(RelPath, PackDir, symbolic),
902
+ assertion(same_file(Source, PackDir))
903
+ )
904
+ ; \+ option(git(false), Options),
905
+ is_git_directory(Source)
906
+ -> remove_existing_pack(PackDir, Options),
907
+ run_process(path(git), [clone, Source, PackDir], [])
908
+ ; prepare_pack_dir(PackDir, Options),
909
+ copy_directory(Source, PackDir)
910
+ ).
911
+ pack_unpack_from_local(Source, PackTopDir, Name, PackDir, Options) :-
912
+ exists_file(Source),
913
+ directory_file_path(PackTopDir, Name, PackDir),
914
+ prepare_pack_dir(PackDir, Options),
915
+ pack_unpack(Source, PackDir, Name, Options).
916
+
917
+ %! pack_unpack(+SourceFile, +PackDir, +Pack, +Options)
918
+ %
919
+ % Unpack an archive to the given package dir.
920
+ %
921
+ % @tbd If library(archive) is not provided we could check for a
922
+ % suitable external program such as `tar` or `unzip`.
923
+
924
+ :- if(exists_source(library(archive))).
925
+ pack_unpack(Source, PackDir, Pack, Options) :-
926
+ ensure_loaded_archive,
927
+ pack_archive_info(Source, Pack, _Info, StripOptions),
928
+ prepare_pack_dir(PackDir, Options),
929
+ archive_extract(Source, PackDir,
930
+ [ exclude(['._*']) % MacOS resource forks
931
+ | StripOptions
932
+ ]).
933
+ :- else.
934
+ pack_unpack(_,_,_,_) :-
935
+ existence_error(library, archive).
936
+ :- endif.
937
+
938
+ %! pack_install_local(:Spec, +Dir, +Options) is det.
939
+ %
940
+ % Install a number of packages in a local directory. This predicate
941
+ % supports installing packages local to an application rather than
942
+ % globally.
943
+
944
+ pack_install_local(M:Gen, Dir, Options) :-
945
+ findall(Pack-PackOptions, call(M:Gen, Pack, PackOptions), Pairs),
946
+ pack_install_set(Pairs, Dir, Options).
947
+
948
+ pack_install_set(Pairs, Dir, Options) :-
949
+ must_be(list(pair), Pairs),
950
+ ensure_directory(Dir),
951
+ partition(known_media, Pairs, Local, Remote),
952
+ maplist(pack_options_to_versions, Local, LocalVersions),
953
+ ( Remote == []
954
+ -> AllVersions = LocalVersions
955
+ ; pairs_keys(Remote, Packs),
956
+ prolog_description(Properties),
957
+ query_pack_server(versions(Packs, Properties), Result, Options),
958
+ ( Result = true(RemoteVersions)
959
+ -> append(LocalVersions, RemoteVersions, AllVersions)
960
+ ; print_message(error, pack(query_failed(Result))),
961
+ fail
962
+ )
963
+ ),
964
+ local_packs(Dir, Existing),
965
+ pack_resolve(Pairs, Existing, AllVersions, Plan0, Options),
966
+ !, % for now, only first plan
967
+ maplist(hsts_info(Options), Plan0, Plan),
968
+ Options1 = [pack_directory(Dir)|Options],
969
+ download_plan(Pairs, Plan, PlanB, Options1),
970
+ register_downloads(PlanB, Options),
971
+ maplist(update_automatic, PlanB),
972
+ build_plan(PlanB, Built, Options1),
973
+ publish_download(PlanB, Options),
974
+ work_done(Pairs, Plan, PlanB, Built, Options).
975
+
976
+ hsts_info(Options, Info0, Info) :-
977
+ hsts(Info0.get(url), URL, Options),
978
+ !,
979
+ Info = Info0.put(url, URL).
980
+ hsts_info(_Options, Info, Info).
981
+
982
+ %! known_media(+Pair) is semidet.
983
+ %
984
+ % True when the options specify installation from a known media. If
985
+ % that applies to all packs, there is no need to query the server. We
986
+ % first download and unpack the known media, then examine the
987
+ % requirements and, if necessary, go to the server to resolve these.
988
+
989
+ known_media(_-Options) :-
990
+ option(url(_), Options).
991
+
992
+ %! pack_resolve(+Pairs, +Existing, +Versions, -Plan, +Options) is det.
993
+ %
994
+ % Generate an installation plan. Pairs is a list of Pack-Options pairs
995
+ % that specifies the desired packages. Existing is a list of
996
+ % pack(Pack, i, Title, Version, URL) terms that represents the already
997
+ % installed packages. Versions is obtained from the server. See
998
+ % `pack.pl` from the web server for details. On success, this results
999
+ % in a Plan to satisfies the requirements. The plan is a list of
1000
+ % packages to install with their location. The steps satisfy the
1001
+ % partial ordering of dependencies, such that dependencies are
1002
+ % installed before the dependents. Options:
1003
+ %
1004
+ % - upgrade(true)
1005
+ % When specified, we try to install the latest version of all
1006
+ % the packages. Otherwise, we try to minimise the installation.
1007
+
1008
+ pack_resolve(Pairs, Existing, Versions, Plan, Options) :-
1009
+ insert_existing(Existing, Versions, AllVersions, Options),
1010
+ phrase(select_version(Pairs, AllVersions,
1011
+ [ plan(PlanA), % access to plan
1012
+ dependency_for([]) % dependencies
1013
+ | Options
1014
+ ]),
1015
+ PlanA),
1016
+ mark_installed(PlanA, Existing, Plan).
1017
+
1018
+ %! insert_existing(+Existing, +Available, -Candidates, +Options) is det.
1019
+ %
1020
+ % Combine the already existing packages with the ones reported as
1021
+ % available by the server to a list of Candidates, where the candidate
1022
+ % of each package is ordered according by preference. When
1023
+ % upgrade(true) is specified, the existing is merged into the set of
1024
+ % Available versions. Otherwise Existing is prepended to Available, so
1025
+ % it is selected as first.
1026
+
1027
+ :- det(insert_existing/4).
1028
+ insert_existing(Existing, [], Versions, _Options) =>
1029
+ maplist(existing_to_versions, Existing, Versions).
1030
+ insert_existing(Existing, [Pack-Versions|T0], AllPackVersions, Options),
1031
+ select(Installed, Existing, Existing2),
1032
+ Installed.pack == Pack =>
1033
+ can_upgrade(Installed, Versions, Installed2),
1034
+ insert_existing_(Installed2, Versions, AllVersions, Options),
1035
+ AllPackVersions = [Pack-AllVersions|T],
1036
+ insert_existing(Existing2, T0, T, Options).
1037
+ insert_existing(Existing, [H|T0], AllVersions, Options) =>
1038
+ AllVersions = [H|T],
1039
+ insert_existing(Existing, T0, T, Options).
1040
+
1041
+ existing_to_versions(Installed, Pack-[Version-[Installed]]) :-
1042
+ Pack = Installed.pack,
1043
+ Version = Installed.version.
1044
+
1045
+ insert_existing_(Installed, Versions, AllVersions, Options) :-
1046
+ option(upgrade(true), Options),
1047
+ !,
1048
+ insert_existing_(Installed, Versions, AllVersions).
1049
+ insert_existing_(Installed, Versions, AllVersions, _) :-
1050
+ AllVersions = [Installed.version-[Installed]|Versions].
1051
+
1052
+ insert_existing_(Installed, [H|T0], [H|T]) :-
1053
+ H = V0-_Infos,
1054
+ cmp_versions(>, V0, Installed.version),
1055
+ !,
1056
+ insert_existing_(Installed, T0, T).
1057
+ insert_existing_(Installed, [H0|T], [H|T]) :-
1058
+ H0 = V0-Infos,
1059
+ V0 == Installed.version,
1060
+ !,
1061
+ H = V0-[Installed|Infos].
1062
+ insert_existing_(Installed, Versions, All) :-
1063
+ All = [Installed.version-[Installed]|Versions].
1064
+
1065
+ %! can_upgrade(+Installed, +Versions, -Installed2) is det.
1066
+ %
1067
+ % Add a `latest_version` key to Installed if its version is older than
1068
+ % the latest available version.
1069
+
1070
+ can_upgrade(Info, [Version-_|_], Info2) :-
1071
+ cmp_versions(>, Version, Info.version),
1072
+ !,
1073
+ Info2 = Info.put(latest_version, Version).
1074
+ can_upgrade(Info, _, Info).
1075
+
1076
+ %! mark_installed(+PlanA, +Existing, -Plan) is det.
1077
+ %
1078
+ % Mark already up-to-date packs from the plan and add a key
1079
+ % `upgrade:true` to elements of PlanA in Existing that are not the
1080
+ % same.
1081
+
1082
+ mark_installed([], _, []).
1083
+ mark_installed([Info|T], Existing, Plan) :-
1084
+ ( member(Installed, Existing),
1085
+ Installed.pack == Info.pack
1086
+ -> ( ( Installed.git == true
1087
+ -> Info.git == true,
1088
+ Installed.hash == Info.hash
1089
+ ; Version = Info.get(version)
1090
+ -> Installed.version == Version
1091
+ )
1092
+ -> Plan = [Info.put(keep, true)|PlanT] % up-to-date
1093
+ ; Plan = [Info.put(upgrade, Installed)|PlanT] % needs upgrade
1094
+ )
1095
+ ; Plan = [Info|PlanT] % new install
1096
+ ),
1097
+ mark_installed(T, Existing, PlanT).
1098
+
1099
+ %! select_version(+PackAndOptions, +Available, +Options)// is nondet.
1100
+ %
1101
+ % True when the output is a list of pack info dicts that satisfy the
1102
+ % installation requirements of PackAndOptions from the packs known to
1103
+ % be Available.
1104
+
1105
+ select_version([], _, _) -->
1106
+ [].
1107
+ select_version([Pack-PackOptions|More], Versions, Options) -->
1108
+ { memberchk(Pack-PackVersions, Versions),
1109
+ member(Version-Infos, PackVersions),
1110
+ compatible_version(Pack, Version, PackOptions),
1111
+ member(Info, Infos),
1112
+ pack_options_compatible_with_info(Info, PackOptions),
1113
+ pack_satisfies(Pack, Version, Info, Info2, PackOptions),
1114
+ all_downloads(PackVersions, Downloads)
1115
+ },
1116
+ add_to_plan(Info2.put(_{version: Version, all_downloads:Downloads}),
1117
+ Versions, Options),
1118
+ select_version(More, Versions, Options).
1119
+ select_version([Pack-_PackOptions|_More], _Versions, _Options) -->
1120
+ { existence_error(pack, Pack) }. % or warn and continue?
1121
+
1122
+ all_downloads(PackVersions, AllDownloads) :-
1123
+ aggregate_all(sum(Downloads),
1124
+ ( member(_Version-Infos, PackVersions),
1125
+ member(Info, Infos),
1126
+ get_dict(downloads, Info, Downloads)
1127
+ ),
1128
+ AllDownloads).
1129
+
1130
+ add_requirements([], _, _) -->
1131
+ [].
1132
+ add_requirements([H|T], Versions, Options) -->
1133
+ { is_prolog_token(H),
1134
+ !,
1135
+ prolog_satisfies(H)
1136
+ },
1137
+ add_requirements(T, Versions, Options).
1138
+ add_requirements([H|T], Versions, Options) -->
1139
+ { member(Pack-PackVersions, Versions),
1140
+ member(Version-Infos, PackVersions),
1141
+ member(Info, Infos),
1142
+ ( Provides = @(Pack,Version)
1143
+ ; member(Provides, Info.get(provides))
1144
+ ),
1145
+ satisfies_req(Provides, H),
1146
+ all_downloads(PackVersions, Downloads)
1147
+ },
1148
+ add_to_plan(Info.put(_{version: Version, all_downloads:Downloads}),
1149
+ Versions, Options),
1150
+ add_requirements(T, Versions, Options).
1151
+
1152
+ %! add_to_plan(+Info, +Versions, +Options) is semidet.
1153
+ %
1154
+ % Add Info to the plan. If an Info about the same pack is already in
1155
+ % the plan, but this is a different version of the pack, we must fail
1156
+ % as we cannot install two different versions of a pack.
1157
+
1158
+ add_to_plan(Info, _Versions, Options) -->
1159
+ { option(plan(Plan), Options),
1160
+ member_nonvar(Planned, Plan),
1161
+ Planned.pack == Info.pack,
1162
+ !,
1163
+ same_version(Planned, Info) % same pack, different version
1164
+ }.
1165
+ add_to_plan(Info, _Versions, _Options) -->
1166
+ { member(Conflict, Info.get(conflicts)),
1167
+ is_prolog_token(Conflict),
1168
+ prolog_satisfies(Conflict),
1169
+ !,
1170
+ fail % incompatible with this Prolog
1171
+ }.
1172
+ add_to_plan(Info, _Versions, Options) -->
1173
+ { option(plan(Plan), Options),
1174
+ member_nonvar(Planned, Plan),
1175
+ info_conflicts(Info, Planned), % Conflicts with a planned pack
1176
+ !,
1177
+ fail
1178
+ }.
1179
+ add_to_plan(Info, Versions, Options) -->
1180
+ { select_option(dependency_for(Dep0), Options, Options1),
1181
+ Options2 = [dependency_for([Info.pack|Dep0])|Options1],
1182
+ ( Dep0 = [DepFor|_]
1183
+ -> add_dependency_for(DepFor, Info, Info1)
1184
+ ; Info1 = Info
1185
+ )
1186
+ },
1187
+ [Info1],
1188
+ add_requirements(Info.get(requires,[]), Versions, Options2).
1189
+
1190
+ add_dependency_for(Pack, Info, Info) :-
1191
+ Old = Info.get(dependency_for),
1192
+ !,
1193
+ b_set_dict(dependency_for, Info, [Pack|Old]).
1194
+ add_dependency_for(Pack, Info0, Info) :-
1195
+ Info = Info0.put(dependency_for, [Pack]).
1196
+
1197
+ same_version(Info, Info) :-
1198
+ !.
1199
+ same_version(Planned, Info) :-
1200
+ Hash = Planned.get(hash),
1201
+ Hash \== (-),
1202
+ !,
1203
+ Hash == Info.get(hash).
1204
+ same_version(Planned, Info) :-
1205
+ Planned.get(version) == Info.get(version).
1206
+
1207
+ %! info_conflicts(+Info1, +Info2) is semidet.
1208
+ %
1209
+ % True if Info2 is in conflict with Info2. The relation is symetric.
1210
+
1211
+ info_conflicts(Info, Planned) :-
1212
+ info_conflicts_(Info, Planned),
1213
+ !.
1214
+ info_conflicts(Info, Planned) :-
1215
+ info_conflicts_(Planned, Info),
1216
+ !.
1217
+
1218
+ info_conflicts_(Info, Planned) :-
1219
+ member(Conflict, Info.get(conflicts)),
1220
+ \+ is_prolog_token(Conflict),
1221
+ info_provides(Planned, Provides),
1222
+ satisfies_req(Provides, Conflict),
1223
+ !.
1224
+
1225
+ info_provides(Info, Provides) :-
1226
+ ( Provides = Info.pack@Info.version
1227
+ ; member(Provides, Info.get(provides))
1228
+ ).
1229
+
1230
+ %! pack_satisfies(+Pack, +Version, +Info0, -Info, +Options) is semidet.
1231
+ %
1232
+ % True if Pack@Version with Info satisfies the pack installation
1233
+ % options provided by Options.
1234
+
1235
+ pack_satisfies(_Pack, _Version, Info0, Info, Options) :-
1236
+ option(commit('HEAD'), Options),
1237
+ !,
1238
+ Info0.get(git) == true,
1239
+ Info = Info0.put(commit, 'HEAD').
1240
+ pack_satisfies(_Pack, _Version, Info, Info, Options) :-
1241
+ option(commit(Commit), Options),
1242
+ !,
1243
+ Commit == Info.get(hash).
1244
+ pack_satisfies(Pack, Version, Info, Info, Options) :-
1245
+ option(version(ReqVersion), Options),
1246
+ !,
1247
+ satisfies_version(Pack, Version, ReqVersion).
1248
+ pack_satisfies(_Pack, _Version, Info, Info, _Options).
1249
+
1250
+ %! satisfies_version(+Pack, +PackVersion, +RequiredVersion) is semidet.
1251
+
1252
+ satisfies_version(Pack, Version, ReqVersion) :-
1253
+ catch(require_version(pack(Pack), Version, ReqVersion),
1254
+ error(version_error(pack(Pack), Version, ReqVersion),_),
1255
+ fail).
1256
+
1257
+ %! satisfies_req(+Provides, +Required) is semidet.
1258
+ %
1259
+ % Check a token requirements.
1260
+
1261
+ satisfies_req(Token, Token) => true.
1262
+ satisfies_req(@(Token,_), Token) => true.
1263
+ satisfies_req(@(Token,PrvVersion), Req), cmp(Req, Token, Cmp, ReqVersion) =>
1264
+ cmp_versions(Cmp, PrvVersion, ReqVersion).
1265
+ satisfies_req(_,_) => fail.
1266
+
1267
+ cmp(Token < Version, Token, <, Version).
1268
+ cmp(Token =< Version, Token, =<, Version).
1269
+ cmp(Token = Version, Token, =, Version).
1270
+ cmp(Token == Version, Token, ==, Version).
1271
+ cmp(Token >= Version, Token, >=, Version).
1272
+ cmp(Token > Version, Token, >, Version).
1273
+
1274
+ %! pack_options_to_versions(+PackOptionsPair, -Versions) is det.
1275
+ %
1276
+ % Create an available package term from Pack and Options if it
1277
+ % contains a url(URL) option. This allows installing packages that are
1278
+ % not known to the server. In most cases, the URL will be a git URL or
1279
+ % the URL to download an archive. It can also be a ``file://`` url to
1280
+ % install from a local archive.
1281
+ %
1282
+ % The first clause deals with a wildcard URL. See
1283
+ % pack_default_options/4, case (7).
1284
+
1285
+ :- det(pack_options_to_versions/2).
1286
+ pack_options_to_versions(Pack-PackOptions, Pack-Versions) :-
1287
+ option(versions(Available), PackOptions), !,
1288
+ maplist(version_url_info(Pack, PackOptions), Available, Versions).
1289
+ pack_options_to_versions(Pack-PackOptions, Pack-[Version-[Info]]) :-
1290
+ option(url(URL), PackOptions),
1291
+ findall(Prop, option_info_prop(PackOptions, Prop), Pairs),
1292
+ dict_create(Info, #,
1293
+ [ pack-Pack,
1294
+ url-URL
1295
+ | Pairs
1296
+ ]),
1297
+ Version = Info.get(version, '0.0.0').
1298
+
1299
+ version_url_info(Pack, PackOptions, Version-URL, Version-[Info]) :-
1300
+ findall(Prop,
1301
+ ( option_info_prop(PackOptions, Prop),
1302
+ Prop \= version-_
1303
+ ),
1304
+ Pairs),
1305
+ dict_create(Info, #,
1306
+ [ pack-Pack,
1307
+ url-URL,
1308
+ version-Version
1309
+ | Pairs
1310
+ ]).
1311
+
1312
+ option_info_prop(PackOptions, Prop-Value) :-
1313
+ option_info(Prop),
1314
+ Opt =.. [Prop,Value],
1315
+ option(Opt, PackOptions).
1316
+
1317
+ option_info(git).
1318
+ option_info(hash).
1319
+ option_info(version).
1320
+ option_info(branch).
1321
+ option_info(link).
1322
+
1323
+ %! compatible_version(+Pack, +Version, +Options) is semidet.
1324
+ %
1325
+ % Fails if Options demands a version and Version is not compatible
1326
+ % with Version.
1327
+
1328
+ compatible_version(Pack, Version, PackOptions) :-
1329
+ option(version(ReqVersion), PackOptions),
1330
+ !,
1331
+ satisfies_version(Pack, Version, ReqVersion).
1332
+ compatible_version(_, _, _).
1333
+
1334
+ %! pack_options_compatible_with_info(+Info, +PackOptions) is semidet.
1335
+ %
1336
+ % Ignore information from the server that is incompatible with the
1337
+ % request.
1338
+
1339
+ pack_options_compatible_with_info(Info, PackOptions) :-
1340
+ findall(Prop, option_info_prop(PackOptions, Prop), Pairs),
1341
+ dict_create(Dict, _, Pairs),
1342
+ Dict >:< Info.
1343
+
1344
+ %! download_plan(+Targets, +Plan, +Options) is semidet.
1345
+ %
1346
+ % Download or update all packages from Plan. We need to do this as a
1347
+ % first step because we may not have (up-to-date) dependency
1348
+ % information about all packs. For example, a pack may be installed at
1349
+ % the git HEAD revision that is not yet know to the server or it may
1350
+ % be installed from a url that is not known at all at the server.
1351
+
1352
+ download_plan(_Targets, Plan, Plan, _Options) :-
1353
+ exclude(installed, Plan, []),
1354
+ !.
1355
+ download_plan(Targets, Plan0, Plan, Options) :-
1356
+ confirm(download_plan(Plan0), yes, Options),
1357
+ maplist(download_from_info(Options), Plan0, Plan1),
1358
+ plan_unsatisfied_dependencies(Plan1, Deps),
1359
+ ( Deps == []
1360
+ -> Plan = Plan1
1361
+ ; print_message(informational, pack(new_dependencies(Deps))),
1362
+ prolog_description(Properties),
1363
+ query_pack_server(versions(Deps, Properties), Result, []),
1364
+ ( Result = true(Versions)
1365
+ -> pack_resolve(Targets, Plan1, Versions, Plan2, Options),
1366
+ !,
1367
+ download_plan(Targets, Plan2, Plan, Options)
1368
+ ; print_message(error, pack(query_failed(Result))),
1369
+ fail
1370
+ )
1371
+ ).
1372
+
1373
+ %! plan_unsatisfied_dependencies(+Plan, -Deps) is det.
1374
+ %
1375
+ % True when Deps is a list of dependency tokens in Plan that is not
1376
+ % satisfied.
1377
+
1378
+ plan_unsatisfied_dependencies(Plan, Deps) :-
1379
+ phrase(plan_unsatisfied_dependencies(Plan, Plan), Deps).
1380
+
1381
+ plan_unsatisfied_dependencies([], _) -->
1382
+ [].
1383
+ plan_unsatisfied_dependencies([Info|Infos], Plan) -->
1384
+ { Deps = Info.get(requires) },
1385
+ plan_unsatisfied_requirements(Deps, Plan),
1386
+ plan_unsatisfied_dependencies(Infos, Plan).
1387
+
1388
+ plan_unsatisfied_requirements([], _) -->
1389
+ [].
1390
+ plan_unsatisfied_requirements([H|T], Plan) -->
1391
+ { is_prolog_token(H), % Can this fail?
1392
+ prolog_satisfies(H)
1393
+ },
1394
+ !,
1395
+ plan_unsatisfied_requirements(T, Plan).
1396
+ plan_unsatisfied_requirements([H|T], Plan) -->
1397
+ { member(Info, Plan),
1398
+ ( ( Version = Info.get(version)
1399
+ -> Provides = @(Info.get(pack), Version)
1400
+ ; Provides = Info.get(pack)
1401
+ )
1402
+ ; member(Provides, Info.get(provides))
1403
+ ),
1404
+ satisfies_req(Provides, H)
1405
+ }, !,
1406
+ plan_unsatisfied_requirements(T, Plan).
1407
+ plan_unsatisfied_requirements([H|T], Plan) -->
1408
+ [H],
1409
+ plan_unsatisfied_requirements(T, Plan).
1410
+
1411
+
1412
+ %! build_plan(+Plan, -Built, +Options) is det.
1413
+ %
1414
+ % Run post installation steps. We build dependencies before their
1415
+ % dependents, so we first do a topological sort on the packs based on
1416
+ % the pack dependencies.
1417
+
1418
+ build_plan(Plan, Ordered, Options) :-
1419
+ maplist(decide_autoload_pack(Options), Plan, Plan1),
1420
+ partition(needs_rebuild_from_info(Options), Plan1, ToBuild, NoBuild),
1421
+ maplist(attach_from_info(Options), NoBuild),
1422
+ ( ToBuild == []
1423
+ -> post_install_autoload(NoBuild),
1424
+ Ordered = []
1425
+ ; order_builds(ToBuild, Ordered),
1426
+ confirm(build_plan(Ordered), yes, Options),
1427
+ maplist(exec_plan_rebuild_step(Options), Ordered)
1428
+ ).
1429
+
1430
+ %! needs_rebuild_from_info(+Options, +Info) is semidet.
1431
+ %
1432
+ % True when we need to rebuilt the pack.
1433
+
1434
+ needs_rebuild_from_info(Options, Info) :-
1435
+ PackDir = Info.installed,
1436
+ is_foreign_pack(PackDir, _),
1437
+ \+ is_built(PackDir, Options).
1438
+
1439
+ %! is_built(+PackDir, +Options) is semidet.
1440
+ %
1441
+ % True if the pack in PackDir has been built.
1442
+ %
1443
+ % @tbd We now verify it was built by the exact same version. That is
1444
+ % normally an overkill.
1445
+
1446
+ is_built(PackDir, _Options) :-
1447
+ current_prolog_flag(arch, Arch),
1448
+ prolog_version_dotted(Version), % Major.Minor.Patch
1449
+ pack_status_dir(PackDir, built(Arch, Version, _)).
1450
+
1451
+ %! order_builds(+ToBuild, -Ordered) is det.
1452
+ %
1453
+ % Order the build processes by building dependencies before the
1454
+ % packages that rely on them as they may need them during the build.
1455
+
1456
+ order_builds(ToBuild, Ordered) :-
1457
+ findall(Pack-Dependent, dep_edge(ToBuild, Pack, Dependent), Edges),
1458
+ maplist(get_dict(pack), ToBuild, Packs),
1459
+ vertices_edges_to_ugraph(Packs, Edges, Graph),
1460
+ ugraph_layers(Graph, Layers),
1461
+ append(Layers, PackNames),
1462
+ maplist(pack_info_from_name(ToBuild), PackNames, Ordered).
1463
+
1464
+ %! dep_edge(+Infos, -Pack, -Dependent) is nondet.
1465
+ %
1466
+ % True when Pack needs to be installed as a dependency of Dependent.
1467
+ % Both Pack and Dependent are pack _names_. I.e., this implies that we
1468
+ % must build Pack _before_ Dependent.
1469
+
1470
+ dep_edge(Infos, Pack, Dependent) :-
1471
+ member(Info, Infos),
1472
+ Pack = Info.pack,
1473
+ member(Dependent, Info.get(dependency_for)),
1474
+ ( member(DepInfo, Infos),
1475
+ DepInfo.pack == Dependent
1476
+ -> true
1477
+ ).
1478
+
1479
+ :- det(pack_info_from_name/3).
1480
+ pack_info_from_name(Infos, Pack, Info) :-
1481
+ member(Info, Infos),
1482
+ Info.pack == Pack,
1483
+ !.
1484
+
1485
+ %! exec_plan_rebuild_step(+Options, +Info) is det.
1486
+ %
1487
+ % Execute the rebuild steps for the given Info.
1488
+
1489
+ exec_plan_rebuild_step(Options, Info) :-
1490
+ print_message(informational, pack(build(Info.pack, Info.installed))),
1491
+ pack_post_install(Info, Options),
1492
+ attach_from_info(Options, Info).
1493
+
1494
+ %! attach_from_info(+Options, +Info) is det.
1495
+ %
1496
+ % Make the package visible.
1497
+
1498
+ attach_from_info(_Options, Info) :-
1499
+ Info.get(keep) == true,
1500
+ !.
1501
+ attach_from_info(Options, Info) :-
1502
+ ( option(pack_directory(_Parent), Options)
1503
+ -> pack_attach(Info.installed, [duplicate(replace)])
1504
+ ; pack_attach(Info.installed, [])
1505
+ ).
1506
+
1507
+ %! download_from_info(+Options, +Info0, -Info) is det.
1508
+ %
1509
+ % Download a package guided by Info. Note that this does __not__ run
1510
+ % any scripts. This implies that dependencies do not matter and we can
1511
+ % proceed in any order. This is important because we may use packages
1512
+ % at their git HEAD, which implies that requirements may be different
1513
+ % from what is in the Info terms.
1514
+
1515
+ download_from_info(Options, Info0, Info), option(dryrun(true), Options) =>
1516
+ print_term(Info0, [nl(true)]),
1517
+ Info = Info0.
1518
+ download_from_info(_Options, Info0, Info), installed(Info0) =>
1519
+ Info = Info0.
1520
+ download_from_info(_Options, Info0, Info),
1521
+ _{upgrade:OldInfo, git:true} :< Info0,
1522
+ is_git_directory(OldInfo.installed) =>
1523
+ PackDir = OldInfo.installed,
1524
+ git_checkout_version(PackDir, [commit(Info0.hash)]),
1525
+ reload_info(PackDir, Info0, Info).
1526
+ download_from_info(Options, Info0, Info),
1527
+ _{upgrade:OldInfo} :< Info0 =>
1528
+ PackDir = OldInfo.installed,
1529
+ detach_pack(OldInfo.pack, PackDir),
1530
+ delete_directory_and_contents(PackDir),
1531
+ del_dict(upgrade, Info0, _, Info1),
1532
+ download_from_info(Options, Info1, Info).
1533
+ download_from_info(Options, Info0, Info),
1534
+ _{url:URL, git:true} :< Info0, \+ have_git =>
1535
+ git_archive_url(URL, Archive, Options),
1536
+ download_from_info([git_url(URL)|Options],
1537
+ Info0.put(_{ url:Archive,
1538
+ git:false,
1539
+ git_url:URL
1540
+ }),
1541
+ Info1),
1542
+ % restore the hash to register the download.
1543
+ ( Info1.get(version) == Info0.get(version),
1544
+ Hash = Info0.get(hash)
1545
+ -> Info = Info1.put(hash, Hash)
1546
+ ; Info = Info1
1547
+ ).
1548
+ download_from_info(Options, Info0, Info),
1549
+ _{url:URL} :< Info0 =>
1550
+ select_option(pack_directory(Dir), Options, Options1),
1551
+ select_option(version(_), Options1, Options2, _),
1552
+ download_info_extra(Info0, InstallOptions, Options2),
1553
+ pack_download_from_url(URL, Dir, Info0.pack,
1554
+ [ interactive(false),
1555
+ pack_dir(PackDir)
1556
+ | InstallOptions
1557
+ ]),
1558
+ reload_info(PackDir, Info0, Info).
1559
+
1560
+ download_info_extra(Info, [git(true),commit(Hash)|Options], Options) :-
1561
+ Info.get(git) == true,
1562
+ !,
1563
+ Hash = Info.get(commit, 'HEAD').
1564
+ download_info_extra(Info, [link(true)|Options], Options) :-
1565
+ Info.get(link) == true,
1566
+ !.
1567
+ download_info_extra(_, Options, Options).
1568
+
1569
+ installed(Info) :-
1570
+ _ = Info.get(installed).
1571
+
1572
+ detach_pack(Pack, PackDir) :-
1573
+ ( current_pack(Pack, PackDir)
1574
+ -> '$pack_detach'(Pack, PackDir)
1575
+ ; true
1576
+ ).
1577
+
1578
+ %! reload_info(+PackDir, +Info0, -Info) is det.
1579
+ %
1580
+ % Update the requires and provides metadata. Info0 is what we got from
1581
+ % the server, but the package may be different as we may have asked
1582
+ % for the git HEAD or the package URL may not have been known by the
1583
+ % server at all.
1584
+
1585
+ reload_info(_PackDir, Info, Info) :-
1586
+ _ = Info.get(installed), % we read it from the package
1587
+ !.
1588
+ reload_info(PackDir, Info0, Info) :-
1589
+ local_pack_info(PackDir, Info1),
1590
+ Info = Info0.put(installed, PackDir)
1591
+ .put(downloaded, Info0.url)
1592
+ .put(Info1).
1593
+
1594
+ %! work_done(+Targets, +Plan, +PlanB, +Built, +Options) is det.
1595
+ %
1596
+ % Targets has successfully been installed and the packs Built have
1597
+ % successfully ran their build scripts.
1598
+
1599
+ work_done(_, _, _, _, Options),
1600
+ option(silent(true), Options) =>
1601
+ true.
1602
+ work_done(Targets, Plan, Plan, [], _Options) =>
1603
+ convlist(can_upgrade_target(Plan), Targets, CanUpgrade),
1604
+ ( CanUpgrade == []
1605
+ -> pairs_keys(Targets, Packs),
1606
+ print_message(informational, pack(up_to_date(Packs)))
1607
+ ; print_message(informational, pack(installed_can_upgrade(CanUpgrade)))
1608
+ ).
1609
+ work_done(_, _, _, _, _) =>
1610
+ true.
1611
+
1612
+ can_upgrade_target(Plan, Pack-_, Info) =>
1613
+ member(Info, Plan),
1614
+ Info.pack == Pack,
1615
+ !,
1616
+ _ = Info.get(latest_version).
1617
+
1618
+ %! local_packs(+Dir, -Packs) is det.
1619
+ %
1620
+ % True when Packs is a list with information for all installed
1621
+ % packages.
1622
+
1623
+ local_packs(Dir, Packs) :-
1624
+ findall(Pack, pack_in_subdir(Dir, Pack), Packs).
1625
+
1626
+ pack_in_subdir(Dir, Info) :-
1627
+ directory_member(Dir, PackDir,
1628
+ [ file_type(directory),
1629
+ hidden(false)
1630
+ ]),
1631
+ local_pack_info(PackDir, Info).
1632
+
1633
+ local_pack_info(PackDir,
1634
+ #{ pack: Pack,
1635
+ version: Version,
1636
+ title: Title,
1637
+ hash: Hash,
1638
+ url: URL,
1639
+ git: IsGit,
1640
+ requires: Requires,
1641
+ provides: Provides,
1642
+ conflicts: Conflicts,
1643
+ installed: PackDir
1644
+ }) :-
1645
+ directory_file_path(PackDir, 'pack.pl', MetaFile),
1646
+ exists_file(MetaFile),
1647
+ file_base_name(PackDir, DirName),
1648
+ findall(Term, pack_dir_info(PackDir, _, Term), Info),
1649
+ option(pack(Pack), Info, DirName),
1650
+ option(title(Title), Info, '<no title>'),
1651
+ option(version(Version), Info, '<no version>'),
1652
+ option(download(URL), Info, '<no download url>'),
1653
+ findall(Req, member(requires(Req), Info), Requires),
1654
+ findall(Prv, member(provides(Prv), Info), Provides),
1655
+ findall(Cfl, member(conflicts(Cfl), Info), Conflicts),
1656
+ ( have_git,
1657
+ is_git_directory(PackDir)
1658
+ -> git_hash(Hash, [directory(PackDir)]),
1659
+ IsGit = true
1660
+ ; Hash = '-',
1661
+ IsGit = false
1662
+ ).
1663
+
1664
+
1665
+ /*******************************
1666
+ * PROLOG VERSIONS *
1667
+ *******************************/
1668
+
1669
+ %! prolog_description(-Description) is det.
1670
+ %
1671
+ % Provide a description of the running Prolog system. Version terms:
1672
+ %
1673
+ % - prolog(Dialect, Version)
1674
+ %
1675
+ % @tbd: establish a language for features. Sync with
1676
+ % library(prolog_versions)
1677
+
1678
+ prolog_description([prolog(swi(Version))]) :-
1679
+ prolog_version(Version).
1680
+
1681
+ prolog_version(Version) :-
1682
+ current_prolog_flag(version_git, Version),
1683
+ !.
1684
+ prolog_version(Version) :-
1685
+ prolog_version_dotted(Version).
1686
+
1687
+ prolog_version_dotted(Version) :-
1688
+ current_prolog_flag(version_data, swi(Major, Minor, Patch, _)),
1689
+ VNumbers = [Major, Minor, Patch],
1690
+ atomic_list_concat(VNumbers, '.', Version).
1691
+
1692
+ %! is_prolog_token(+Token) is semidet.
1693
+ %
1694
+ % True when Token describes a property of the target Prolog
1695
+ % system.
1696
+
1697
+ is_prolog_token(Token), cmp(Token, prolog, _Cmp, _Version) => true.
1698
+ is_prolog_token(prolog:Feature), atom(Feature) => true.
1699
+ is_prolog_token(prolog:Feature), flag_value_feature(Feature, _Flag, _Value) =>
1700
+ true.
1701
+ is_prolog_token(_) => fail.
1702
+
1703
+ %! prolog_satisfies(+Token) is semidet.
1704
+ %
1705
+ % True when the running Prolog system satisfies token. Processes
1706
+ % requires(Token) terms for
1707
+ %
1708
+ % - prolog Cmp Version
1709
+ % Demand a Prolog version (range).
1710
+ % - prolog:Flag
1711
+ % - prolog:Flag(Value)
1712
+ % - prolog:library(Lib)
1713
+ %
1714
+ % @see require_prolog_version/2.
1715
+
1716
+ prolog_satisfies(Token), cmp(Token, prolog, Cmp, ReqVersion) =>
1717
+ prolog_version(CurrentVersion),
1718
+ cmp_versions(Cmp, CurrentVersion, ReqVersion).
1719
+ prolog_satisfies(prolog:library(Lib)), atom(Lib) =>
1720
+ exists_source(library(Lib)).
1721
+ prolog_satisfies(prolog:Feature), atom(Feature) =>
1722
+ current_prolog_flag(Feature, true).
1723
+ prolog_satisfies(prolog:Feature), flag_value_feature(Feature, Flag, Value) =>
1724
+ current_prolog_flag(Flag, Value).
1725
+
1726
+ flag_value_feature(Feature, Flag, Value) :-
1727
+ compound(Feature),
1728
+ compound_name_arguments(Feature, Flag, [Value]),
1729
+ atom(Flag).
1730
+
1731
+
1732
+ /*******************************
1733
+ * INFO *
1734
+ *******************************/
1735
+
1736
+ %! pack_archive_info(+Archive, +Pack, -Info, -Strip)
1737
+ %
1738
+ % True when Archive archives Pack. Info is unified with the terms
1739
+ % from pack.pl in the pack and Strip is the strip-option for
1740
+ % archive_extract/3.
1741
+ %
1742
+ % Requires library(archive), which is lazily loaded when needed.
1743
+ %
1744
+ % @error existence_error(pack_file, 'pack.pl') if the archive
1745
+ % doesn't contain pack.pl
1746
+ % @error Syntax errors if pack.pl cannot be parsed.
1747
+
1748
+ :- if(exists_source(library(archive))).
1749
+ ensure_loaded_archive :-
1750
+ current_predicate(archive_open/3),
1751
+ !.
1752
+ ensure_loaded_archive :-
1753
+ use_module(library(archive)).
1754
+
1755
+ pack_archive_info(Archive, Pack, [archive_size(Bytes)|Info], Strip) :-
1756
+ ensure_loaded_archive,
1757
+ size_file(Archive, Bytes),
1758
+ setup_call_cleanup(
1759
+ archive_open(Archive, Handle, []),
1760
+ ( repeat,
1761
+ ( archive_next_header(Handle, InfoFile)
1762
+ -> true
1763
+ ; !, fail
1764
+ )
1765
+ ),
1766
+ archive_close(Handle)),
1767
+ file_base_name(InfoFile, 'pack.pl'),
1768
+ atom_concat(Prefix, 'pack.pl', InfoFile),
1769
+ strip_option(Prefix, Pack, Strip),
1770
+ setup_call_cleanup(
1771
+ archive_open_entry(Handle, Stream),
1772
+ read_stream_to_terms(Stream, Info),
1773
+ close(Stream)),
1774
+ !,
1775
+ must_be(ground, Info),
1776
+ maplist(valid_term(pack_info_term), Info).
1777
+ :- else.
1778
+ pack_archive_info(_, _, _, _) :-
1779
+ existence_error(library, archive).
1780
+ :- endif.
1781
+ pack_archive_info(_, _, _, _) :-
1782
+ existence_error(pack_file, 'pack.pl').
1783
+
1784
+ strip_option('', _, []) :- !.
1785
+ strip_option('./', _, []) :- !.
1786
+ strip_option(Prefix, Pack, [remove_prefix(Prefix)]) :-
1787
+ atom_concat(PrefixDir, /, Prefix),
1788
+ file_base_name(PrefixDir, Base),
1789
+ ( Base == Pack
1790
+ -> true
1791
+ ; pack_version_file(Pack, _, Base)
1792
+ -> true
1793
+ ; \+ sub_atom(PrefixDir, _, _, _, /)
1794
+ ).
1795
+
1796
+ read_stream_to_terms(Stream, Terms) :-
1797
+ read(Stream, Term0),
1798
+ read_stream_to_terms(Term0, Stream, Terms).
1799
+
1800
+ read_stream_to_terms(end_of_file, _, []) :- !.
1801
+ read_stream_to_terms(Term0, Stream, [Term0|Terms]) :-
1802
+ read(Stream, Term1),
1803
+ read_stream_to_terms(Term1, Stream, Terms).
1804
+
1805
+
1806
+ %! pack_git_info(+GitDir, -Hash, -Info) is det.
1807
+ %
1808
+ % Retrieve info from a cloned git repository that is compatible
1809
+ % with pack_archive_info/4.
1810
+
1811
+ pack_git_info(GitDir, Hash, [git(true), installed_size(Bytes)|Info]) :-
1812
+ exists_directory(GitDir),
1813
+ !,
1814
+ git_ls_tree(Entries, [directory(GitDir)]),
1815
+ git_hash(Hash, [directory(GitDir)]),
1816
+ maplist(arg(4), Entries, Sizes),
1817
+ sum_list(Sizes, Bytes),
1818
+ dir_metadata(GitDir, Info).
1819
+
1820
+ dir_metadata(GitDir, Info) :-
1821
+ directory_file_path(GitDir, 'pack.pl', InfoFile),
1822
+ read_file_to_terms(InfoFile, Info, [encoding(utf8)]),
1823
+ maplist(valid_term(pack_info_term), Info).
1824
+
1825
+ %! download_file_sanity_check(+Archive, +Pack, +Info) is semidet.
1826
+ %
1827
+ % Perform basic sanity checks on DownloadFile
1828
+
1829
+ download_file_sanity_check(Archive, Pack, Info) :-
1830
+ info_field(name(PackName), Info),
1831
+ info_field(version(PackVersion), Info),
1832
+ pack_version_file(PackFile, FileVersion, Archive),
1833
+ must_match([Pack, PackName, PackFile], name),
1834
+ must_match([PackVersion, FileVersion], version).
1835
+
1836
+ info_field(Field, Info) :-
1837
+ memberchk(Field, Info),
1838
+ ground(Field),
1839
+ !.
1840
+ info_field(Field, _Info) :-
1841
+ functor(Field, FieldName, _),
1842
+ print_message(error, pack(missing(FieldName))),
1843
+ fail.
1844
+
1845
+ must_match(Values, _Field) :-
1846
+ sort(Values, [_]),
1847
+ !.
1848
+ must_match(Values, Field) :-
1849
+ print_message(error, pack(conflict(Field, Values))),
1850
+ fail.
1851
+
1852
+
1853
+ /*******************************
1854
+ * INSTALLATION *
1855
+ *******************************/
1856
+
1857
+ %! prepare_pack_dir(+Dir, +Options)
1858
+ %
1859
+ % Prepare for installing the package into Dir. This
1860
+ %
1861
+ % - If the directory exist and is empty, done.
1862
+ % - Else if the directory exists, remove the directory and recreate
1863
+ % it. Note that if the directory is a symlink this just deletes
1864
+ % the link.
1865
+ % - Else if some entry (file, link, ...) exists, delete it and
1866
+ % create a new directory.
1867
+ % - Else create the directory.
1868
+
1869
+ prepare_pack_dir(Dir, Options) :-
1870
+ exists_directory(Dir),
1871
+ !,
1872
+ ( empty_directory(Dir)
1873
+ -> true
1874
+ ; remove_existing_pack(Dir, Options)
1875
+ -> make_directory(Dir)
1876
+ ).
1877
+ prepare_pack_dir(Dir, _) :-
1878
+ ( read_link(Dir, _, _)
1879
+ ; access_file(Dir, exist)
1880
+ ),
1881
+ !,
1882
+ delete_file(Dir),
1883
+ make_directory(Dir).
1884
+ prepare_pack_dir(Dir, _) :-
1885
+ make_directory(Dir).
1886
+
1887
+ %! empty_directory(+Directory) is semidet.
1888
+ %
1889
+ % True if Directory is empty (holds no files or sub-directories).
1890
+
1891
+ empty_directory(Dir) :-
1892
+ \+ ( directory_files(Dir, Entries),
1893
+ member(Entry, Entries),
1894
+ \+ special(Entry)
1895
+ ).
1896
+
1897
+ special(.).
1898
+ special(..).
1899
+
1900
+ %! remove_existing_pack(+PackDir, +Options) is semidet.
1901
+ %
1902
+ % Remove a possible existing pack directory if the option
1903
+ % upgrade(true) is present. This is used to remove an old installation
1904
+ % before unpacking a new archive, copy or link a directory with the
1905
+ % new contents.
1906
+
1907
+ remove_existing_pack(PackDir, Options) :-
1908
+ exists_directory(PackDir),
1909
+ !,
1910
+ ( ( option(upgrade(true), Options)
1911
+ ; confirm(remove_existing_pack(PackDir), yes, Options)
1912
+ )
1913
+ -> delete_directory_and_contents(PackDir)
1914
+ ; print_message(error, pack(directory_exists(PackDir))),
1915
+ fail
1916
+ ).
1917
+ remove_existing_pack(_, _).
1918
+
1919
+ %! pack_download_from_url(+URL, +PackDir, +Pack, +Options)
1920
+ %
1921
+ % Download a package from a remote source. For git repositories, we
1922
+ % simply clone. Archives are downloaded. Options:
1923
+ %
1924
+ % - git(true)
1925
+ % Assume URL refers to a git repository.
1926
+ % - pack_dir(-Dir)
1927
+ % Dir is unified with the location where the pack is installed.
1928
+ %
1929
+ % @tbd We currently use the built-in HTTP client. For complete
1930
+ % coverage, we should consider using an external (e.g., `curl`) if
1931
+ % available.
1932
+
1933
+ pack_download_from_url(URL, PackTopDir, Pack, Options) :-
1934
+ option(git(true), Options),
1935
+ !,
1936
+ directory_file_path(PackTopDir, Pack, PackDir),
1937
+ prepare_pack_dir(PackDir, Options),
1938
+ ( option(branch(Branch), Options)
1939
+ -> Extra = ['--branch', Branch]
1940
+ ; Extra = []
1941
+ ),
1942
+ run_process(path(git), [clone, URL, PackDir|Extra], []),
1943
+ git_checkout_version(PackDir, [update(false)|Options]),
1944
+ option(pack_dir(PackDir), Options, _).
1945
+ pack_download_from_url(URL0, PackTopDir, Pack, Options) :-
1946
+ download_url(URL0),
1947
+ !,
1948
+ hsts(URL0, URL, Options),
1949
+ directory_file_path(PackTopDir, Pack, PackDir),
1950
+ prepare_pack_dir(PackDir, Options),
1951
+ pack_download_dir(PackTopDir, DownLoadDir),
1952
+ download_file(URL, Pack, DownloadBase, Options),
1953
+ directory_file_path(DownLoadDir, DownloadBase, DownloadFile),
1954
+ ( option(insecure(true), Options, false)
1955
+ -> TLSOptions = [cert_verify_hook(ssl_verify)]
1956
+ ; TLSOptions = []
1957
+ ),
1958
+ print_message(informational, pack(download(begin, Pack, URL, DownloadFile))),
1959
+ setup_call_cleanup(
1960
+ http_open(URL, In, TLSOptions),
1961
+ setup_call_cleanup(
1962
+ open(DownloadFile, write, Out, [type(binary)]),
1963
+ copy_stream_data(In, Out),
1964
+ close(Out)),
1965
+ close(In)),
1966
+ print_message(informational, pack(download(end, Pack, URL, DownloadFile))),
1967
+ pack_archive_info(DownloadFile, Pack, Info, _),
1968
+ ( option(git_url(GitURL), Options)
1969
+ -> Origin = GitURL % implicit download from git.
1970
+ ; download_file_sanity_check(DownloadFile, Pack, Info),
1971
+ Origin = URL
1972
+ ),
1973
+ pack_unpack_from_local(DownloadFile, PackTopDir, Pack, PackDir, Options),
1974
+ pack_assert(PackDir, archive(DownloadFile, Origin)),
1975
+ option(pack_dir(PackDir), Options, _).
1976
+ pack_download_from_url(URL, PackTopDir, Pack, Options) :-
1977
+ local_uri_file_name(URL, File),
1978
+ !,
1979
+ pack_unpack_from_local(File, PackTopDir, Pack, PackDir, Options),
1980
+ pack_assert(PackDir, archive(File, URL)),
1981
+ option(pack_dir(PackDir), Options, _).
1982
+ pack_download_from_url(URL, _PackTopDir, _Pack, _Options) :-
1983
+ domain_error(url, URL).
1984
+
1985
+ %! git_checkout_version(+PackDir, +Options) is det.
1986
+ %
1987
+ % Given a checked out version of a repository, put the repo at the
1988
+ % desired version. Options:
1989
+ %
1990
+ % - commit(+Commit)
1991
+ % Target commit or `'HEAD'`. If `'HEAD'`, get the HEAD of the
1992
+ % explicit (option branch(Branch)), current or default branch. If
1993
+ % the commit is a hash and it is the tip of a branch, checkout
1994
+ % this branch. Else simply checkout the hash.
1995
+ % - branch(+Branch)
1996
+ % Used with commit('HEAD').
1997
+ % - version(+Version)
1998
+ % Checkout a tag. If there is a tag matching Version use that,
1999
+ % otherwise try to find a tag that ends with Version and demand
2000
+ % the prefix to be letters, optionally followed by a dash or
2001
+ % underscore. Examples: 2.1, V2.1, v_2.1.
2002
+ % - update(true)
2003
+ % If none of the above is given update the repo. If it is on
2004
+ % a branch, _pull_. Else, put it on the default branch and
2005
+ % pull.
2006
+
2007
+ git_checkout_version(PackDir, Options) :-
2008
+ option(commit('HEAD'), Options),
2009
+ option(branch(Branch), Options),
2010
+ !,
2011
+ git_ensure_on_branch(PackDir, Branch),
2012
+ run_process(path(git), ['-C', PackDir, pull], []).
2013
+ git_checkout_version(PackDir, Options) :-
2014
+ option(commit('HEAD'), Options),
2015
+ git_current_branch(_, [directory(PackDir)]),
2016
+ !,
2017
+ run_process(path(git), ['-C', PackDir, pull], []).
2018
+ git_checkout_version(PackDir, Options) :-
2019
+ option(commit('HEAD'), Options),
2020
+ !,
2021
+ git_default_branch(Branch, [directory(PackDir)]),
2022
+ git_ensure_on_branch(PackDir, Branch),
2023
+ run_process(path(git), ['-C', PackDir, pull], []).
2024
+ git_checkout_version(PackDir, Options) :-
2025
+ option(commit(Hash), Options),
2026
+ run_process(path(git), ['-C', PackDir, fetch], []),
2027
+ git_branches(Branches, [contains(Hash), directory(PackDir)]),
2028
+ git_process_output(['-C', PackDir, 'rev-parse' | Branches],
2029
+ read_lines_to_atoms(Commits),
2030
+ []),
2031
+ nth1(I, Commits, Hash),
2032
+ nth1(I, Branches, Branch),
2033
+ !,
2034
+ git_ensure_on_branch(PackDir, Branch).
2035
+ git_checkout_version(PackDir, Options) :-
2036
+ option(commit(Hash), Options),
2037
+ !,
2038
+ run_process(path(git), ['-C', PackDir, checkout, '--quiet', Hash], []).
2039
+ git_checkout_version(PackDir, Options) :-
2040
+ option(version(Version), Options),
2041
+ !,
2042
+ git_tags(Tags, [directory(PackDir)]),
2043
+ ( memberchk(Version, Tags)
2044
+ -> Tag = Version
2045
+ ; member(Tag, Tags),
2046
+ sub_atom(Tag, B, _, 0, Version),
2047
+ sub_atom(Tag, 0, B, _, Prefix),
2048
+ version_prefix(Prefix)
2049
+ -> true
2050
+ ; existence_error(version_tag, Version)
2051
+ ),
2052
+ run_process(path(git), ['-C', PackDir, checkout, Tag], []).
2053
+ git_checkout_version(_PackDir, Options) :-
2054
+ option(fresh(true), Options),
2055
+ !.
2056
+ git_checkout_version(PackDir, _Options) :-
2057
+ git_current_branch(_, [directory(PackDir)]),
2058
+ !,
2059
+ run_process(path(git), ['-C', PackDir, pull], []).
2060
+ git_checkout_version(PackDir, _Options) :-
2061
+ git_default_branch(Branch, [directory(PackDir)]),
2062
+ git_ensure_on_branch(PackDir, Branch),
2063
+ run_process(path(git), ['-C', PackDir, pull], []).
2064
+
2065
+ %! git_ensure_on_branch(+PackDir, +Branch) is det.
2066
+ %
2067
+ % Ensure PackDir is on Branch.
2068
+
2069
+ git_ensure_on_branch(PackDir, Branch) :-
2070
+ git_current_branch(Branch, [directory(PackDir)]),
2071
+ !.
2072
+ git_ensure_on_branch(PackDir, Branch) :-
2073
+ run_process(path(git), ['-C', PackDir, checkout, Branch], []).
2074
+
2075
+ read_lines_to_atoms(Atoms, In) :-
2076
+ read_line_to_string(In, Line),
2077
+ ( Line == end_of_file
2078
+ -> Atoms = []
2079
+ ; atom_string(Atom, Line),
2080
+ Atoms = [Atom|T],
2081
+ read_lines_to_atoms(T, In)
2082
+ ).
2083
+
2084
+ version_prefix(Prefix) :-
2085
+ atom_codes(Prefix, Codes),
2086
+ phrase(version_prefix, Codes).
2087
+
2088
+ version_prefix -->
2089
+ [C],
2090
+ { code_type(C, alpha) },
2091
+ !,
2092
+ version_prefix.
2093
+ version_prefix -->
2094
+ "-".
2095
+ version_prefix -->
2096
+ "_".
2097
+ version_prefix -->
2098
+ "".
2099
+
2100
+ %! download_file(+URL, +Pack, -File, +Options) is det.
2101
+ %
2102
+ % Determine the file into which to download URL. The second clause
2103
+ % deals with GitHub downloads from a release tag.
2104
+
2105
+ download_file(URL, Pack, File, Options) :-
2106
+ option(version(Version), Options),
2107
+ !,
2108
+ file_name_extension(_, Ext, URL),
2109
+ format(atom(File), '~w-~w.~w', [Pack, Version, Ext]).
2110
+ download_file(URL, Pack, File, _) :-
2111
+ file_base_name(URL,Basename),
2112
+ no_int_file_name_extension(Tag,Ext,Basename),
2113
+ tag_version(Tag,Version),
2114
+ !,
2115
+ format(atom(File0), '~w-~w', [Pack, Version]),
2116
+ file_name_extension(File0, Ext, File).
2117
+ download_file(URL, _, File, _) :-
2118
+ file_base_name(URL, File).
2119
+
2120
+ %! pack_url_file(+URL, -File) is det.
2121
+ %
2122
+ % True if File is a unique id for the referenced pack and version.
2123
+ % Normally, that is simply the base name, but GitHub archives destroy
2124
+ % this picture. Needed by the pack manager in the web server.
2125
+
2126
+ :- public pack_url_file/2.
2127
+ pack_url_file(URL, FileID) :-
2128
+ github_release_url(URL, Pack, Version),
2129
+ !,
2130
+ download_file(URL, Pack, FileID, [version(Version)]).
2131
+ pack_url_file(URL, FileID) :-
2132
+ file_base_name(URL, FileID).
2133
+
2134
+ % ssl_verify(+SSL, +ProblemCert, +AllCerts, +FirstCert, +Error)
2135
+ %
2136
+ % Used if insecure(true) is given to pack_install/2. Accepts any
2137
+ % certificate.
2138
+
2139
+ :- public ssl_verify/5.
2140
+ ssl_verify(_SSL,
2141
+ _ProblemCertificate, _AllCertificates, _FirstCertificate,
2142
+ _Error).
2143
+
2144
+ pack_download_dir(PackTopDir, DownLoadDir) :-
2145
+ directory_file_path(PackTopDir, 'Downloads', DownLoadDir),
2146
+ ( exists_directory(DownLoadDir)
2147
+ -> true
2148
+ ; make_directory(DownLoadDir)
2149
+ ),
2150
+ ( access_file(DownLoadDir, write)
2151
+ -> true
2152
+ ; permission_error(write, directory, DownLoadDir)
2153
+ ).
2154
+
2155
+ %! download_url(@URL) is semidet.
2156
+ %
2157
+ % True if URL looks like a URL we can download from. Noet that urls
2158
+ % like ``ftp://`` are also download URLs, but _we_ cannot download
2159
+ % from them.
2160
+
2161
+ download_url(URL) :-
2162
+ url_scheme(URL, Scheme),
2163
+ download_scheme(Scheme).
2164
+
2165
+ url_scheme(URL, Scheme) :-
2166
+ atom(URL),
2167
+ uri_components(URL, Components),
2168
+ uri_data(scheme, Components, Scheme0),
2169
+ atom(Scheme0),
2170
+ Scheme = Scheme0.
2171
+
2172
+ download_scheme(http).
2173
+ download_scheme(https).
2174
+
2175
+ %! hsts(+URL0, -URL, +Options) is det.
2176
+ %
2177
+ % HSTS (HTTP Strict Transport Security) is standard by which means a
2178
+ % site asks to always use HTTPS. For SWI-Prolog packages we now force
2179
+ % using HTTPS for all downloads. This may be overrules using the
2180
+ % option insecure(true), which may also be used to disable TLS
2181
+ % certificate checking. Note that the pack integrity is still
2182
+ % protected by its SHA1 hash.
2183
+
2184
+ hsts(URL0, URL, Options) :-
2185
+ option(insecure(true), Options, false),
2186
+ !,
2187
+ URL = URL0.
2188
+ hsts(URL0, URL, _Options) :-
2189
+ url_scheme(URL0, http),
2190
+ !,
2191
+ uri_edit(scheme(https), URL0, URL).
2192
+ hsts(URL, URL, _Options).
2193
+
2194
+
2195
+ %! pack_post_install(+Info, +Options) is det.
2196
+ %
2197
+ % Process post installation work. Steps:
2198
+ %
2199
+ % - Create foreign resources
2200
+ % - Register directory as autoload library
2201
+ % - Attach the package
2202
+
2203
+ pack_post_install(Info, Options) :-
2204
+ Pack = Info.pack,
2205
+ PackDir = Info.installed,
2206
+ post_install_foreign(Pack, PackDir, Options),
2207
+ post_install_autoload(Info),
2208
+ pack_attach(PackDir, [duplicate(warning)]).
2209
+
2210
+ %! pack_rebuild is det.
2211
+ %! pack_rebuild(+Pack) is det.
2212
+ %
2213
+ % Rebuild possible foreign components of Pack. The predicate
2214
+ % pack_rebuild/0 rebuilds all registered packs.
2215
+
2216
+ pack_rebuild :-
2217
+ forall(current_pack(Pack),
2218
+ ( print_message(informational, pack(rebuild(Pack))),
2219
+ pack_rebuild(Pack)
2220
+ )).
2221
+
2222
+ pack_rebuild(Pack) :-
2223
+ current_pack(Pack, PackDir),
2224
+ !,
2225
+ post_install_foreign(Pack, PackDir, [rebuild(true)]),
2226
+ pack_attach(PackDir, [duplicate(replace)]).
2227
+ pack_rebuild(Pack) :-
2228
+ unattached_pack(Pack, PackDir),
2229
+ !,
2230
+ post_install_foreign(Pack, PackDir, [rebuild(true)]),
2231
+ pack_attach(PackDir, [duplicate(replace)]).
2232
+ pack_rebuild(Pack) :-
2233
+ existence_error(pack, Pack).
2234
+
2235
+ unattached_pack(Pack, BaseDir) :-
2236
+ directory_file_path(Pack, 'pack.pl', PackFile),
2237
+ absolute_file_name(pack(PackFile), PackPath,
2238
+ [ access(read),
2239
+ file_errors(fail)
2240
+ ]),
2241
+ file_directory_name(PackPath, BaseDir).
2242
+
2243
+
2244
+
2245
+ %! post_install_foreign(+Pack, +PackDir, +Options) is det.
2246
+ %
2247
+ % Install foreign parts of the package. Options:
2248
+ %
2249
+ % - rebuild(When)
2250
+ % Determine when to rebuild. Possible values:
2251
+ % - if_absent
2252
+ % Only rebuild if we have no existing foreign library. This
2253
+ % is the default.
2254
+ % - true
2255
+ % Always rebuild.
2256
+
2257
+ post_install_foreign(Pack, PackDir, Options) :-
2258
+ is_foreign_pack(PackDir, _),
2259
+ !,
2260
+ ( pack_info_term(PackDir, pack_version(Version))
2261
+ -> true
2262
+ ; Version = 1
2263
+ ),
2264
+ option(rebuild(Rebuild), Options, if_absent),
2265
+ current_prolog_flag(arch, Arch),
2266
+ prolog_version_dotted(PrologVersion),
2267
+ ( Rebuild == if_absent,
2268
+ foreign_present(PackDir, Arch)
2269
+ -> print_message(informational, pack(kept_foreign(Pack, Arch))),
2270
+ ( pack_status_dir(PackDir, built(Arch, _, _))
2271
+ -> true
2272
+ ; pack_assert(PackDir, built(Arch, PrologVersion, downloaded))
2273
+ )
2274
+ ; BuildSteps0 = [[dependencies], [configure], build, install, [test]],
2275
+ ( Rebuild == true
2276
+ -> BuildSteps1 = [distclean|BuildSteps0]
2277
+ ; BuildSteps1 = BuildSteps0
2278
+ ),
2279
+ ( option(test(false), Options)
2280
+ -> delete(BuildSteps1, [test], BuildSteps2)
2281
+ ; BuildSteps2 = BuildSteps1
2282
+ ),
2283
+ ( option(clean(true), Options)
2284
+ -> append(BuildSteps2, [[clean]], BuildSteps)
2285
+ ; BuildSteps = BuildSteps2
2286
+ ),
2287
+ build_steps(BuildSteps, PackDir, [pack_version(Version)|Options]),
2288
+ pack_assert(PackDir, built(Arch, PrologVersion, built))
2289
+ ).
2290
+ post_install_foreign(_, _, _).
2291
+
2292
+
2293
+ %! foreign_present(+PackDir, +Arch) is semidet.
2294
+ %
2295
+ % True if we find one or more modules in the pack `lib` directory for
2296
+ % the current architecture.
2297
+ %
2298
+ % @tbd Does not check that these can be loaded, nor whether all
2299
+ % required modules are present.
2300
+
2301
+ foreign_present(PackDir, Arch) :-
2302
+ atomic_list_concat([PackDir, '/lib'], ForeignBaseDir),
2303
+ exists_directory(ForeignBaseDir),
2304
+ !,
2305
+ atomic_list_concat([PackDir, '/lib/', Arch], ForeignDir),
2306
+ exists_directory(ForeignDir),
2307
+ current_prolog_flag(shared_object_extension, Ext),
2308
+ atomic_list_concat([ForeignDir, '/*.', Ext], Pattern),
2309
+ expand_file_name(Pattern, Files),
2310
+ Files \== [].
2311
+
2312
+ %! is_foreign_pack(+PackDir, -Type) is nondet.
2313
+ %
2314
+ % True when PackDir contains files that indicate the need for a
2315
+ % specific class of build tools indicated by Type.
2316
+
2317
+ is_foreign_pack(PackDir, Type) :-
2318
+ foreign_file(File, Type),
2319
+ directory_file_path(PackDir, File, Path),
2320
+ exists_file(Path).
2321
+
2322
+ foreign_file('CMakeLists.txt', cmake).
2323
+ foreign_file('configure', configure).
2324
+ foreign_file('configure.in', autoconf).
2325
+ foreign_file('configure.ac', autoconf).
2326
+ foreign_file('Makefile.am', automake).
2327
+ foreign_file('Makefile', make).
2328
+ foreign_file('makefile', make).
2329
+ foreign_file('conanfile.txt', conan).
2330
+ foreign_file('conanfile.py', conan).
2331
+
2332
+
2333
+ /*******************************
2334
+ * AUTOLOAD *
2335
+ *******************************/
2336
+
2337
+ %! post_install_autoload(+InfoOrList) is det.
2338
+ %
2339
+ % Create an autoload index if the package demands such.
2340
+
2341
+ post_install_autoload(List), is_list(List) =>
2342
+ maplist(post_install_autoload, List).
2343
+ post_install_autoload(Info),
2344
+ _{installed:PackDir, autoload:true} :< Info =>
2345
+ directory_file_path(PackDir, prolog, PrologLibDir),
2346
+ make_library_index(PrologLibDir).
2347
+ post_install_autoload(Info) =>
2348
+ directory_file_path(Info.installed, 'prolog/INDEX.pl', IndexFile),
2349
+ ( exists_file(IndexFile)
2350
+ -> E = error(_,_),
2351
+ print_message(warning, pack(delete_autoload_index(Info.pack, IndexFile))),
2352
+ catch(delete_file(IndexFile), E,
2353
+ print_message(warning, E))
2354
+ ; true
2355
+ ).
2356
+
2357
+ %! decide_autoload_pack(+Options, +Info0, -Info) is det.
2358
+ %
2359
+ % Add autoload:true to Info if the pack needs to be configured for
2360
+ % autoloading.
2361
+
2362
+ decide_autoload_pack(Options, Info0, Info) :-
2363
+ is_autoload_pack(Info0.pack, Info0.installed, Options),
2364
+ !,
2365
+ Info = Info0.put(autoload, true).
2366
+ decide_autoload_pack(_, Info, Info).
2367
+
2368
+ is_autoload_pack(_Pack, _PackDir, Options) :-
2369
+ option(autoload(true), Options),
2370
+ !.
2371
+ is_autoload_pack(Pack, PackDir, Options) :-
2372
+ pack_info_term(PackDir, autoload(true)),
2373
+ confirm(autoload(Pack), no, Options).
2374
+
2375
+
2376
+ /*******************************
2377
+ * UPGRADE *
2378
+ *******************************/
2379
+
2380
+ %! pack_upgrade(+Pack) is semidet.
2381
+ %
2382
+ % Upgrade Pack. Shorthand for pack_install(Pack, [upgrade(true)]).
2383
+
2384
+ pack_upgrade(Pack) :-
2385
+ pack_install(Pack, [upgrade(true)]).
2386
+
2387
+
2388
+ /*******************************
2389
+ * REMOVE *
2390
+ *******************************/
2391
+
2392
+ %! pack_remove(+Name) is det.
2393
+ %! pack_remove(+Name, +Options) is det.
2394
+ %
2395
+ % Remove the indicated package. If packages depend (indirectly) on
2396
+ % this pack, ask to remove these as well. Options:
2397
+ %
2398
+ % - interactive(false)
2399
+ % Do not prompt the user.
2400
+ % - dependencies(Boolean)
2401
+ % If `true` delete dependencies without asking.
2402
+
2403
+ pack_remove(Pack) :-
2404
+ pack_remove(Pack, []).
2405
+
2406
+ pack_remove(Pack, Options) :-
2407
+ option(dependencies(false), Options),
2408
+ !,
2409
+ pack_remove_forced(Pack).
2410
+ pack_remove(Pack, Options) :-
2411
+ ( dependents(Pack, Deps)
2412
+ -> ( option(dependencies(true), Options)
2413
+ -> true
2414
+ ; confirm_remove(Pack, Deps, Delete, Options)
2415
+ ),
2416
+ forall(member(P, Delete), pack_remove_forced(P))
2417
+ ; pack_remove_forced(Pack)
2418
+ ).
2419
+
2420
+ pack_remove_forced(Pack) :-
2421
+ catch('$pack_detach'(Pack, BaseDir),
2422
+ error(existence_error(pack, Pack), _),
2423
+ fail),
2424
+ !,
2425
+ ( read_link(BaseDir, _, Target)
2426
+ -> What = link(Target)
2427
+ ; What = directory
2428
+ ),
2429
+ print_message(informational, pack(remove(What, BaseDir))),
2430
+ delete_directory_and_contents(BaseDir).
2431
+ pack_remove_forced(Pack) :-
2432
+ unattached_pack(Pack, BaseDir),
2433
+ !,
2434
+ delete_directory_and_contents(BaseDir).
2435
+ pack_remove_forced(Pack) :-
2436
+ print_message(informational, error(existence_error(pack, Pack),_)).
2437
+
2438
+ confirm_remove(Pack, Deps, Delete, Options) :-
2439
+ print_message(warning, pack(depends(Pack, Deps))),
2440
+ menu(pack(resolve_remove),
2441
+ [ [Pack] = remove_only(Pack),
2442
+ [Pack|Deps] = remove_deps(Pack, Deps),
2443
+ [] = cancel
2444
+ ], [], Delete, Options),
2445
+ Delete \== [].
2446
+
2447
+
2448
+ /*******************************
2449
+ * PUBLISH *
2450
+ *******************************/
2451
+
2452
+ %! pack_publish(+Spec, +Options) is det.
2453
+ %
2454
+ % Publish a package. There are two ways typical ways to call this. We
2455
+ % recommend developing a pack in a GIT repository. In this scenario
2456
+ % the pack can be published using
2457
+ %
2458
+ % ?- pack_publish('.', []).
2459
+ %
2460
+ % Alternatively, an archive file has been uploaded to a public
2461
+ % location. In this scenario we can publish the pack using
2462
+ %
2463
+ % ?- pack_publish(URL, [])
2464
+ %
2465
+ % In both scenarios, pack_publish/2 by default creates an isolated
2466
+ % environment and installs the package in this directory from the
2467
+ % public URL. On success it triggers the pack server to register the
2468
+ % URL as a new pack or a new release of a pack.
2469
+ %
2470
+ % Packs may also be published using the _app_ `pack`, e.g.
2471
+ %
2472
+ % swipl pack publish .
2473
+ %
2474
+ % Options:
2475
+ %
2476
+ % - git(Boolean)
2477
+ % If `true`, and Spec is a git managed directory, install using
2478
+ % the remote repo.
2479
+ % - sign(Boolean)
2480
+ % Sign the repository with the current version. This runs
2481
+ % ``git tag -s <tag>``.
2482
+ % - force(Boolean)
2483
+ % Force the git tag. This runs ``git tag -f <tag>``.
2484
+ % - branch(+Branch)
2485
+ % Branch used for releases. Defined by git_default_branch/2
2486
+ % if not specified.
2487
+ % - register(+Boolean)
2488
+ % If `false` (default `true`), perform the installation, but do
2489
+ % not upload to the server. This can be used for testing.
2490
+ % - isolated(+Boolean)
2491
+ % If `true` (default), install and build all packages in an
2492
+ % isolated package directory. If `false`, use other packages
2493
+ % installed for the environment. The latter may be used to
2494
+ % speedup debugging.
2495
+ % - pack_directory(+Dir)
2496
+ % Install the temporary packages in Dir. If omitted pack_publish/2
2497
+ % creates a temporary directory and deletes this directory after
2498
+ % completion. An explict target Dir is created if it does not
2499
+ % exist and is not deleted on completion.
2500
+ % - clean(+Boolean)
2501
+ % If `true` (default), clean the destination directory first
2502
+
2503
+ pack_publish(Dir, Options) :-
2504
+ \+ download_url(Dir),
2505
+ is_git_directory(Dir), !,
2506
+ pack_git_info(Dir, _Hash, Metadata),
2507
+ prepare_repository(Dir, Metadata, Options),
2508
+ ( memberchk(download(URL), Metadata),
2509
+ git_url(URL, _)
2510
+ -> true
2511
+ ; option(remote(Remote), Options, origin),
2512
+ git_remote_url(Remote, RemoteURL, [directory(Dir)]),
2513
+ git_to_https_url(RemoteURL, URL)
2514
+ ),
2515
+ memberchk(version(Version), Metadata),
2516
+ pack_publish_(URL,
2517
+ [ version(Version)
2518
+ | Options
2519
+ ]).
2520
+ pack_publish(Spec, Options) :-
2521
+ pack_publish_(Spec, Options).
2522
+
2523
+ pack_publish_(Spec, Options) :-
2524
+ pack_default_options(Spec, Pack, Options, DefOptions),
2525
+ option(url(URL), DefOptions),
2526
+ valid_publish_url(URL, Options),
2527
+ prepare_build_location(Pack, Dir, Clean, Options),
2528
+ ( option(register(false), Options)
2529
+ -> InstallOptions = DefOptions
2530
+ ; InstallOptions = [publish(Pack)|DefOptions]
2531
+ ),
2532
+ call_cleanup(pack_install(Pack,
2533
+ [ pack(Pack)
2534
+ | InstallOptions
2535
+ ]),
2536
+ cleanup_publish(Clean, Dir)).
2537
+
2538
+ cleanup_publish(true, Dir) :-
2539
+ !,
2540
+ delete_directory_and_contents(Dir).
2541
+ cleanup_publish(_, _).
2542
+
2543
+ valid_publish_url(URL, Options) :-
2544
+ option(register(Register), Options, true),
2545
+ ( Register == false
2546
+ -> true
2547
+ ; download_url(URL)
2548
+ -> true
2549
+ ; permission_error(publish, pack, URL)
2550
+ ).
2551
+
2552
+ prepare_build_location(Pack, Dir, Clean, Options) :-
2553
+ ( option(pack_directory(Dir), Options)
2554
+ -> ensure_directory(Dir),
2555
+ ( option(clean(true), Options, true)
2556
+ -> delete_directory_contents(Dir)
2557
+ ; true
2558
+ )
2559
+ ; tmp_file(pack, Dir),
2560
+ make_directory(Dir),
2561
+ Clean = true
2562
+ ),
2563
+ ( option(isolated(false), Options)
2564
+ -> detach_pack(Pack, _),
2565
+ attach_packs(Dir, [search(first)])
2566
+ ; attach_packs(Dir, [replace(true)])
2567
+ ).
2568
+
2569
+
2570
+
2571
+ %! prepare_repository(+Dir, +Metadata, +Options) is semidet.
2572
+ %
2573
+ % Prepare the git repository. If register(false) is provided, this is
2574
+ % a test run and therefore we do not need this. Otherwise we demand
2575
+ % the working directory to be clean, we tag the current commit and
2576
+ % push the current branch.
2577
+
2578
+ prepare_repository(_Dir, _Metadata, Options) :-
2579
+ option(register(false), Options),
2580
+ !.
2581
+ prepare_repository(Dir, Metadata, Options) :-
2582
+ git_dir_must_be_clean(Dir),
2583
+ git_must_be_on_default_branch(Dir, Options),
2584
+ tag_git_dir(Dir, Metadata, Action, Options),
2585
+ confirm(git_push, yes, Options),
2586
+ run_process(path(git), ['-C', file(Dir), push ], []),
2587
+ ( Action = push_tag(Tag)
2588
+ -> run_process(path(git), ['-C', file(Dir), push, origin, Tag ], [])
2589
+ ; true
2590
+ ).
2591
+
2592
+ git_dir_must_be_clean(Dir) :-
2593
+ git_describe(Description, [directory(Dir)]),
2594
+ ( sub_atom(Description, _, _, 0, '-DIRTY')
2595
+ -> print_message(error, pack(git_not_clean(Dir))),
2596
+ fail
2597
+ ; true
2598
+ ).
2599
+
2600
+ git_must_be_on_default_branch(Dir, Options) :-
2601
+ ( option(branch(Default), Options)
2602
+ -> true
2603
+ ; git_default_branch(Default, [directory(Dir)])
2604
+ ),
2605
+ git_current_branch(Current, [directory(Dir)]),
2606
+ ( Default == Current
2607
+ -> true
2608
+ ; print_message(error,
2609
+ pack(git_branch_not_default(Dir, Default, Current))),
2610
+ fail
2611
+ ).
2612
+
2613
+
2614
+ %! tag_git_dir(+Dir, +Metadata, -Action, +Options) is semidet.
2615
+ %
2616
+ % Add a version tag to the git repository.
2617
+ %
2618
+ % @arg Action is one of push_tag(Tag) or `none`
2619
+
2620
+ tag_git_dir(Dir, Metadata, Action, Options) :-
2621
+ memberchk(version(Version), Metadata),
2622
+ atom_concat('V', Version, Tag),
2623
+ git_tags(Tags, [directory(Dir)]),
2624
+ ( memberchk(Tag, Tags)
2625
+ -> git_tag_is_consistent(Dir, Tag, Action, Options)
2626
+ ; format(string(Message), 'Release ~w', [Version]),
2627
+ findall(Opt, git_tag_option(Opt, Options), Argv,
2628
+ [ '-m', Message, Tag ]),
2629
+ confirm(git_tag(Tag), yes, Options),
2630
+ run_process(path(git), ['-C', file(Dir), tag | Argv ], []),
2631
+ Action = push_tag(Tag)
2632
+ ).
2633
+
2634
+ git_tag_option('-s', Options) :- option(sign(true), Options, true).
2635
+ git_tag_option('-f', Options) :- option(force(true), Options, true).
2636
+
2637
+ git_tag_is_consistent(Dir, Tag, Action, Options) :-
2638
+ format(atom(TagRef), 'refs/tags/~w', [Tag]),
2639
+ format(atom(CommitRef), 'refs/tags/~w^{}', [Tag]),
2640
+ option(remote(Remote), Options, origin),
2641
+ git_ls_remote(Dir, LocalTags, [tags(true)]),
2642
+ memberchk(CommitHash-CommitRef, LocalTags),
2643
+ ( git_hash(CommitHash, [directory(Dir)])
2644
+ -> true
2645
+ ; print_message(error, pack(git_release_tag_not_at_head(Tag))),
2646
+ fail
2647
+ ),
2648
+ memberchk(TagHash-TagRef, LocalTags),
2649
+ git_ls_remote(Remote, RemoteTags, [tags(true)]),
2650
+ ( memberchk(RemoteCommitHash-CommitRef, RemoteTags),
2651
+ memberchk(RemoteTagHash-TagRef, RemoteTags)
2652
+ -> ( RemoteCommitHash == CommitHash,
2653
+ RemoteTagHash == TagHash
2654
+ -> Action = none
2655
+ ; print_message(error, pack(git_tag_out_of_sync(Tag))),
2656
+ fail
2657
+ )
2658
+ ; Action = push_tag(Tag)
2659
+ ).
2660
+
2661
+ %! git_to_https_url(+GitURL, -HTTP_URL) is semidet.
2662
+ %
2663
+ % Get the HTTP(s) URL for a git repository, given a git url.
2664
+ % Whether or not this is available and how to translate the
2665
+ % one into the other depends in the server software.
2666
+
2667
+ git_to_https_url(URL, URL) :-
2668
+ download_url(URL),
2669
+ !.
2670
+ git_to_https_url(GitURL, URL) :-
2671
+ atom_concat('git@github.com:', Repo, GitURL),
2672
+ !,
2673
+ atom_concat('https://github.com/', Repo, URL).
2674
+ git_to_https_url(GitURL, _) :-
2675
+ print_message(error, pack(git_no_https(GitURL))),
2676
+ fail.
2677
+
2678
+
2679
+ /*******************************
2680
+ * PROPERTIES *
2681
+ *******************************/
2682
+
2683
+ %! pack_property(?Pack, ?Property) is nondet.
2684
+ %
2685
+ % True when Property is a property of an installed Pack. This
2686
+ % interface is intended for programs that wish to interact with the
2687
+ % package manager. Defined properties are:
2688
+ %
2689
+ % - directory(Directory)
2690
+ % Directory into which the package is installed
2691
+ % - version(Version)
2692
+ % Installed version
2693
+ % - title(Title)
2694
+ % Full title of the package
2695
+ % - author(Author)
2696
+ % Registered author
2697
+ % - download(URL)
2698
+ % Official download URL
2699
+ % - readme(File)
2700
+ % Package README file (if present)
2701
+ % - todo(File)
2702
+ % Package TODO file (if present)
2703
+
2704
+ pack_property(Pack, Property) :-
2705
+ findall(Pack-Property, pack_property_(Pack, Property), List),
2706
+ member(Pack-Property, List). % make det if applicable
2707
+
2708
+ pack_property_(Pack, Property) :-
2709
+ pack_info(Pack, _, Property).
2710
+ pack_property_(Pack, Property) :-
2711
+ \+ \+ info_file(Property, _),
2712
+ '$pack':pack(Pack, BaseDir),
2713
+ access_file(BaseDir, read),
2714
+ directory_files(BaseDir, Files),
2715
+ member(File, Files),
2716
+ info_file(Property, Pattern),
2717
+ downcase_atom(File, Pattern),
2718
+ directory_file_path(BaseDir, File, InfoFile),
2719
+ arg(1, Property, InfoFile).
2720
+
2721
+ info_file(readme(_), 'readme.txt').
2722
+ info_file(readme(_), 'readme').
2723
+ info_file(todo(_), 'todo.txt').
2724
+ info_file(todo(_), 'todo').
2725
+
2726
+
2727
+ /*******************************
2728
+ * VERSION LOGIC *
2729
+ *******************************/
2730
+
2731
+ %! pack_version_file(-Pack, -Version:atom, +File) is semidet.
2732
+ %
2733
+ % True if File is the name of a file or URL of a file that
2734
+ % contains Pack at Version. File must have an extension and the
2735
+ % basename must be of the form <pack>-<n>{.<m>}*. E.g.,
2736
+ % =|mypack-1.5|=.
2737
+
2738
+ pack_version_file(Pack, Version, GitHubRelease) :-
2739
+ atomic(GitHubRelease),
2740
+ github_release_url(GitHubRelease, Pack, Version),
2741
+ !.
2742
+ pack_version_file(Pack, Version, Path) :-
2743
+ atomic(Path),
2744
+ file_base_name(Path, File),
2745
+ no_int_file_name_extension(Base, _Ext, File),
2746
+ atom_codes(Base, Codes),
2747
+ ( phrase(pack_version(Pack, Version), Codes),
2748
+ safe_pack_name(Pack)
2749
+ -> true
2750
+ ).
2751
+
2752
+ no_int_file_name_extension(Base, Ext, File) :-
2753
+ file_name_extension(Base0, Ext0, File),
2754
+ \+ atom_number(Ext0, _),
2755
+ !,
2756
+ Base = Base0,
2757
+ Ext = Ext0.
2758
+ no_int_file_name_extension(File, '', File).
2759
+
2760
+ %! safe_pack_name(+Name:atom) is semidet.
2761
+ %
2762
+ % Verifies that Name is a valid pack name. This avoids trickery
2763
+ % with pack file names to make shell commands behave unexpectly.
2764
+
2765
+ safe_pack_name(Name) :-
2766
+ atom_length(Name, Len),
2767
+ Len >= 3, % demand at least three length
2768
+ atom_codes(Name, Codes),
2769
+ maplist(safe_pack_char, Codes),
2770
+ !.
2771
+
2772
+ safe_pack_char(C) :- between(0'a, 0'z, C), !.
2773
+ safe_pack_char(C) :- between(0'A, 0'Z, C), !.
2774
+ safe_pack_char(C) :- between(0'0, 0'9, C), !.
2775
+ safe_pack_char(0'_).
2776
+
2777
+ %! pack_version(-Pack:atom, -Version:atom)// is semidet.
2778
+ %
2779
+ % True when the input statifies <pack>-<version>
2780
+
2781
+ pack_version(Pack, Version) -->
2782
+ string(Codes), "-",
2783
+ version(Parts),
2784
+ !,
2785
+ { atom_codes(Pack, Codes),
2786
+ atomic_list_concat(Parts, '.', Version)
2787
+ }.
2788
+
2789
+ version([H|T]) -->
2790
+ version_part(H),
2791
+ ( "."
2792
+ -> version(T)
2793
+ ; {T=[]}
2794
+ ).
2795
+
2796
+ version_part(*) --> "*", !.
2797
+ version_part(Int) --> integer(Int).
2798
+
2799
+
2800
+ /*******************************
2801
+ * GIT LOGIC *
2802
+ *******************************/
2803
+
2804
+ %! have_git is semidet.
2805
+ %
2806
+ % True if we have the `git` program. This could be simple, but Apple
2807
+ % decided to include a fake `/usr/bin/git` that triggers the Xcode
2808
+ % installation. So, if we find `git` at `/usr/bin/git` we should check
2809
+ % that Xcode is properly enabled. This is the case if ``xcode-select
2810
+ % -p`` points at an Xcode installation. Note that if we find `git` at
2811
+ % some other location, we assume it is installed by the user,
2812
+ % Macports, Homebrew or something else.
2813
+
2814
+ have_git :-
2815
+ process_which(path(git), GIT),
2816
+ is_sane_git(GIT).
2817
+
2818
+ :- if(current_prolog_flag(apple, true)).
2819
+ sane_xcode_path -->
2820
+ "Xcode.app/Contents".
2821
+ sane_xcode_path -->
2822
+ "CommandLineTools".
2823
+
2824
+ is_sane_git('/usr/bin/git') :-
2825
+ !,
2826
+ process_which(path('xcode-select'), XSpath),
2827
+ catch(run_process(XSpath,['-p'],[output(Output),error(_)]), error(_,_), fail),
2828
+ once(phrase((string(_), sane_xcode_path), Output, _)).
2829
+ :- endif.
2830
+ is_sane_git(_).
2831
+
2832
+ %! git_url(+URL, -Pack) is semidet.
2833
+ %
2834
+ % True if URL describes a git url for Pack
2835
+
2836
+ git_url(URL, Pack) :-
2837
+ uri_components(URL, Components),
2838
+ uri_data(scheme, Components, Scheme),
2839
+ nonvar(Scheme), % must be full URL
2840
+ uri_data(path, Components, Path),
2841
+ ( Scheme == git
2842
+ -> true
2843
+ ; git_download_scheme(Scheme),
2844
+ file_name_extension(_, git, Path)
2845
+ ; git_download_scheme(Scheme),
2846
+ catch(git_ls_remote(URL, _, [refs(['HEAD']), error(_)]), _, fail)
2847
+ -> true
2848
+ ),
2849
+ file_base_name(Path, PackExt),
2850
+ ( file_name_extension(Pack, git, PackExt)
2851
+ -> true
2852
+ ; Pack = PackExt
2853
+ ),
2854
+ ( safe_pack_name(Pack)
2855
+ -> true
2856
+ ; domain_error(pack_name, Pack)
2857
+ ).
2858
+
2859
+ git_download_scheme(http).
2860
+ git_download_scheme(https).
2861
+
2862
+ %! github_release_url(+URL, -Pack, -Version:atom) is semidet.
2863
+ %
2864
+ % True when URL is the URL of a GitHub release. Such releases are
2865
+ % accessible as
2866
+ %
2867
+ % https:/github.com/<owner>/<pack>/archive/[vV]?<version>.zip'
2868
+
2869
+ github_release_url(URL, Pack, Version) :-
2870
+ uri_components(URL, Components),
2871
+ uri_data(authority, Components, 'github.com'),
2872
+ uri_data(scheme, Components, Scheme),
2873
+ download_scheme(Scheme),
2874
+ uri_data(path, Components, Path),
2875
+ github_archive_path(Archive,Pack,File),
2876
+ atomic_list_concat(Archive, /, Path),
2877
+ file_name_extension(Tag, Ext, File),
2878
+ github_archive_extension(Ext),
2879
+ tag_version(Tag, Version),
2880
+ !.
2881
+
2882
+ github_archive_path(['',_User,Pack,archive,File],Pack,File).
2883
+ github_archive_path(['',_User,Pack,archive,refs,tags,File],Pack,File).
2884
+
2885
+ github_archive_extension(tgz).
2886
+ github_archive_extension(zip).
2887
+
2888
+ %! tag_version(+GitTag, -Version) is semidet.
2889
+ %
2890
+ % True when a GIT tag describes version Version. GitTag must
2891
+ % satisfy ``[vV]?int(\.int)*``.
2892
+
2893
+ tag_version(Tag, Version) :-
2894
+ version_tag_prefix(Prefix),
2895
+ atom_concat(Prefix, Version, Tag),
2896
+ is_version(Version).
2897
+
2898
+ version_tag_prefix(v).
2899
+ version_tag_prefix('V').
2900
+ version_tag_prefix('').
2901
+
2902
+
2903
+ %! git_archive_url(+URL, -Archive, +Options) is semidet.
2904
+ %
2905
+ % If we do not have git installed, some git services offer downloading
2906
+ % the code as an archive using HTTP. This predicate makes this
2907
+ % translation.
2908
+
2909
+ git_archive_url(URL, Archive, Options) :-
2910
+ uri_components(URL, Components),
2911
+ uri_data(authority, Components, 'github.com'),
2912
+ uri_data(path, Components, Path),
2913
+ atomic_list_concat(['', User, RepoGit], /, Path),
2914
+ $,
2915
+ remove_git_ext(RepoGit, Repo),
2916
+ git_archive_version(Version, Options),
2917
+ atomic_list_concat(['', User, Repo, zip, Version], /, ArchivePath),
2918
+ uri_edit([ path(ArchivePath),
2919
+ host('codeload.github.com')
2920
+ ],
2921
+ URL, Archive).
2922
+ git_archive_url(URL, _, _) :-
2923
+ print_message(error, pack(no_git(URL))),
2924
+ fail.
2925
+
2926
+ remove_git_ext(RepoGit, Repo) :-
2927
+ file_name_extension(Repo, git, RepoGit),
2928
+ !.
2929
+ remove_git_ext(Repo, Repo).
2930
+
2931
+ git_archive_version(Version, Options) :-
2932
+ option(commit(Version), Options),
2933
+ !.
2934
+ git_archive_version(Version, Options) :-
2935
+ option(branch(Version), Options),
2936
+ !.
2937
+ git_archive_version(Version, Options) :-
2938
+ option(version(Version), Options),
2939
+ !.
2940
+ git_archive_version('HEAD', _).
2941
+
2942
+ /*******************************
2943
+ * QUERY CENTRAL DB *
2944
+ *******************************/
2945
+
2946
+ %! publish_download(+Infos, +Options) is semidet.
2947
+ %! register_downloads(+Infos, +Options) is det.
2948
+ %
2949
+ % Register our downloads with the pack server. The publish_download/2
2950
+ % version is used to register a specific pack after successfully
2951
+ % installing the pack. In this scenario, we
2952
+ %
2953
+ % 1. call register_downloads/2 with publish(Pack) that must be
2954
+ % a no-op.
2955
+ % 2. build and test the pack
2956
+ % 3. call publish_download/2, which calls register_downloads/2
2957
+ % after replacing publish(Pack) by do_publish(Pack).
2958
+
2959
+ register_downloads(_, Options) :-
2960
+ option(register(false), Options),
2961
+ !.
2962
+ register_downloads(_, Options) :-
2963
+ option(publish(_), Options),
2964
+ !.
2965
+ register_downloads(Infos, Options) :-
2966
+ convlist(download_data, Infos, Data),
2967
+ ( Data == []
2968
+ -> true
2969
+ ; query_pack_server(downloaded(Data), Reply, Options),
2970
+ ( option(do_publish(Pack), Options)
2971
+ -> ( member(Info, Infos),
2972
+ Info.pack == Pack
2973
+ -> true
2974
+ ),
2975
+ ( Reply = true(Actions),
2976
+ memberchk(Pack-Result, Actions)
2977
+ -> ( registered(Result)
2978
+ -> print_message(informational, pack(published(Info, Result)))
2979
+ ; print_message(error, pack(publish_failed(Info, Result))),
2980
+ fail
2981
+ )
2982
+ ; print_message(error, pack(publish_failed(Info, false)))
2983
+ )
2984
+ ; true
2985
+ )
2986
+ ).
2987
+
2988
+ registered(git(_URL)).
2989
+ registered(file(_URL)).
2990
+
2991
+ publish_download(Infos, Options) :-
2992
+ select_option(publish(Pack), Options, Options1),
2993
+ !,
2994
+ register_downloads(Infos, [do_publish(Pack)|Options1]).
2995
+ publish_download(_Infos, _Options).
2996
+
2997
+ %! download_data(+Info, -Data) is semidet.
2998
+ %
2999
+ % If we downloaded and installed Info, unify Data with the information
3000
+ % that we share with the pack registry. That is a term
3001
+ %
3002
+ % download(URL, Hash, Metadata).
3003
+ %
3004
+ % Where URL is location of the GIT repository or URL of the download
3005
+ % archive. Hash is either the GIT commit hash or the SHA1 of the
3006
+ % archive file.
3007
+
3008
+ download_data(Info, Data),
3009
+ Info.get(git) == true => % Git clone
3010
+ Data = download(URL, Hash, Metadata),
3011
+ URL = Info.get(downloaded),
3012
+ pack_git_info(Info.installed, Hash, Metadata).
3013
+ download_data(Info, Data),
3014
+ _{git_url:URL,hash:Hash} :< Info, Hash \== (-) =>
3015
+ Data = download(URL, Hash, Metadata), % Git downloaded as zip
3016
+ dir_metadata(Info.installed, Metadata).
3017
+ download_data(Info, Data) => % Archive download.
3018
+ Data = download(URL, Hash, Metadata),
3019
+ URL = Info.get(downloaded),
3020
+ download_url(URL),
3021
+ pack_status_dir(Info.installed, archive(Archive, URL)),
3022
+ file_sha1(Archive, Hash),
3023
+ pack_archive_info(Archive, _Pack, Metadata, _).
3024
+
3025
+ %! query_pack_server(+Query, -Result, +Options)
3026
+ %
3027
+ % Send a Prolog query to the package server and process its
3028
+ % results.
3029
+
3030
+ query_pack_server(Query, Result, Options) :-
3031
+ ( option(server(ServerOpt), Options)
3032
+ -> server_url(ServerOpt, ServerBase)
3033
+ ; setting(server, ServerBase),
3034
+ ServerBase \== ''
3035
+ ),
3036
+ atom_concat(ServerBase, query, Server),
3037
+ format(codes(Data), '~q.~n', Query),
3038
+ info_level(Informational, Options),
3039
+ print_message(Informational, pack(contacting_server(Server))),
3040
+ setup_call_cleanup(
3041
+ http_open(Server, In,
3042
+ [ post(codes(application/'x-prolog', Data)),
3043
+ header(content_type, ContentType)
3044
+ ]),
3045
+ read_reply(ContentType, In, Result),
3046
+ close(In)),
3047
+ message_severity(Result, Level, Informational),
3048
+ print_message(Level, pack(server_reply(Result))).
3049
+
3050
+ server_url(URL0, URL) :-
3051
+ uri_components(URL0, Components),
3052
+ uri_data(scheme, Components, Scheme),
3053
+ var(Scheme),
3054
+ !,
3055
+ atom_concat('https://', URL0, URL1),
3056
+ server_url(URL1, URL).
3057
+ server_url(URL0, URL) :-
3058
+ uri_components(URL0, Components),
3059
+ uri_data(path, Components, ''),
3060
+ !,
3061
+ uri_edit([path('/pack/')], URL0, URL).
3062
+ server_url(URL, URL).
3063
+
3064
+ read_reply(ContentType, In, Result) :-
3065
+ sub_atom(ContentType, 0, _, _, 'application/x-prolog'),
3066
+ !,
3067
+ set_stream(In, encoding(utf8)),
3068
+ read(In, Result).
3069
+ read_reply(ContentType, In, _Result) :-
3070
+ read_string(In, 500, String),
3071
+ print_message(error, pack(no_prolog_response(ContentType, String))),
3072
+ fail.
3073
+
3074
+ info_level(Level, Options) :-
3075
+ option(silent(true), Options),
3076
+ !,
3077
+ Level = silent.
3078
+ info_level(informational, _).
3079
+
3080
+ message_severity(true(_), Informational, Informational).
3081
+ message_severity(false, warning, _).
3082
+ message_severity(exception(_), error, _).
3083
+
3084
+
3085
+ /*******************************
3086
+ * WILDCARD URIs *
3087
+ *******************************/
3088
+
3089
+ %! available_download_versions(+URL, -Versions:list(atom), +Options) is det.
3090
+ %
3091
+ % Deal with wildcard URLs, returning a list of Version-URL pairs,
3092
+ % sorted by version.
3093
+ %
3094
+ % @tbd Deal with protocols other than HTTP
3095
+
3096
+ available_download_versions(URL, Versions, _Options) :-
3097
+ wildcard_pattern(URL),
3098
+ github_url(URL, User, Repo), % demands https
3099
+ !,
3100
+ findall(Version-VersionURL,
3101
+ github_version(User, Repo, Version, VersionURL),
3102
+ Versions).
3103
+ available_download_versions(URL0, Versions, Options) :-
3104
+ wildcard_pattern(URL0),
3105
+ !,
3106
+ hsts(URL0, URL, Options),
3107
+ file_directory_name(URL, DirURL0),
3108
+ ensure_slash(DirURL0, DirURL),
3109
+ print_message(informational, pack(query_versions(DirURL))),
3110
+ setup_call_cleanup(
3111
+ http_open(DirURL, In, []),
3112
+ load_html(stream(In), DOM,
3113
+ [ syntax_errors(quiet)
3114
+ ]),
3115
+ close(In)),
3116
+ findall(MatchingURL,
3117
+ absolute_matching_href(DOM, URL, MatchingURL),
3118
+ MatchingURLs),
3119
+ ( MatchingURLs == []
3120
+ -> print_message(warning, pack(no_matching_urls(URL)))
3121
+ ; true
3122
+ ),
3123
+ versioned_urls(MatchingURLs, VersionedURLs),
3124
+ sort_version_pairs(VersionedURLs, Versions),
3125
+ print_message(informational, pack(found_versions(Versions))).
3126
+ available_download_versions(URL, [Version-URL], _Options) :-
3127
+ ( pack_version_file(_Pack, Version0, URL)
3128
+ -> Version = Version0
3129
+ ; Version = '0.0.0'
3130
+ ).
3131
+
3132
+ %! sort_version_pairs(+Pairs, -Sorted) is det.
3133
+ %
3134
+ % Sort a list of Version-Data by decreasing version.
3135
+
3136
+ sort_version_pairs(Pairs, Sorted) :-
3137
+ map_list_to_pairs(version_pair_sort_key_, Pairs, Keyed),
3138
+ sort(1, @>=, Keyed, SortedKeyed),
3139
+ pairs_values(SortedKeyed, Sorted).
3140
+
3141
+ version_pair_sort_key_(Version-_Data, Key) :-
3142
+ version_sort_key(Version, Key).
3143
+
3144
+ version_sort_key(Version, Key) :-
3145
+ split_string(Version, ".", "", Parts),
3146
+ maplist(number_string, Key, Parts),
3147
+ !.
3148
+ version_sort_key(Version, _) :-
3149
+ domain_error(version, Version).
3150
+
3151
+ %! github_url(+URL, -User, -Repo) is semidet.
3152
+ %
3153
+ % True when URL refers to a github repository.
3154
+
3155
+ github_url(URL, User, Repo) :-
3156
+ uri_components(URL, uri_components(https,'github.com',Path,_,_)),
3157
+ atomic_list_concat(['',User,Repo|_], /, Path).
3158
+
3159
+
3160
+ %! github_version(+User, +Repo, -Version, -VersionURI) is nondet.
3161
+ %
3162
+ % True when Version is a release version and VersionURI is the
3163
+ % download location for the zip file.
3164
+
3165
+ github_version(User, Repo, Version, VersionURI) :-
3166
+ atomic_list_concat(['',repos,User,Repo,tags], /, Path1),
3167
+ uri_components(ApiUri, uri_components(https,'api.github.com',Path1,_,_)),
3168
+ setup_call_cleanup(
3169
+ http_open(ApiUri, In,
3170
+ [ request_header('Accept'='application/vnd.github.v3+json')
3171
+ ]),
3172
+ json_read_dict(In, Dicts),
3173
+ close(In)),
3174
+ member(Dict, Dicts),
3175
+ atom_string(Tag, Dict.name),
3176
+ tag_version(Tag, Version),
3177
+ atom_string(VersionURI, Dict.zipball_url).
3178
+
3179
+ wildcard_pattern(URL) :- sub_atom(URL, _, _, _, *).
3180
+ wildcard_pattern(URL) :- sub_atom(URL, _, _, _, ?).
3181
+
3182
+ ensure_slash(Dir, DirS) :-
3183
+ ( sub_atom(Dir, _, _, 0, /)
3184
+ -> DirS = Dir
3185
+ ; atom_concat(Dir, /, DirS)
3186
+ ).
3187
+
3188
+ remove_slash(Dir0, Dir) :-
3189
+ Dir0 \== '/',
3190
+ atom_concat(Dir1, /, Dir0),
3191
+ !,
3192
+ remove_slash(Dir1, Dir).
3193
+ remove_slash(Dir, Dir).
3194
+
3195
+ absolute_matching_href(DOM, Pattern, Match) :-
3196
+ xpath(DOM, //a(@href), HREF),
3197
+ uri_normalized(HREF, Pattern, Match),
3198
+ wildcard_match(Pattern, Match).
3199
+
3200
+ versioned_urls([], []).
3201
+ versioned_urls([H|T0], List) :-
3202
+ file_base_name(H, File),
3203
+ ( pack_version_file(_Pack, Version, File)
3204
+ -> List = [Version-H|T]
3205
+ ; List = T
3206
+ ),
3207
+ versioned_urls(T0, T).
3208
+
3209
+
3210
+ /*******************************
3211
+ * DEPENDENCIES *
3212
+ *******************************/
3213
+
3214
+ %! pack_provides(?Pack, -Provides) is multi.
3215
+ %! pack_requires(?Pack, -Requires) is nondet.
3216
+ %! pack_conflicts(?Pack, -Conflicts) is nondet.
3217
+ %
3218
+ % Provide logical access to pack dependency relations.
3219
+
3220
+ pack_provides(Pack, Pack@Version) :-
3221
+ current_pack(Pack),
3222
+ once(pack_info(Pack, version, version(Version))).
3223
+ pack_provides(Pack, Provides) :-
3224
+ findall(Prv, pack_info(Pack, dependency, provides(Prv)), PrvList),
3225
+ member(Provides, PrvList).
3226
+
3227
+ pack_requires(Pack, Requires) :-
3228
+ current_pack(Pack),
3229
+ findall(Req, pack_info(Pack, dependency, requires(Req)), ReqList),
3230
+ member(Requires, ReqList).
3231
+
3232
+ pack_conflicts(Pack, Conflicts) :-
3233
+ current_pack(Pack),
3234
+ findall(Cfl, pack_info(Pack, dependency, conflicts(Cfl)), CflList),
3235
+ member(Conflicts, CflList).
3236
+
3237
+ %! pack_depends_on(?Pack, ?Dependency) is nondet.
3238
+ %
3239
+ % True when Pack depends on pack Dependency. This predicate does not
3240
+ % deal with transitive dependency.
3241
+
3242
+ pack_depends_on(Pack, Dependency) :-
3243
+ ground(Pack),
3244
+ !,
3245
+ pack_requires(Pack, Requires),
3246
+ \+ is_prolog_token(Requires),
3247
+ pack_provides(Dependency, Provides),
3248
+ satisfies_req(Provides, Requires).
3249
+ pack_depends_on(Pack, Dependency) :-
3250
+ ground(Dependency),
3251
+ !,
3252
+ pack_provides(Dependency, Provides),
3253
+ pack_requires(Pack, Requires),
3254
+ satisfies_req(Provides, Requires).
3255
+ pack_depends_on(Pack, Dependency) :-
3256
+ current_pack(Pack),
3257
+ pack_depends_on(Pack, Dependency).
3258
+
3259
+ %! dependents(+Pack, -Dependents) is semidet.
3260
+ %
3261
+ % True when Dependents is a list of packs that (indirectly) depend on
3262
+ % Pack.
3263
+
3264
+ dependents(Pack, Deps) :-
3265
+ setof(Dep, dependent(Pack, Dep, []), Deps).
3266
+
3267
+ dependent(Pack, Dep, Seen) :-
3268
+ pack_depends_on(Dep0, Pack),
3269
+ \+ memberchk(Dep0, Seen),
3270
+ ( Dep = Dep0
3271
+ ; dependent(Dep0, Dep, [Dep0|Seen])
3272
+ ).
3273
+
3274
+ %! validate_dependencies is det.
3275
+ %
3276
+ % Validate all dependencies, reporting on failures
3277
+
3278
+ validate_dependencies :-
3279
+ setof(Issue, pack_dependency_issue(_, Issue), Issues),
3280
+ !,
3281
+ print_message(warning, pack(dependency_issues(Issues))).
3282
+ validate_dependencies.
3283
+
3284
+ %! pack_dependency_issue(?Pack, -Issue) is nondet.
3285
+ %
3286
+ % True when Issue is a dependency issue regarding Pack. Issue is one
3287
+ % of
3288
+ %
3289
+ % - unsatisfied(Pack, Requires)
3290
+ % The requirement Requires of Pack is not fulfilled.
3291
+ % - conflicts(Pack, Conflict)
3292
+ % Pack conflicts with Conflict.
3293
+
3294
+ pack_dependency_issue(Pack, Issue) :-
3295
+ current_pack(Pack),
3296
+ pack_dependency_issue_(Pack, Issue).
3297
+
3298
+ pack_dependency_issue_(Pack, unsatisfied(Pack, Requires)) :-
3299
+ pack_requires(Pack, Requires),
3300
+ ( is_prolog_token(Requires)
3301
+ -> \+ prolog_satisfies(Requires)
3302
+ ; \+ ( pack_provides(_, Provides),
3303
+ satisfies_req(Provides, Requires) )
3304
+ ).
3305
+ pack_dependency_issue_(Pack, conflicts(Pack, Conflicts)) :-
3306
+ pack_conflicts(Pack, Conflicts),
3307
+ ( is_prolog_token(Conflicts)
3308
+ -> prolog_satisfies(Conflicts)
3309
+ ; pack_provides(_, Provides),
3310
+ satisfies_req(Provides, Conflicts)
3311
+ ).
3312
+
3313
+
3314
+ /*******************************
3315
+ * RECORD PACK FACTS *
3316
+ *******************************/
3317
+
3318
+ %! pack_assert(+PackDir, ++Fact) is det.
3319
+ %
3320
+ % Add/update a fact about packs. These facts are stored in
3321
+ % PackDir/status.db. Known facts are:
3322
+ %
3323
+ % - built(Arch, Version, How)
3324
+ % Pack has been built by SWI-Prolog Version for Arch. How is one
3325
+ % of `built` if we built it or `downloaded` if it was downloaded.
3326
+ % - automatic(Boolean)
3327
+ % If `true`, pack was installed as dependency.
3328
+ % - archive(Archive, URL)
3329
+ % Available when the pack was installed by unpacking Archive that
3330
+ % was retrieved from URL.
3331
+
3332
+ pack_assert(PackDir, Fact) :-
3333
+ must_be(ground, Fact),
3334
+ findall(Term, pack_status_dir(PackDir, Term), Facts0),
3335
+ update_facts(Facts0, Fact, Facts),
3336
+ OpenOptions = [encoding(utf8), lock(exclusive)],
3337
+ status_file(PackDir, StatusFile),
3338
+ ( Facts == Facts0
3339
+ -> true
3340
+ ; Facts0 \== [],
3341
+ append(Facts0, New, Facts)
3342
+ -> setup_call_cleanup(
3343
+ open(StatusFile, append, Out, OpenOptions),
3344
+ maplist(write_fact(Out), New),
3345
+ close(Out))
3346
+ ; setup_call_cleanup(
3347
+ open(StatusFile, write, Out, OpenOptions),
3348
+ ( write_facts_header(Out),
3349
+ maplist(write_fact(Out), Facts)
3350
+ ),
3351
+ close(Out))
3352
+ ).
3353
+
3354
+ update_facts([], Fact, [Fact]) :-
3355
+ !.
3356
+ update_facts([H|T], Fact, [Fact|T]) :-
3357
+ general_pack_fact(Fact, GenFact),
3358
+ general_pack_fact(H, GenTerm),
3359
+ GenFact =@= GenTerm,
3360
+ !.
3361
+ update_facts([H|T0], Fact, [H|T]) :-
3362
+ update_facts(T0, Fact, T).
3363
+
3364
+ general_pack_fact(built(Arch, _Version, _How), General) =>
3365
+ General = built(Arch, _, _).
3366
+ general_pack_fact(Term, General), compound(Term) =>
3367
+ compound_name_arity(Term, Name, Arity),
3368
+ compound_name_arity(General, Name, Arity).
3369
+ general_pack_fact(Term, General) =>
3370
+ General = Term.
3371
+
3372
+ write_facts_header(Out) :-
3373
+ format(Out, '% Fact status file. Managed by package manager.~n', []).
3374
+
3375
+ write_fact(Out, Term) :-
3376
+ format(Out, '~q.~n', [Term]).
3377
+
3378
+ %! pack_status(?Pack, ?Fact).
3379
+ %! pack_status_dir(+PackDir, ?Fact)
3380
+ %
3381
+ % True when Fact is true about the package in PackDir. Facts
3382
+ % are asserted a file `status.db`.
3383
+
3384
+ pack_status(Pack, Fact) :-
3385
+ current_pack(Pack, PackDir),
3386
+ pack_status_dir(PackDir, Fact).
3387
+
3388
+ pack_status_dir(PackDir, Fact) :-
3389
+ det_if(ground(Fact), pack_status_(PackDir, Fact)).
3390
+
3391
+ pack_status_(PackDir, Fact) :-
3392
+ status_file(PackDir, StatusFile),
3393
+ catch(term_in_file(valid_term(pack_status_term), StatusFile, Fact),
3394
+ error(existence_error(source_sink, StatusFile), _),
3395
+ fail).
3396
+
3397
+ pack_status_term(built(atom, version, oneof([built,downloaded]))).
3398
+ pack_status_term(automatic(boolean)).
3399
+ pack_status_term(archive(atom, atom)).
3400
+
3401
+
3402
+ %! update_automatic(+Info) is det.
3403
+ %
3404
+ % Update the _automatic_ status of a package. If we install it has no
3405
+ % automatic status and we install it as a dependency we mark it as
3406
+ % _automatic_. Else, we mark it as non-automatic as it has been
3407
+ % installed explicitly.
3408
+
3409
+ update_automatic(Info) :-
3410
+ _ = Info.get(dependency_for),
3411
+ \+ pack_status(Info.installed, automatic(_)),
3412
+ !,
3413
+ pack_assert(Info.installed, automatic(true)).
3414
+ update_automatic(Info) :-
3415
+ pack_assert(Info.installed, automatic(false)).
3416
+
3417
+ status_file(PackDir, StatusFile) :-
3418
+ directory_file_path(PackDir, 'status.db', StatusFile).
3419
+
3420
+ /*******************************
3421
+ * USER INTERACTION *
3422
+ *******************************/
3423
+
3424
+ :- multifile prolog:message//1.
3425
+
3426
+ %! menu(Question, +Alternatives, +Default, -Selection, +Options)
3427
+
3428
+ menu(_Question, _Alternatives, Default, Selection, Options) :-
3429
+ option(interactive(false), Options),
3430
+ !,
3431
+ Selection = Default.
3432
+ menu(Question, Alternatives, Default, Selection, _) :-
3433
+ length(Alternatives, N),
3434
+ between(1, 5, _),
3435
+ print_message(query, Question),
3436
+ print_menu(Alternatives, Default, 1),
3437
+ print_message(query, pack(menu(select))),
3438
+ read_selection(N, Choice),
3439
+ !,
3440
+ ( Choice == default
3441
+ -> Selection = Default
3442
+ ; nth1(Choice, Alternatives, Selection=_)
3443
+ -> true
3444
+ ).
3445
+
3446
+ print_menu([], _, _).
3447
+ print_menu([Value=Label|T], Default, I) :-
3448
+ ( Value == Default
3449
+ -> print_message(query, pack(menu(default_item(I, Label))))
3450
+ ; print_message(query, pack(menu(item(I, Label))))
3451
+ ),
3452
+ I2 is I + 1,
3453
+ print_menu(T, Default, I2).
3454
+
3455
+ read_selection(Max, Choice) :-
3456
+ get_single_char(Code),
3457
+ ( answered_default(Code)
3458
+ -> Choice = default
3459
+ ; code_type(Code, digit(Choice)),
3460
+ between(1, Max, Choice)
3461
+ -> true
3462
+ ; print_message(warning, pack(menu(reply(1,Max)))),
3463
+ fail
3464
+ ).
3465
+
3466
+ %! confirm(+Question, +Default, +Options) is semidet.
3467
+ %
3468
+ % Ask for confirmation.
3469
+ %
3470
+ % @arg Default is one of `yes`, `no` or `none`.
3471
+
3472
+ confirm(_Question, Default, Options) :-
3473
+ Default \== none,
3474
+ option(interactive(false), Options, true),
3475
+ !,
3476
+ Default == yes.
3477
+ confirm(Question, Default, _) :-
3478
+ between(1, 5, _),
3479
+ print_message(query, pack(confirm(Question, Default))),
3480
+ read_yes_no(YesNo, Default),
3481
+ !,
3482
+ format(user_error, '~N', []),
3483
+ YesNo == yes.
3484
+
3485
+ read_yes_no(YesNo, Default) :-
3486
+ get_single_char(Code),
3487
+ code_yes_no(Code, Default, YesNo),
3488
+ !.
3489
+
3490
+ code_yes_no(0'y, _, yes).
3491
+ code_yes_no(0'Y, _, yes).
3492
+ code_yes_no(0'n, _, no).
3493
+ code_yes_no(0'N, _, no).
3494
+ code_yes_no(_, none, _) :- !, fail.
3495
+ code_yes_no(C, Default, Default) :-
3496
+ answered_default(C).
3497
+
3498
+ answered_default(0'\r).
3499
+ answered_default(0'\n).
3500
+ answered_default(0'\s).
3501
+
3502
+
3503
+ /*******************************
3504
+ * MESSAGES *
3505
+ *******************************/
3506
+
3507
+ :- multifile prolog:message//1.
3508
+
3509
+ prolog:message(pack(Message)) -->
3510
+ message(Message).
3511
+
3512
+ :- discontiguous
3513
+ message//1,
3514
+ label//1.
3515
+
3516
+ message(invalid_term(pack_info_term, Term)) -->
3517
+ [ 'Invalid package meta data: ~q'-[Term] ].
3518
+ message(invalid_term(pack_status_term, Term)) -->
3519
+ [ 'Invalid package status data: ~q'-[Term] ].
3520
+ message(directory_exists(Dir)) -->
3521
+ [ 'Package target directory exists and is not empty:', nl,
3522
+ '\t~q'-[Dir]
3523
+ ].
3524
+ message(already_installed(pack(Pack, Version))) -->
3525
+ [ 'Pack `~w'' is already installed @~w'-[Pack, Version] ].
3526
+ message(already_installed(Pack)) -->
3527
+ [ 'Pack `~w'' is already installed. Package info:'-[Pack] ].
3528
+ message(kept_foreign(Pack, Arch)) -->
3529
+ [ 'Found foreign libraries for architecture '-[],
3530
+ ansi(code, '~q', [Arch]), nl,
3531
+ 'Use ', ansi(code, '?- pack_rebuild(~q).', [Pack]),
3532
+ ' to rebuild from sources'-[]
3533
+ ].
3534
+ message(no_pack_installed(Pack)) -->
3535
+ [ 'No pack ~q installed. Use ?- pack_list(Pattern) to search'-[Pack] ].
3536
+ message(dependency_issues(Issues)) -->
3537
+ [ 'The current set of packs has dependency issues:', nl ],
3538
+ dep_issues(Issues).
3539
+ message(depends(Pack, Deps)) -->
3540
+ [ 'The following packs depend on `~w\':'-[Pack], nl ],
3541
+ pack_list(Deps).
3542
+ message(remove(link(To), PackDir)) -->
3543
+ [ 'Removing ', url(PackDir), nl, ' as link to ', url(To) ].
3544
+ message(remove(directory, PackDir)) -->
3545
+ [ 'Removing ~q and contents'-[PackDir] ].
3546
+ message(remove_existing_pack(PackDir)) -->
3547
+ [ 'Remove old installation in ~q'-[PackDir] ].
3548
+ message(delete_autoload_index(Pack, Index)) -->
3549
+ [ 'Pack ' ], msg_pack(Pack), [ ': deleting autoload index ', url(Index) ].
3550
+ message(download_plan(Plan)) -->
3551
+ [ ansi(bold, 'Installation plan:', []), nl ],
3552
+ install_plan(Plan, Actions),
3553
+ install_label(Actions).
3554
+ message(build_plan(Plan)) -->
3555
+ [ ansi(bold, 'The following packs have post install scripts:', []), nl ],
3556
+ msg_build_plan(Plan),
3557
+ [ nl, ansi(bold, 'Run scripts?', []) ].
3558
+ message(autoload(Pack)) -->
3559
+ [ 'Pack ' ], msg_pack(Pack),
3560
+ [ ' prefers to be added as autoload library',
3561
+ nl, ansi(bold, 'Allow?', [])
3562
+ ].
3563
+ message(no_meta_data(BaseDir)) -->
3564
+ [ 'Cannot find pack.pl inside directory ~q. Not a package?'-[BaseDir] ].
3565
+ message(search_no_matches(Name)) -->
3566
+ [ 'Search for "~w", returned no matching packages'-[Name] ].
3567
+ message(rebuild(Pack)) -->
3568
+ [ 'Checking pack "~w" for rebuild ...'-[Pack] ].
3569
+ message(up_to_date([Pack])) -->
3570
+ !,
3571
+ [ 'Pack ' ], msg_pack(Pack), [' is up-to-date' ].
3572
+ message(up_to_date(Packs)) -->
3573
+ [ 'Packs ' ], sequence(msg_pack, [', '], Packs), [' are up-to-date' ].
3574
+ message(installed_can_upgrade(List)) -->
3575
+ sequence(msg_can_upgrade_target, [nl], List).
3576
+ message(new_dependencies(Deps)) -->
3577
+ [ 'Found new dependencies after downloading (~p).'-[Deps], nl ].
3578
+ message(query_versions(URL)) -->
3579
+ [ 'Querying "~w" to find new versions ...'-[URL] ].
3580
+ message(no_matching_urls(URL)) -->
3581
+ [ 'Could not find any matching URL: ~q'-[URL] ].
3582
+ message(found_versions([Latest-_URL|More])) -->
3583
+ { length(More, Len) },
3584
+ [ ' Latest version: ~w (~D older)'-[Latest, Len] ].
3585
+ message(build(Pack, PackDir)) -->
3586
+ [ ansi(bold, 'Building pack ~w in directory ~w', [Pack, PackDir]) ].
3587
+ message(contacting_server(Server)) -->
3588
+ [ 'Contacting server at ~w ...'-[Server], flush ].
3589
+ message(server_reply(true(_))) -->
3590
+ [ at_same_line, ' ok'-[] ].
3591
+ message(server_reply(false)) -->
3592
+ [ at_same_line, ' done'-[] ].
3593
+ message(server_reply(exception(E))) -->
3594
+ [ 'Server reported the following error:'-[], nl ],
3595
+ '$messages':translate_message(E).
3596
+ message(cannot_create_dir(Alias)) -->
3597
+ { findall(PackDir,
3598
+ absolute_file_name(Alias, PackDir, [solutions(all)]),
3599
+ PackDirs0),
3600
+ sort(PackDirs0, PackDirs)
3601
+ },
3602
+ [ 'Cannot find a place to create a package directory.'-[],
3603
+ 'Considered:'-[]
3604
+ ],
3605
+ candidate_dirs(PackDirs).
3606
+ message(conflict(version, [PackV, FileV])) -->
3607
+ ['Version mismatch: pack.pl: '-[]], msg_version(PackV),
3608
+ [', file claims version '-[]], msg_version(FileV).
3609
+ message(conflict(name, [PackInfo, FileInfo])) -->
3610
+ ['Pack ~w mismatch: pack.pl: ~p'-[PackInfo]],
3611
+ [', file claims ~w: ~p'-[FileInfo]].
3612
+ message(no_prolog_response(ContentType, String)) -->
3613
+ [ 'Expected Prolog response. Got content of type ~p'-[ContentType], nl,
3614
+ '~s'-[String]
3615
+ ].
3616
+ message(download(begin, Pack, _URL, _DownloadFile)) -->
3617
+ [ 'Downloading ' ], msg_pack(Pack), [ ' ... ', flush ].
3618
+ message(download(end, _, _, File)) -->
3619
+ { size_file(File, Bytes) },
3620
+ [ at_same_line, '~D bytes'-[Bytes] ].
3621
+ message(no_git(URL)) -->
3622
+ [ 'Cannot install from git repository ', url(URL), '.', nl,
3623
+ 'Cannot find git program and do not know how to download the code', nl,
3624
+ 'from this git service. Please install git and retry.'
3625
+ ].
3626
+ message(git_no_https(GitURL)) -->
3627
+ [ 'Do not know how to get an HTTP(s) URL for ', url(GitURL) ].
3628
+ message(git_branch_not_default(Dir, Default, Current)) -->
3629
+ [ 'GIT current branch on ', url(Dir), ' is not default.', nl,
3630
+ ' Current branch: ', ansi(code, '~w', [Current]),
3631
+ ' default: ', ansi(code, '~w', [Default])
3632
+ ].
3633
+ message(git_not_clean(Dir)) -->
3634
+ [ 'GIT working directory is dirty: ', url(Dir), nl,
3635
+ 'Your repository must be clean before publishing.'
3636
+ ].
3637
+ message(git_push) -->
3638
+ [ 'Push release to GIT origin?' ].
3639
+ message(git_tag(Tag)) -->
3640
+ [ 'Tag repository with release tag ', ansi(code, '~w', [Tag]) ].
3641
+ message(git_release_tag_not_at_head(Tag)) -->
3642
+ [ 'Release tag ', ansi(code, '~w', [Tag]), ' is not at HEAD.', nl,
3643
+ 'If you want to update the tag, please run ',
3644
+ ansi(code, 'git tag -d ~w', [Tag])
3645
+ ].
3646
+ message(git_tag_out_of_sync(Tag)) -->
3647
+ [ 'Release tag ', ansi(code, '~w', [Tag]),
3648
+ ' differs from this tag at the origin'
3649
+ ].
3650
+
3651
+ message(published(Info, At)) -->
3652
+ [ 'Published pack ' ], msg_pack(Info), msg_info_version(Info),
3653
+ [' to be installed from '],
3654
+ msg_published_address(At).
3655
+ message(publish_failed(Info, Reason)) -->
3656
+ [ 'Pack ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ],
3657
+ msg_publish_failed(Reason).
3658
+
3659
+ msg_publish_failed(throw(error(permission_error(register,
3660
+ pack(_),_URL),_))) -->
3661
+ [ ' is already registered with a different URL'].
3662
+ msg_publish_failed(download) -->
3663
+ [' was already published?'].
3664
+ msg_publish_failed(Status) -->
3665
+ [ ' failed for unknown reason (~p)'-[Status] ].
3666
+
3667
+ msg_published_address(git(URL)) -->
3668
+ msg_url(URL, _).
3669
+ msg_published_address(file(URL)) -->
3670
+ msg_url(URL, _).
3671
+
3672
+ candidate_dirs([]) --> [].
3673
+ candidate_dirs([H|T]) --> [ nl, ' ~w'-[H] ], candidate_dirs(T).
3674
+ % Questions
3675
+ message(resolve_remove) -->
3676
+ [ nl, 'Please select an action:', nl, nl ].
3677
+ message(create_pack_dir) -->
3678
+ [ nl, 'Create directory for packages', nl ].
3679
+ message(menu(item(I, Label))) -->
3680
+ [ '~t(~d)~6| '-[I] ],
3681
+ label(Label).
3682
+ message(menu(default_item(I, Label))) -->
3683
+ [ '~t(~d)~6| * '-[I] ],
3684
+ label(Label).
3685
+ message(menu(select)) -->
3686
+ [ nl, 'Your choice? ', flush ].
3687
+ message(confirm(Question, Default)) -->
3688
+ message(Question),
3689
+ confirm_default(Default),
3690
+ [ flush ].
3691
+ message(menu(reply(Min,Max))) -->
3692
+ ( { Max =:= Min+1 }
3693
+ -> [ 'Please enter ~w or ~w'-[Min,Max] ]
3694
+ ; [ 'Please enter a number between ~w and ~w'-[Min,Max] ]
3695
+ ).
3696
+
3697
+ % support predicates
3698
+ dep_issues(Issues) -->
3699
+ sequence(dep_issue, [nl], Issues).
3700
+
3701
+ dep_issue(unsatisfied(Pack, Requires)) -->
3702
+ [ ' - Pack ' ], msg_pack(Pack), [' requires ~p'-[Requires]].
3703
+ dep_issue(conflicts(Pack, Conflict)) -->
3704
+ [ ' - Pack ' ], msg_pack(Pack), [' conflicts with ~p'-[Conflict]].
3705
+
3706
+ %! install_plan(+Plan, -Actions)// is det.
3707
+ %! install_label(+Actions)// is det.
3708
+ %
3709
+ % Describe the overall installation plan before downloading.
3710
+
3711
+ install_label([link]) -->
3712
+ !,
3713
+ [ ansi(bold, 'Activate pack?', []) ].
3714
+ install_label([unpack]) -->
3715
+ !,
3716
+ [ ansi(bold, 'Unpack archive?', []) ].
3717
+ install_label(_) -->
3718
+ [ ansi(bold, 'Download packs?', []) ].
3719
+
3720
+
3721
+ install_plan(Plan, Actions) -->
3722
+ install_plan(Plan, Actions, Sec),
3723
+ sec_warning(Sec).
3724
+
3725
+ install_plan([], [], _) -->
3726
+ [].
3727
+ install_plan([H|T], [AH|AT], Sec) -->
3728
+ install_step(H, AH, Sec), [nl],
3729
+ install_plan(T, AT, Sec).
3730
+
3731
+ install_step(Info, keep, _Sec) -->
3732
+ { Info.get(keep) == true },
3733
+ !,
3734
+ [ ' Keep ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ],
3735
+ msg_can_upgrade(Info).
3736
+ install_step(Info, Action, Sec) -->
3737
+ { From = Info.get(upgrade),
3738
+ VFrom = From.version,
3739
+ VTo = Info.get(version),
3740
+ ( cmp_versions(>=, VTo, VFrom)
3741
+ -> Label = ansi(bold, ' Upgrade ', [])
3742
+ ; Label = ansi(warning, ' Downgrade ', [])
3743
+ )
3744
+ },
3745
+ [ Label ], msg_pack(Info),
3746
+ [ ' from version ~w to ~w'- [From.version, Info.get(version)] ],
3747
+ install_from(Info, Action, Sec).
3748
+ install_step(Info, Action, Sec) -->
3749
+ { _From = Info.get(upgrade) },
3750
+ [ ' Upgrade ' ], msg_pack(Info),
3751
+ install_from(Info, Action, Sec).
3752
+ install_step(Info, Action, Sec) -->
3753
+ { Dep = Info.get(dependency_for) },
3754
+ [ ' Install ' ], msg_pack(Info),
3755
+ [ ' at version ~w as dependency for '-[Info.version],
3756
+ ansi(code, '~w', [Dep])
3757
+ ],
3758
+ install_from(Info, Action, Sec),
3759
+ msg_downloads(Info).
3760
+ install_step(Info, Action, Sec) -->
3761
+ { Info.get(commit) == 'HEAD' },
3762
+ !,
3763
+ [ ' Install ' ], msg_pack(Info), [ ' at current GIT HEAD'-[] ],
3764
+ install_from(Info, Action, Sec),
3765
+ msg_downloads(Info).
3766
+ install_step(Info, link, _Sec) -->
3767
+ { Info.get(link) == true,
3768
+ uri_file_name(Info.get(url), Dir)
3769
+ },
3770
+ !,
3771
+ [ ' Install ' ], msg_pack(Info), [ ' as symlink to ', url(Dir) ].
3772
+ install_step(Info, Action, Sec) -->
3773
+ [ ' Install ' ], msg_pack(Info), [ ' at version ~w'-[Info.get(version)] ],
3774
+ install_from(Info, Action, Sec),
3775
+ msg_downloads(Info).
3776
+ install_step(Info, Action, Sec) -->
3777
+ [ ' Install ' ], msg_pack(Info),
3778
+ install_from(Info, Action, Sec),
3779
+ msg_downloads(Info).
3780
+
3781
+ install_from(Info, download, Sec) -->
3782
+ { download_url(Info.url) },
3783
+ !,
3784
+ [ ' from ' ], msg_url(Info.url, Sec).
3785
+ install_from(Info, unpack, Sec) -->
3786
+ [ ' from ' ], msg_url(Info.url, Sec).
3787
+
3788
+ msg_url(URL, unsafe) -->
3789
+ { atomic(URL),
3790
+ atom_concat('http://', Rest, URL)
3791
+ },
3792
+ [ ansi(error, '~w', ['http://']), '~w'-[Rest] ].
3793
+ msg_url(URL, _) -->
3794
+ [ url(URL) ].
3795
+
3796
+ sec_warning(Sec) -->
3797
+ { var(Sec) },
3798
+ !.
3799
+ sec_warning(unsafe) -->
3800
+ [ ansi(warning, ' WARNING: The installation plan includes downloads \c
3801
+ from insecure HTTP servers.', []), nl
3802
+ ].
3803
+
3804
+ msg_downloads(Info) -->
3805
+ { Downloads = Info.get(all_downloads),
3806
+ Downloads > 0
3807
+ },
3808
+ [ ansi(comment, ' (downloaded ~D times)', [Downloads]) ],
3809
+ !.
3810
+ msg_downloads(_) -->
3811
+ [].
3812
+
3813
+ msg_pack(Pack) -->
3814
+ { atom(Pack) },
3815
+ !,
3816
+ [ ansi(code, '~w', [Pack]) ].
3817
+ msg_pack(Info) -->
3818
+ msg_pack(Info.pack).
3819
+
3820
+ msg_info_version(Info) -->
3821
+ [ ansi(code, '@~w', [Info.get(version)]) ],
3822
+ !.
3823
+ msg_info_version(_Info) -->
3824
+ [].
3825
+
3826
+ %! msg_build_plan(+Plan)//
3827
+ %
3828
+ % Describe the build plan before running the build steps.
3829
+
3830
+ msg_build_plan(Plan) -->
3831
+ sequence(build_step, [nl], Plan).
3832
+
3833
+ build_step(Info) -->
3834
+ [ ' Build ' ], msg_pack(Info), [' in directory ', url(Info.installed) ].
3835
+
3836
+ msg_can_upgrade_target(Info) -->
3837
+ [ ' Pack ' ], msg_pack(Info),
3838
+ [ ' is installed at version ~w'-[Info.version] ],
3839
+ msg_can_upgrade(Info).
3840
+
3841
+ pack_list([]) --> [].
3842
+ pack_list([H|T]) -->
3843
+ [ ' - Pack ' ], msg_pack(H), [nl],
3844
+ pack_list(T).
3845
+
3846
+ label(remove_only(Pack)) -->
3847
+ [ 'Only remove package ~w (break dependencies)'-[Pack] ].
3848
+ label(remove_deps(Pack, Deps)) -->
3849
+ { length(Deps, Count) },
3850
+ [ 'Remove package ~w and ~D dependencies'-[Pack, Count] ].
3851
+ label(create_dir(Dir)) -->
3852
+ [ '~w'-[Dir] ].
3853
+ label(install_from(git(URL))) -->
3854
+ !,
3855
+ [ 'GIT repository at ~w'-[URL] ].
3856
+ label(install_from(URL)) -->
3857
+ [ '~w'-[URL] ].
3858
+ label(cancel) -->
3859
+ [ 'Cancel' ].
3860
+
3861
+ confirm_default(yes) -->
3862
+ [ ' Y/n? ' ].
3863
+ confirm_default(no) -->
3864
+ [ ' y/N? ' ].
3865
+ confirm_default(none) -->
3866
+ [ ' y/n? ' ].
3867
+
3868
+ msg_version(Version) -->
3869
+ [ '~w'-[Version] ].
3870
+
3871
+ msg_can_upgrade(Info) -->
3872
+ { Latest = Info.get(latest_version) },
3873
+ [ ansi(warning, ' (can be upgraded to ~w)', [Latest]) ].
3874
+ msg_can_upgrade(_) -->
3875
+ [].
3876
+
3877
+
3878
+ /*******************************
3879
+ * MISC *
3880
+ *******************************/
3881
+
3882
+ local_uri_file_name(URL, FileName) :-
3883
+ uri_file_name(URL, FileName),
3884
+ !.
3885
+ local_uri_file_name(URL, FileName) :-
3886
+ uri_components(URL, Components),
3887
+ uri_data(scheme, Components, File), File == file,
3888
+ uri_data(authority, Components, FileNameEnc),
3889
+ uri_data(path, Components, ''),
3890
+ uri_encoded(path, FileName, FileNameEnc).
3891
+
3892
+ det_if(Cond, Goal) :-
3893
+ ( Cond
3894
+ -> Goal,
3895
+ !
3896
+ ; Goal
3897
+ ).
3898
+
3899
+ member_nonvar(_, Var) :-
3900
+ var(Var),
3901
+ !,
3902
+ fail.
3903
+ member_nonvar(E, [E|_]).
3904
+ member_nonvar(E, [_|T]) :-
3905
+ member_nonvar(E, T).
3906
+