1. class CompUnit::Repository::FileSystem does CompUnit::Repository::Locally does CompUnit::Repository {
  2. has %!loaded;
  3. has $!precomp;
  4. has $!id;
  5. has %!meta;
  6. has $!precomp-stores;
  7. has $!precomp-store;
  8. my @extensions = <pm6 pm>;
  9. my $extensions := nqp::hash('pm6',1,'pm',1);
  10. # global cache of files seen
  11. my %seen;
  12. method !matching-file(CompUnit::DependencySpecification $spec) {
  13. if $spec.from eq 'Perl6' {
  14. my $name = $spec.short-name;
  15. return %!loaded{$name} if %!loaded{$name}:exists;
  16. my $base := $!prefix.add($name.subst(:g, "::", $*SPEC.dir-sep) ~ '.').Str;
  17. return $base if %seen{$base}:exists;
  18. my $found;
  19. # find source file
  20. # pick a META6.json if it is there
  21. if not %!meta and (my $meta = $!prefix.add('META6.json')) and $meta.f {
  22. try {
  23. %!meta = Rakudo::Internals::JSON.from-json: $meta.slurp;
  24. CATCH {
  25. when JSONException {
  26. fail "Invalid JSON found in META6.json";
  27. }
  28. }
  29. }
  30. }
  31. if %!meta {
  32. if %!meta<provides>{$name} -> $file {
  33. my $path = $file.IO.is-absolute ?? $file.IO !! $!prefix.add($file);
  34. $found = $path if $path.f;
  35. }
  36. }
  37. unless ?$found {
  38. # deduce path to compilation unit from package name
  39. for @extensions -> $extension {
  40. my $path = ($base ~ $extension).IO;
  41. $found = $path if $path.f;
  42. last if $found;
  43. }
  44. }
  45. return $base, $found if $found;
  46. }
  47. False
  48. }
  49. method !comp-unit-id($name) {
  50. CompUnit::PrecompilationId.new-from-string($name);
  51. }
  52. method id() {
  53. my $parts := nqp::list_s;
  54. my $prefix = self.prefix;
  55. my $dir := { .match(/ ^ <.ident> [ <[ ' - ]> <.ident> ]* $ /) }; # ' hl
  56. my $file := -> str $file {
  57. nqp::eqat($file,'.pm',nqp::sub_i(nqp::chars($file),3))
  58. || nqp::eqat($file,'.pm6',nqp::sub_i(nqp::chars($file),4))
  59. };
  60. nqp::if(
  61. $!id,
  62. $!id,
  63. ($!id = nqp::if(
  64. $prefix.e,
  65. nqp::stmts(
  66. (my $iter := Rakudo::Internals.DIR-RECURSE(
  67. $prefix.absolute,:$dir,:$file).iterator),
  68. nqp::until(
  69. nqp::eqaddr((my $pulled := $iter.pull-one),IterationEnd),
  70. nqp::if(
  71. nqp::filereadable($pulled),
  72. nqp::push_s($parts,nqp::sha1(slurp($pulled, :enc<iso-8859-1>))),
  73. )
  74. ),
  75. nqp::if(
  76. (my $next := self.next-repo),
  77. nqp::push_s($parts,$next.id),
  78. ),
  79. nqp::sha1(nqp::join('',$parts))
  80. ),
  81. nqp::sha1('')
  82. ))
  83. )
  84. }
  85. method resolve(CompUnit::DependencySpecification $spec --> CompUnit:D) {
  86. my ($base, $file) = self!matching-file($spec);
  87. return CompUnit.new(
  88. :short-name($spec.short-name),
  89. :repo-id(self!comp-unit-id($spec.short-name).Str),
  90. :repo(self)
  91. ) if $base;
  92. return self.next-repo.resolve($spec) if self.next-repo;
  93. Nil
  94. }
  95. method !precomp-stores() {
  96. $!precomp-stores //= Array[CompUnit::PrecompilationStore].new(
  97. gather {
  98. my $repo = $*REPO;
  99. while $repo {
  100. my \store = $repo.precomp-store;
  101. take store if store.defined;
  102. $repo = $repo.next-repo;
  103. }
  104. }
  105. )
  106. }
  107. method need(
  108. CompUnit::DependencySpecification $spec,
  109. CompUnit::PrecompilationRepository $precomp = self.precomp-repository(),
  110. CompUnit::PrecompilationStore :@precomp-stores = self!precomp-stores(),
  111. --> CompUnit:D)
  112. {
  113. my ($base, $file) = self!matching-file($spec);
  114. if $base {
  115. my $name = $spec.short-name;
  116. return %!loaded{$name} if %!loaded{$name}:exists;
  117. return %seen{$base} if %seen{$base}:exists;
  118. my $id = self!comp-unit-id($name);
  119. my $*RESOURCES = Distribution::Resources.new(:repo(self), :dist-id(''));
  120. my $handle = $precomp.try-load(
  121. CompUnit::PrecompilationDependency::File.new(
  122. :$id,
  123. :src($file.Str),
  124. :$spec,
  125. ),
  126. :@precomp-stores,
  127. );
  128. my $precompiled = defined $handle;
  129. $handle //= CompUnit::Loader.load-source-file($file); # precomp failed
  130. return %!loaded{$name} = %seen{$base} = CompUnit.new(
  131. :short-name($name),
  132. :$handle,
  133. :repo(self),
  134. :repo-id($id.Str),
  135. :$precompiled,
  136. );
  137. }
  138. return self.next-repo.need($spec, $precomp, :@precomp-stores) if self.next-repo;
  139. X::CompUnit::UnsatisfiedDependency.new(:specification($spec)).throw;
  140. }
  141. method load(IO::Path:D $file --> CompUnit:D) {
  142. unless $file.is-absolute {
  143. # We have a $file when we hit: require "PATH" or use/require Foo:file<PATH>;
  144. my $precompiled =
  145. $file.Str.ends-with(Rakudo::Internals.PRECOMP-EXT);
  146. my $path = $!prefix.add($file);
  147. if $path.f {
  148. return %!loaded{$file.Str} //= %seen{$path.Str} = CompUnit.new(
  149. :handle(
  150. $precompiled
  151. ?? CompUnit::Loader.load-precompilation-file($path)
  152. !! CompUnit::Loader.load-source-file($path)
  153. ),
  154. :short-name($file.Str),
  155. :repo(self),
  156. :repo-id($file.Str),
  157. :$precompiled,
  158. );
  159. }
  160. }
  161. return self.next-repo.load($file) if self.next-repo;
  162. nqp::die("Could not find $file in:\n" ~ $*REPO.repo-chain.map(*.Str).join("\n").indent(4));
  163. }
  164. method short-id() { 'file' }
  165. method loaded(--> Iterable:D) {
  166. return %!loaded.values;
  167. }
  168. method files($file, :$name, :$auth, :$ver) {
  169. my $base := $file.IO;
  170. $base.f
  171. ?? { files => { $file => $base.path }, ver => Version.new('0') }
  172. !! ();
  173. }
  174. method resource($dist-id, $key) {
  175. # We now save the 'resources/' part of a resource's path in files, i.e:
  176. # "files" : [ "resources/libraries/xxx" => "resources/libraries/xxx.so" ]
  177. # but we also want to root any path request to the CUR's resources directory
  178. # When $.prefix points at a directory containing a meta file (eg. -I.)
  179. return $.prefix.add( %!meta<files>{$key} )
  180. if %!meta<files> && %!meta<files>{$key};
  181. return $.prefix.add( $key )
  182. if %!meta<resources> && %!meta<resources>.first({ $_ eq $key.subst(/^resources\//, "") });
  183. # When $.prefix is presumably the 'lib' folder (eg. -Ilib)
  184. return $.prefix.parent.add($key);
  185. }
  186. method precomp-store(--> CompUnit::PrecompilationStore:D) {
  187. $!precomp-store //= CompUnit::PrecompilationStore::File.new(
  188. :prefix(self.prefix.add('.precomp')),
  189. )
  190. }
  191. method precomp-repository(--> CompUnit::PrecompilationRepository:D) {
  192. $!precomp := CompUnit::PrecompilationRepository::Default.new(
  193. :store(self.precomp-store),
  194. ) unless $!precomp;
  195. $!precomp
  196. }
  197. }