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.child($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.child('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.child($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;
  41. $found = $path.IO if IO::Path.new-from-absolute-path($path).f;
  42. }
  43. }
  44. return $base, $found if $found;
  45. }
  46. False
  47. }
  48. method !comp-unit-id($name) {
  49. CompUnit::PrecompilationId.new(nqp::sha1($name));
  50. }
  51. method id() {
  52. my $parts := nqp::list_s;
  53. my $prefix = self.prefix;
  54. my $dir := { .match(/ ^ <.ident> [ <[ ' - ]> <.ident> ]* $ /) }; # ' hl
  55. my $file := -> str $file {
  56. nqp::eqat($file,'.pm',nqp::sub_i(nqp::chars($file),3))
  57. || nqp::eqat($file,'.pm6',nqp::sub_i(nqp::chars($file),4))
  58. };
  59. nqp::if(
  60. $!id,
  61. $!id,
  62. ($!id = nqp::if(
  63. $prefix.e,
  64. nqp::stmts(
  65. (my $iter := Rakudo::Internals.DIR-RECURSE(
  66. $prefix.absolute,:$dir,:$file).iterator),
  67. nqp::until(
  68. nqp::eqaddr((my $pulled := $iter.pull-one),IterationEnd),
  69. nqp::if(
  70. nqp::filereadable($pulled)
  71. && (my $pio := nqp::open($pulled,'r')),
  72. nqp::stmts(
  73. nqp::setencoding($pio,'iso-8859-1'),
  74. nqp::push_s($parts,nqp::sha1(nqp::readallfh($pio))),
  75. nqp::closefh($pio)
  76. )
  77. )
  78. ),
  79. nqp::if(
  80. (my $next := self.next-repo),
  81. nqp::push_s($parts,$next.id),
  82. ),
  83. nqp::sha1(nqp::join('',$parts))
  84. ),
  85. nqp::sha1('')
  86. ))
  87. )
  88. }
  89. method resolve(CompUnit::DependencySpecification $spec --> CompUnit:D) {
  90. my ($base, $file) = self!matching-file($spec);
  91. return CompUnit.new(
  92. :short-name($spec.short-name),
  93. :repo-id(self!comp-unit-id($spec.short-name).Str),
  94. :repo(self)
  95. ) if $base;
  96. return self.next-repo.resolve($spec) if self.next-repo;
  97. Nil
  98. }
  99. method !precomp-stores() {
  100. $!precomp-stores //= Array[CompUnit::PrecompilationStore].new(
  101. self.repo-chain.map(*.precomp-store).grep(*.defined)
  102. )
  103. }
  104. method need(
  105. CompUnit::DependencySpecification $spec,
  106. CompUnit::PrecompilationRepository $precomp = self.precomp-repository(),
  107. CompUnit::PrecompilationStore :@precomp-stores = self!precomp-stores(),
  108. --> CompUnit:D)
  109. {
  110. my ($base, $file) = self!matching-file($spec);
  111. if $base {
  112. my $name = $spec.short-name;
  113. return %!loaded{$name} if %!loaded{$name}:exists;
  114. return %seen{$base} if %seen{$base}:exists;
  115. my $id = self!comp-unit-id($name);
  116. my $*RESOURCES = Distribution::Resources.new(:repo(self), :dist-id(''));
  117. my $handle = $precomp.try-load(
  118. CompUnit::PrecompilationDependency::File.new(
  119. :$id,
  120. :src($file.Str),
  121. :$spec,
  122. ),
  123. :@precomp-stores,
  124. );
  125. my $precompiled = defined $handle;
  126. $handle //= CompUnit::Loader.load-source-file($file); # precomp failed
  127. return %!loaded{$name} = %seen{$base} = CompUnit.new(
  128. :short-name($name),
  129. :$handle,
  130. :repo(self),
  131. :repo-id($id.Str),
  132. :$precompiled,
  133. );
  134. }
  135. return self.next-repo.need($spec, $precomp, :@precomp-stores) if self.next-repo;
  136. X::CompUnit::UnsatisfiedDependency.new(:specification($spec)).throw;
  137. }
  138. method load(IO::Path:D $file --> CompUnit:D) {
  139. unless $file.is-absolute {
  140. # We have a $file when we hit: require "PATH" or use/require Foo:file<PATH>;
  141. my $precompiled =
  142. $file.Str.ends-with(Rakudo::Internals.PRECOMP-EXT);
  143. my $path = $!prefix.child($file);
  144. if $path.f {
  145. return %!loaded{$file.Str} //= %seen{$path.Str} = CompUnit.new(
  146. :handle(
  147. $precompiled
  148. ?? CompUnit::Loader.load-precompilation-file($path)
  149. !! CompUnit::Loader.load-source-file($path)
  150. ),
  151. :short-name($file.Str),
  152. :repo(self),
  153. :repo-id($file.Str),
  154. :$precompiled,
  155. );
  156. }
  157. }
  158. return self.next-repo.load($file) if self.next-repo;
  159. nqp::die("Could not find $file in:\n" ~ $*REPO.repo-chain.map(*.Str).join("\n").indent(4));
  160. }
  161. method short-id() { 'file' }
  162. method loaded(--> Iterable:D) {
  163. return %!loaded.values;
  164. }
  165. method files($file, :$name, :$auth, :$ver) {
  166. my $base := $file.IO;
  167. $base.f
  168. ?? { files => { $file => $base.path }, ver => Version.new('0') }
  169. !! ();
  170. }
  171. method resource($dist-id, $key) {
  172. # We now save the 'resources/' part of a resource's path in files, i.e:
  173. # "files" : [ "resources/libraries/xxx" => "resources/libraries/xxx.so" ]
  174. # but we also want to root any path request to the CUR's resources directory
  175. $.prefix.parent.child('resources').child($key.subst(/^resources\//, ""));
  176. }
  177. method precomp-store(--> CompUnit::PrecompilationStore:D) {
  178. $!precomp-store //= CompUnit::PrecompilationStore::File.new(
  179. :prefix(self.prefix.child('.precomp')),
  180. )
  181. }
  182. method precomp-repository(--> CompUnit::PrecompilationRepository:D) {
  183. $!precomp := CompUnit::PrecompilationRepository::Default.new(
  184. :store(self.precomp-store),
  185. ) unless $!precomp;
  186. $!precomp
  187. }
  188. }