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. :$!description,
  53. :@!depends,
  54. :%!provides,
  55. :%!files,
  56. :$!source-url,
  57. }
  58. }
  59. method Str() {
  60. return "{$.meta<name>}"
  61. ~ ":ver<{$.meta<ver> // ''}>"
  62. ~ ":auth<{$.meta<auth> // ''}>"
  63. ~ ":api<{$.meta<api> // ''}>";
  64. }
  65. method content($content-id --> IO::Handle:D) { }
  66. }.new(|%_)
  67. !! self.bless(|%_)
  68. }
  69. }
  70. role Distribution::Locally does Distribution {
  71. has IO::Path $.prefix;
  72. method content($address) {
  73. my $handle = IO::Handle.new: path => IO::Path.new($address, :CWD($!prefix // $*CWD));
  74. $handle // $handle.throw;
  75. }
  76. }
  77. # A distribution passed to `CURI.install()` will get encapsulated in this
  78. # class, which normalizes the meta6 data and adds identifiers/content-id
  79. class CompUnit::Repository::Distribution {
  80. has Distribution $!dist handles 'content';
  81. has $!meta;
  82. submethod BUILD(:$!meta, :$!dist --> Nil) { }
  83. method new(Distribution $dist) {
  84. my $meta = $dist.meta.hash;
  85. $meta<ver> //= $meta<version>;
  86. $meta<auth> //= $meta<authority> // $meta<author>;
  87. self.bless(:$dist, :$meta);
  88. }
  89. method meta { $!meta }
  90. method Str() {
  91. return "{$.meta<name>}"
  92. ~ ":ver<{$.meta<ver> // ''}>"
  93. ~ ":auth<{$.meta<auth> // ''}>"
  94. ~ ":api<{$.meta<api> // ''}>";
  95. }
  96. method id() {
  97. return nqp::sha1(self.Str);
  98. }
  99. }
  100. class Distribution::Hash does Distribution::Locally {
  101. has $!meta;
  102. submethod BUILD(:$!meta, :$!prefix --> Nil) { }
  103. method new($hash, :$prefix) { self.bless(:meta($hash), :$prefix) }
  104. method meta { $!meta }
  105. }
  106. class Distribution::Path does Distribution::Locally {
  107. has $!meta;
  108. submethod BUILD(:$!meta, :$!prefix --> Nil) { }
  109. method new(IO::Path $prefix, IO::Path :$meta-file is copy) {
  110. $meta-file //= $prefix.add('META6.json');
  111. die "No meta file located at {$meta-file.path}" unless $meta-file.e;
  112. my $meta = Rakudo::Internals::JSON.from-json($meta-file.slurp);
  113. # generate `files` (special directories) directly from the file system
  114. my %bins = Rakudo::Internals.DIR-RECURSE($prefix.add('bin').absolute).map(*.IO).map: -> $real-path {
  115. my $name-path = $real-path.is-relative
  116. ?? $real-path
  117. !! $real-path.relative($prefix);
  118. $name-path => $real-path.absolute
  119. }
  120. my $resources-dir = $prefix.add('resources');
  121. my %resources = $meta<resources>.grep(*.?chars).map(*.IO).map: -> $path {
  122. my $real-path = $path ~~ m/^libraries\/(.*)/
  123. ?? $resources-dir.add('libraries').add( $*VM.platform-library-name($0.Str.IO) )
  124. !! $resources-dir.add($path);
  125. my $name-path = $path.is-relative
  126. ?? "resources/{$path}"
  127. !! "resources/{$path.relative($prefix)}";
  128. $name-path => $real-path.absolute;
  129. }
  130. $meta<files> = |%bins, |%resources;
  131. self.bless(:$meta, :$prefix);
  132. }
  133. method meta { $!meta }
  134. }
  135. role CompUnit::Repository { ... }
  136. class Distribution::Resource {
  137. has $.repo;
  138. has $.repo-name;
  139. has $.dist-id;
  140. has $.key;
  141. method IO() {
  142. my $repo := self.repo-name
  143. ?? CompUnit::RepositoryRegistry.repository-for-name(self.repo-name)
  144. !! CompUnit::RepositoryRegistry.repository-for-spec(self.repo);
  145. $repo.resource(self.dist-id, "resources/$.key")
  146. }
  147. method platform-library-name() {
  148. my $library = self.IO;
  149. ($library ~~ /\.<.alpha>+$/ or $library ~~ /\.so(\.<.digit>+)+$/) #Already a full name?
  150. ?? $library
  151. !! $*VM.platform-library-name($library)
  152. }
  153. # delegate appropriate IO::Path methods to the resource IO::Path object
  154. method Str(|c) {
  155. self.IO.Str(|c)
  156. }
  157. method gist(|c) {
  158. self.IO.gist(|c)
  159. }
  160. method perl(|c) {
  161. self.IO.perl(|c)
  162. }
  163. method absolute(|c) {
  164. self.IO.absolute(|c)
  165. }
  166. method is-absolute(|c) {
  167. self.IO.is-absolute(|c)
  168. }
  169. method relative(|c) {
  170. self.IO.relative(|c)
  171. }
  172. method is-relative(|c) {
  173. self.IO.is-relative(|c)
  174. }
  175. method parts(|c) {
  176. self.IO.parts(|c)
  177. }
  178. method volume(|c) {
  179. self.IO.volume(|c)
  180. }
  181. method dirname(|c) {
  182. self.IO.dirname(|c)
  183. }
  184. method basename(|c) {
  185. self.IO.basename(|c)
  186. }
  187. method extension(|c) {
  188. self.IO.extension(|c)
  189. }
  190. method open(|c) {
  191. self.IO.open(|c)
  192. }
  193. method resolve(|c) {
  194. self.IO.resolve(|c)
  195. }
  196. method slurp(|c) {
  197. self.IO.slurp(|c)
  198. }
  199. method lines(|c) {
  200. self.IO.lines(|c)
  201. }
  202. method comb(|c) {
  203. self.IO.comb(|c)
  204. }
  205. method split(|c) {
  206. self.IO.split(|c)
  207. }
  208. method words(|c) {
  209. self.IO.words(|c)
  210. }
  211. method copy(|c) {
  212. self.IO.copy(|c)
  213. }
  214. }
  215. class Distribution::Resources does Associative {
  216. has Str $.dist-id;
  217. has Str $.repo;
  218. has Str $.repo-name;
  219. proto method BUILD(|) { * }
  220. multi method BUILD(:$!dist-id, CompUnit::Repository :$repo --> Nil) {
  221. unless $repo.can('name') and $!repo-name = $repo.name and $!repo-name ne '' {
  222. $!repo = $repo.path-spec;
  223. $!repo-name = Str;
  224. }
  225. }
  226. multi method BUILD(:$!dist-id, :$repo, Str :$!repo-name --> Nil) { }
  227. multi method BUILD(:$!dist-id, Str :$!repo, :$repo-name --> Nil) { }
  228. method from-precomp() {
  229. if %*ENV<RAKUDO_PRECOMP_DIST> -> \dist {
  230. my %data := Rakudo::Internals::JSON.from-json: dist;
  231. self.new(:repo(%data<repo>), :repo-name(%data<repo-name>), :dist-id(%data<dist-id>))
  232. }
  233. else {
  234. Nil
  235. }
  236. }
  237. method AT-KEY($key) {
  238. Distribution::Resource.new(:$.repo, :$.repo-name, :$.dist-id, :$key)
  239. }
  240. method Str() {
  241. Rakudo::Internals::JSON.to-json: {:$.repo, :$.repo-name, :$.dist-id}
  242. }
  243. }