1. # API to obtain the data of any addressable content
  2. role Distribution { ... }
  3. role Distribution {
  4. # `meta` provides an API to the meta data in META6 spec (s22)
  5. # - A Distribution may be represented internally by some other
  6. # spec (such as using the file system itself for prereqs), as
  7. # long as it can also be represented as the META6 hash format
  8. method meta(--> Hash:D) {
  9. # Cannot just use ... here as that would break legacy code
  10. my $class-name = ::?CLASS.^name;
  11. die $class-name eq 'Distribution'
  12. ?? 'Legacy Distribution object used in code expecting an object consuming the Distribution role'
  13. !! "Method 'meta' must be implemented by $class-name because it is required by role Distribution"
  14. }
  15. # `content($content-id)` provides an API to the data itself
  16. # - Use `.meta` to determine the $address of a specific $content-id
  17. # - IO::Handle is meant to be a data stream that may or may not be available; for now
  18. # it would return an IO::Handle and have `.open.slurp-rest(:bin)` called on it. So if
  19. # a socket wants to handle this role currently it would have to wrap `open` or `.slurp-rest`
  20. # to handle any protocol negotiation as well as probably saving the data to a tmpfile and
  21. # return an IO::Handle to that
  22. method content($content-id --> IO::Handle:D) {
  23. # Cannot just use ... here as that would break legacy code
  24. my $class-name = ::?CLASS.^name;
  25. die $class-name eq 'Distribution'
  26. ?? 'Legacy Distribution object used in code expecting an object consuming the Distribution role'
  27. !! "Method 'content' must be implemented by $class-name because it is required by role Distribution"
  28. }
  29. # Backwards compatibility shim
  30. submethod new(*%_) {
  31. ::?CLASS.^name eq 'Distribution'
  32. ?? class :: {
  33. has $.name;
  34. has $.auth;
  35. has $.author;
  36. has $.authority;
  37. has $.api;
  38. has $.ver;
  39. has $.version;
  40. has $.description;
  41. has @.depends;
  42. has %.provides;
  43. has %.files;
  44. has $.source-url;
  45. method auth { $!auth // $!author // $!authority }
  46. method ver { $!ver // $!version }
  47. method meta(--> Hash:D) {
  48. {
  49. :$!name,
  50. :$.auth,
  51. :$.ver,
  52. :$.api,
  53. :$!description,
  54. :@!depends,
  55. :%!provides,
  56. :%!files,
  57. :$!source-url,
  58. }
  59. }
  60. method Str() {
  61. return "{$.meta<name>}"
  62. ~ ":ver<{$.meta<ver> // ''}>"
  63. ~ ":auth<{$.meta<auth> // ''}>"
  64. ~ ":api<{$.meta<api> // ''}>";
  65. }
  66. method content($content-id --> IO::Handle:D) { }
  67. }.new(|%_)
  68. !! self.bless(|%_)
  69. }
  70. }
  71. role Distribution::Locally does Distribution {
  72. has IO::Path $.prefix;
  73. method content($address) {
  74. my $handle = IO::Handle.new: path => IO::Path.new($address, :CWD($!prefix // $*CWD));
  75. $handle // $handle.throw;
  76. }
  77. }
  78. # A distribution passed to `CURI.install()` will get encapsulated in this
  79. # class, which normalizes the meta6 data and adds identifiers/content-id
  80. class CompUnit::Repository::Distribution {
  81. has Distribution $!dist handles 'content';
  82. has $!meta;
  83. submethod BUILD(:$!meta, :$!dist --> Nil) { }
  84. method new(Distribution $dist) {
  85. my $meta = $dist.meta.hash;
  86. $meta<ver> //= $meta<version>;
  87. $meta<auth> //= $meta<authority> // $meta<author>;
  88. self.bless(:$dist, :$meta);
  89. }
  90. method meta { $!meta }
  91. method Str() {
  92. return "{$.meta<name>}"
  93. ~ ":ver<{$.meta<ver> // ''}>"
  94. ~ ":auth<{$.meta<auth> // ''}>"
  95. ~ ":api<{$.meta<api> // ''}>";
  96. }
  97. method id() {
  98. return nqp::sha1(self.Str);
  99. }
  100. }
  101. class Distribution::Hash does Distribution::Locally {
  102. has $!meta;
  103. submethod BUILD(:$!meta, :$!prefix --> Nil) { }
  104. method new($hash, :$prefix) { self.bless(:meta($hash), :$prefix) }
  105. method meta { $!meta }
  106. }
  107. class Distribution::Path does Distribution::Locally {
  108. has $!meta;
  109. submethod BUILD(:$!meta, :$!prefix --> Nil) { }
  110. method new(IO::Path $prefix, IO::Path :$meta-file is copy) {
  111. $meta-file //= $prefix.add('META6.json');
  112. die "No meta file located at {$meta-file.path}" unless $meta-file.e;
  113. my $meta = Rakudo::Internals::JSON.from-json($meta-file.slurp);
  114. # generate `files` (special directories) directly from the file system
  115. my %bins = Rakudo::Internals.DIR-RECURSE($prefix.add('bin').absolute).map(*.IO).map: -> $real-path {
  116. my $name-path = $real-path.is-relative
  117. ?? $real-path
  118. !! $real-path.relative($prefix);
  119. $name-path => $real-path.absolute
  120. }
  121. my $resources-dir = $prefix.add('resources');
  122. my %resources = $meta<resources>.grep(*.?chars).map(*.IO).map: -> $path {
  123. my $real-path = $path ~~ m/^libraries\/(.*)/
  124. ?? $resources-dir.add('libraries').add( $*VM.platform-library-name($0.Str.IO) )
  125. !! $resources-dir.add($path);
  126. my $name-path = $path.is-relative
  127. ?? "resources/{$path}"
  128. !! "resources/{$path.relative($prefix)}";
  129. $name-path => $real-path.absolute;
  130. }
  131. $meta<files> = |%bins, |%resources;
  132. self.bless(:$meta, :$prefix);
  133. }
  134. method meta { $!meta }
  135. }
  136. role CompUnit::Repository { ... }
  137. class Distribution::Resource {
  138. has $.repo;
  139. has $.repo-name;
  140. has $.dist-id;
  141. has $.key;
  142. method IO() {
  143. my $repo := self.repo-name
  144. ?? CompUnit::RepositoryRegistry.repository-for-name(self.repo-name)
  145. !! CompUnit::RepositoryRegistry.repository-for-spec(self.repo);
  146. $repo.resource(self.dist-id, "resources/$.key")
  147. }
  148. method platform-library-name() {
  149. my $library = self.IO;
  150. ($library ~~ /\.<.alpha>+$/ or $library ~~ /\.so(\.<.digit>+)+$/) #Already a full name?
  151. ?? $library
  152. !! $*VM.platform-library-name($library)
  153. }
  154. # delegate appropriate IO::Path methods to the resource IO::Path object
  155. method Str(|c) {
  156. self.IO.Str(|c)
  157. }
  158. method gist(|c) {
  159. self.IO.gist(|c)
  160. }
  161. method perl(|c) {
  162. self.IO.perl(|c)
  163. }
  164. method absolute(|c) {
  165. self.IO.absolute(|c)
  166. }
  167. method is-absolute(|c) {
  168. self.IO.is-absolute(|c)
  169. }
  170. method relative(|c) {
  171. self.IO.relative(|c)
  172. }
  173. method is-relative(|c) {
  174. self.IO.is-relative(|c)
  175. }
  176. method parts(|c) {
  177. self.IO.parts(|c)
  178. }
  179. method volume(|c) {
  180. self.IO.volume(|c)
  181. }
  182. method dirname(|c) {
  183. self.IO.dirname(|c)
  184. }
  185. method basename(|c) {
  186. self.IO.basename(|c)
  187. }
  188. method extension(|c) {
  189. self.IO.extension(|c)
  190. }
  191. method open(|c) {
  192. self.IO.open(|c)
  193. }
  194. method resolve(|c) {
  195. self.IO.resolve(|c)
  196. }
  197. method slurp(|c) {
  198. self.IO.slurp(|c)
  199. }
  200. method lines(|c) {
  201. self.IO.lines(|c)
  202. }
  203. method comb(|c) {
  204. self.IO.comb(|c)
  205. }
  206. method split(|c) {
  207. self.IO.split(|c)
  208. }
  209. method words(|c) {
  210. self.IO.words(|c)
  211. }
  212. method copy(|c) {
  213. self.IO.copy(|c)
  214. }
  215. }
  216. class Distribution::Resources does Associative {
  217. has Str $.dist-id;
  218. has Str $.repo;
  219. has Str $.repo-name;
  220. proto method BUILD(|) {*}
  221. multi method BUILD(:$!dist-id, CompUnit::Repository :$repo --> Nil) {
  222. unless $repo.can('name') and $!repo-name = $repo.name and $!repo-name ne '' {
  223. $!repo = $repo.path-spec;
  224. $!repo-name = Str;
  225. }
  226. }
  227. multi method BUILD(:$!dist-id, :$repo, Str :$!repo-name --> Nil) { }
  228. multi method BUILD(:$!dist-id, Str :$!repo, :$repo-name --> Nil) { }
  229. method from-precomp() {
  230. if %*ENV<RAKUDO_PRECOMP_DIST> -> \dist {
  231. my %data := Rakudo::Internals::JSON.from-json: dist;
  232. self.new(:repo(%data<repo>), :repo-name(%data<repo-name>), :dist-id(%data<dist-id>))
  233. }
  234. else {
  235. Nil
  236. }
  237. }
  238. method AT-KEY($key) {
  239. Distribution::Resource.new(:$.repo, :$.repo-name, :$.dist-id, :$key)
  240. }
  241. method Str() {
  242. Rakudo::Internals::JSON.to-json: {:$.repo, :$.repo-name, :$.dist-id}
  243. }
  244. }