@@ -234,12 +234,14 @@ end
234
234
# # package identification: determine unique identity of package to be loaded ##
235
235
236
236
# Used by Pkg but not used in loading itself
237
- function find_package (arg)
238
- pkg = identify_package (arg)
237
+ find_package (name:: String ) = find_package (PkgId (" " ), name) # `where` without a uuid will be ignored
238
+ function find_package (where :: Union{Module,PkgId} , name:: String )
239
+ pkg = identify_package (where , name)
239
240
pkg === nothing && return nothing
240
241
return locate_package (pkg)
241
242
end
242
243
244
+
243
245
# # package identity: given a package name and a context, try to return its identity ##
244
246
identify_package (where :: Module , name:: String ) = identify_package (PkgId (where ), name)
245
247
@@ -267,6 +269,29 @@ function identify_package(name::String)::Union{Nothing,PkgId}
267
269
return nothing
268
270
end
269
271
272
+ # identify_package computes the list of packages that can be loaded by where
273
+ function identify_package_deps (where :: PkgId ):: Vector{PkgId}
274
+ where . uuid === nothing && return identify_package_deps ()
275
+ deps = PkgId[]
276
+ for env in load_path ()
277
+ deps = manifest_deps_list (env, where )
278
+ deps === nothing || return deps # found--return it
279
+ end
280
+ return PkgId[where ]
281
+ end
282
+
283
+ # identify_package lists all packages that can be loaded from any toplevel context
284
+ # by looking through the Project.toml files and directories
285
+ function identify_package_deps ():: Vector{PkgId}
286
+ deps = PkgId[]
287
+ for env in load_path ()
288
+ add = project_deps_list (env)
289
+ add === nothing || append! (deps, add)
290
+ end
291
+ return deps
292
+ end
293
+
294
+
270
295
# # package location: given a package identity, find file to load ##
271
296
function locate_package (pkg:: PkgId ):: Union{Nothing,String}
272
297
if pkg. uuid === nothing
@@ -281,7 +306,7 @@ function locate_package(pkg::PkgId)::Union{Nothing,String}
281
306
return implicit_manifest_uuid_path (env, pkg)
282
307
end
283
308
@assert found. uuid != = nothing
284
- return locate_package (found) # restart search now that we know the uuid for pkg
309
+ return locate_package (found) # restart search now that we know the uuid for pkg ( TODO : the existance of this line of code is probably a bug)
285
310
end
286
311
else
287
312
for env in load_path ()
@@ -362,9 +387,18 @@ function project_deps_get(env::String, name::String)::Union{Nothing,PkgId}
362
387
return nothing
363
388
end
364
389
390
+ function project_deps_list (env:: String ):: Union{Nothing,Vector{PkgId}}
391
+ project_file = env_project_file (env)
392
+ if project_file isa String
393
+ return explicit_project_deps_list (project_file)
394
+ elseif project_file
395
+ return implicit_project_deps_list (env)
396
+ end
397
+ return nothing
398
+ end
399
+
365
400
function manifest_deps_get (env:: String , where :: PkgId , name:: String ):: Union{Nothing,PkgId}
366
- uuid = where . uuid
367
- @assert uuid != = nothing
401
+ @assert where . uuid != = nothing
368
402
project_file = env_project_file (env)
369
403
if project_file isa String
370
404
# first check if `where` names the Project itself
@@ -375,14 +409,34 @@ function manifest_deps_get(env::String, where::PkgId, name::String)::Union{Nothi
375
409
return PkgId (pkg_uuid, name)
376
410
end
377
411
# look for manifest file and `where` stanza
378
- return explicit_manifest_deps_get (project_file, uuid , name)
412
+ return explicit_manifest_deps_get (project_file, where , name)
379
413
elseif project_file
380
414
# if env names a directory, search it
381
415
return implicit_manifest_deps_get (env, where , name)
382
416
end
383
417
return nothing
384
418
end
385
419
420
+ function manifest_deps_list (env:: String , where :: PkgId ):: Union{Nothing,Vector{PkgId}}
421
+ @assert where . uuid != = nothing
422
+ project_file = env_project_file (env)
423
+ if project_file isa String
424
+ # first check if `where` names the Project itself
425
+ proj = project_file_name_uuid (project_file, where . name)
426
+ if proj == where
427
+ # if `where` matches the project, use [deps] section as manifest, and stop searching
428
+ return explicit_project_deps_list (project_file)
429
+ end
430
+ # look for manifest file and `where` stanza
431
+ return explicit_manifest_deps_list (project_file, where )
432
+ elseif project_file
433
+ # if env names a directory, search it
434
+ return implicit_manifest_deps_list (env, where )
435
+ end
436
+ return nothing
437
+ end
438
+
439
+
386
440
function manifest_uuid_path (env:: String , pkg:: PkgId ):: Union{Nothing,String}
387
441
project_file = env_project_file (env)
388
442
if project_file isa String
402
456
403
457
# find project file's top-level UUID entry (or nothing)
404
458
function project_file_name_uuid (project_file:: String , name:: String ):: PkgId
405
- uuid = dummy_uuid (project_file)
406
459
d = parsed_toml (project_file)
407
460
uuid′ = get (d, " uuid" , nothing ):: Union{String, Nothing}
408
- uuid′ === nothing || (uuid = UUID (uuid′) )
461
+ uuid = uuid ′ === nothing ? dummy_uuid (project_file) : UUID (uuid′)
409
462
name = get (d, " name" , name):: String
410
463
return PkgId (uuid, name)
411
464
end
433
486
434
487
# given a directory (implicit env from LOAD_PATH) and a name,
435
488
# check if it is an implicit package
489
+ # TODO : aren't we supposed to first check for the Project file first and see if it declares a path?
436
490
function entry_point_and_project_file_inside (dir:: String , name:: String ):: Union{Tuple{Nothing,Nothing},Tuple{String,Nothing},Tuple{String,String}}
437
491
path = normpath (joinpath (dir, " src" , " $name .jl" ))
438
492
isfile_casesensitive (path) || return nothing , nothing
472
526
# return `nothing` if `name` is not found
473
527
function explicit_project_deps_get (project_file:: String , name:: String ):: Union{Nothing,UUID}
474
528
d = parsed_toml (project_file)
475
- root_uuid = dummy_uuid (project_file)
476
529
if get (d, " name" , nothing ):: Union{String, Nothing} === name
477
530
uuid = get (d, " uuid" , nothing ):: Union{String, Nothing}
478
- return uuid === nothing ? root_uuid : UUID (uuid)
531
+ return uuid === nothing ? dummy_uuid (project_file) : UUID (uuid)
479
532
end
480
533
deps = get (d, " deps" , nothing ):: Union{Dict{String, Any}, Nothing}
481
534
if deps != = nothing
@@ -485,29 +538,57 @@ function explicit_project_deps_get(project_file::String, name::String)::Union{No
485
538
return nothing
486
539
end
487
540
541
+ function explicit_project_deps_list (project_file:: String ):: Union{Nothing,Vector{PkgId}}
542
+ d = parsed_toml (project_file)
543
+ list = PkgId[]
544
+ name = get (d, " name" , nothing ):: Union{String, Nothing}
545
+ if name != = nothing
546
+ uuid = get (d, " uuid" , nothing ):: Union{String, Nothing}
547
+ uuid = uuid === nothing ? dummy_uuid (project_file) : UUID (uuid)
548
+ push! (list, PkgId (uuid, name))
549
+ end
550
+ deps = get (d, " deps" , nothing ):: Union{Dict{String, Any}, Nothing}
551
+ if deps != = nothing
552
+ for (name, uuid) in deps
553
+ push! (list, PkgId (UUID (uuid), name))
554
+ end
555
+ end
556
+ return list
557
+ end
558
+
488
559
# find `where` stanza and return the PkgId for `name`
489
560
# return `nothing` if it did not find `where` (indicating caller should continue searching)
490
- function explicit_manifest_deps_get (project_file:: String , where :: UUID , name:: String ):: Union{Nothing,PkgId}
561
+ function explicit_manifest_deps_get (project_file:: String , where :: PkgId , name:: String ):: Union{Nothing,PkgId}
491
562
manifest_file = project_file_manifest_path (project_file)
492
563
manifest_file === nothing && return nothing # manifest not found--keep searching LOAD_PATH
493
564
d = parsed_toml (manifest_file)
494
565
found_where = false
495
- found_name = false
496
- for (dep_name, entries) in d
497
- entries:: Vector{Any}
566
+ entries = get (d, where . name, nothing ):: Union{Vector{Any}, Nothing}
567
+ if entries != = nothing
498
568
for entry in entries
499
569
entry = entry:: Dict{String, Any}
500
570
uuid = get (entry, " uuid" , nothing ):: Union{String, Nothing}
501
571
uuid === nothing && continue
502
- if UUID (uuid) === where
572
+ if UUID (uuid) === where . uuid
503
573
found_where = true
504
574
# deps is either a list of names (deps = ["DepA", "DepB"]) or
505
575
# a table of entries (deps = {"DepA" = "6ea...", "DepB" = "55d..."}
506
576
deps = get (entry, " deps" , nothing ):: Union{Vector{String}, Dict{String, Any}, Nothing}
507
577
deps === nothing && continue
508
578
if deps isa Vector{String}
509
579
found_name = name in deps
510
- break
580
+ if found_name
581
+ # we have a unique name for each dep
582
+ name_deps = get (d, name, nothing ):: Union{Nothing, Vector{Any}}
583
+ if name_deps === nothing || length (name_deps) != 1
584
+ error (" expected a single entry for $(repr (name)) in $(repr (project_file)) " )
585
+ end
586
+ entry = first (name_deps:: Vector{Any} ):: Dict{String, Any}
587
+ uuid = get (entry, " uuid" , nothing ):: Union{String, Nothing}
588
+ uuid === nothing && return nothing
589
+ return PkgId (UUID (uuid), name)
590
+ end
591
+ break # TODO : it seems wrong that we use all of break, continue, and return nothing to handle the failure cases here
511
592
else
512
593
deps = deps:: Dict{String, Any}
513
594
for (dep, uuid) in deps
@@ -521,18 +602,54 @@ function explicit_manifest_deps_get(project_file::String, where::UUID, name::Str
521
602
end
522
603
end
523
604
found_where || return nothing
524
- found_name || return PkgId (name)
525
- # Only reach here if deps was not a dict which mean we have a unique name for the dep
526
- name_deps = get (d, name, nothing ):: Union{Nothing, Vector{Any}}
527
- if name_deps === nothing || length (name_deps) != 1
528
- error (" expected a single entry for $(repr (name)) in $(repr (project_file)) " )
605
+ return PkgId (name)
606
+ end
607
+
608
+ # find `where` stanzas
609
+ function explicit_manifest_deps_list (project_file:: String , where :: PkgId ):: Union{Nothing,Vector{PkgId}}
610
+ manifest_file = project_file_manifest_path (project_file)
611
+ manifest_file === nothing && return nothing # manifest not found--keep searching LOAD_PATH
612
+ d = parsed_toml (manifest_file)
613
+ found_where = false
614
+ entries = get (d, where . name, nothing ):: Union{Vector{Any}, Nothing}
615
+ if entries != = nothing
616
+ for entry in entries
617
+ entry:: Dict{String, Any}
618
+ uuid = get (entry, " uuid" , nothing ):: Union{String, Nothing}
619
+ uuid === nothing && continue
620
+ if UUID (uuid) === where . uuid
621
+ found_where = true
622
+ # deps is either a list of names (deps = ["DepA", "DepB"]) or
623
+ # a table of entries (deps = {"DepA" = "6ea...", "DepB" = "55d..."}
624
+ deps = get (entry, " deps" , nothing ):: Union{Vector{String}, Dict{String, Any}, Nothing}
625
+ deps === nothing && continue
626
+ list = [where ]
627
+ if deps isa Vector{String}
628
+ for name in deps
629
+ # we have a unique name for each dep
630
+ name_deps = get (d, name, nothing ):: Union{Nothing, Vector{Any}}
631
+ if name_deps === nothing || length (name_deps) != 1
632
+ error (" expected a single entry for $(repr (name)) in $(repr (project_file)) " )
633
+ end
634
+ entry = first (name_deps:: Vector{Any} ):: Dict{String, Any}
635
+ uuid = get (entry, " uuid" , nothing ):: Union{String, Nothing}
636
+ uuid === nothing && continue
637
+ push! (list, PkgId (UUID (uuid), name))
638
+ end
639
+ else
640
+ for (dep, uuid) in deps:: Dict{String, Any}
641
+ push! (list, PkgId (UUID (uuid:: String ), dep))
642
+ end
643
+ end
644
+ return list
645
+ end
646
+ end
529
647
end
530
- entry = first (name_deps:: Vector{Any} ):: Dict{String, Any}
531
- uuid = get (entry, " uuid" , nothing ):: Union{String, Nothing}
532
- uuid === nothing && return nothing
533
- return PkgId (UUID (uuid), name)
648
+ found_where || return nothing
649
+ return [where ]
534
650
end
535
651
652
+
536
653
# find `uuid` stanza, return the corresponding path
537
654
function explicit_manifest_uuid_path (project_file:: String , pkg:: PkgId ):: Union{Nothing,String}
538
655
manifest_file = project_file_manifest_path (project_file)
@@ -587,6 +704,40 @@ function implicit_project_deps_get(dir::String, name::String)::Union{Nothing,Pkg
587
704
return proj
588
705
end
589
706
707
+ # look for entry points accessible to names in a top-level package (no environment)
708
+ # from an implicit environment (e.g. stdlib)
709
+ function implicit_project_deps_list (dir:: String ):: Vector{PkgId}
710
+ list = PkgId[]
711
+ function add_path (name, project_file:: Nothing )
712
+ push! (list, PkgId (name))
713
+ nothing
714
+ end
715
+ function add_path (name, project_file)
716
+ id = project_file_name_uuid (project_file, name, cache)
717
+ if id. name == name
718
+ push! (list, id)
719
+ end
720
+ nothing
721
+ end
722
+ for fname in readdir (dir)
723
+ fpath = joinpath (dir, fname)
724
+ s = stat (fpath)
725
+ isjl = endswith (fname, " .jl" )
726
+ name = isjl ? fname[1 : prevind (fname, end - 2 )] : fname
727
+ if isjl && isfile (s)
728
+ add_path (name, nothing )
729
+ elseif isdir (s)
730
+ if isjl
731
+ path, project_file = entry_point_and_project_file_inside (fpath, name)
732
+ path === nothing || add_path (name, project_file)
733
+ end
734
+ path, project_file = entry_point_and_project_file_inside (fpath, fname)
735
+ path === nothing || add_path (fname, project_file)
736
+ end
737
+ end
738
+ return list
739
+ end
740
+
590
741
# look for an entry-point for `name`, check that UUID matches
591
742
# if there's a project file, look up `name` in its deps and return that
592
743
# otherwise return `nothing` to indicate the caller should keep searching
@@ -601,6 +752,18 @@ function implicit_manifest_deps_get(dir::String, where::PkgId, name::String)::Un
601
752
return PkgId (pkg_uuid, name)
602
753
end
603
754
755
+ # look for entry-point for pkg names, if if there's a project file,
756
+ # otherwise return `nothing` to indicate the caller should keep searching
757
+ function implicit_manifest_deps_list (dir:: String , where :: PkgId ):: Union{Nothing,Vector{PkgId}}
758
+ @assert where . uuid != = nothing
759
+ project_file = entry_point_and_project_file (dir, where . name)[2 ]
760
+ project_file === nothing && return nothing # a project file is mandatory for a package with a uuid
761
+ proj = project_file_name_uuid (project_file, where . name)
762
+ proj == where || return nothing # verify that this is the correct project file
763
+ # this is the correct project, so stop searching here
764
+ return explicit_project_deps_list (project_file)
765
+ end
766
+
604
767
# look for an entry-point for `pkg` and return its path if UUID matches
605
768
function implicit_manifest_uuid_path (dir:: String , pkg:: PkgId ):: Union{Nothing,String}
606
769
path, project_file = entry_point_and_project_file (dir, pkg. name)
@@ -1792,6 +1955,44 @@ function stale_cachefile(modpath::String, cachefile::String)
1792
1955
end
1793
1956
end
1794
1957
1958
+
1959
+ # starting with a particular `pkg` as the root, list all packages that can possibly be loaded
1960
+ function identify_all_deps (pkg:: PkgId )
1961
+ lists = Dict {PkgId,Vector{PkgId}} ()
1962
+ q = [pkg]
1963
+ while ! isempty (q)
1964
+ pkg = pop! (q)
1965
+ get! (lists, pkg) do
1966
+ deps = identify_package_deps (pkg)
1967
+ append! (q, deps)
1968
+ deps
1969
+ end
1970
+ end
1971
+ return lists
1972
+ end
1973
+
1974
+ # map locate_package across a list of pkgs
1975
+ function locate_all_packages (pkgs)
1976
+ pths = Dict {PkgId,String} ()
1977
+ for pkg in pkgs
1978
+ pth = locate_package (pkg)
1979
+ pth === nothing || (pths[pkg] = pth)
1980
+ end
1981
+ return pths
1982
+ end
1983
+
1984
+ # get a tree of the information for all loadable packages starting from a
1985
+ # particular `pkg` as the root
1986
+ function find_all_packages (pkg:: PkgId )
1987
+ deps = identify_all_deps (pkg)
1988
+ return locate_all_packages (keys (deps)) => deps
1989
+ end
1990
+ find_all_packages (name:: String ) =
1991
+ find_all_packages (PkgId (" " ), name) # `where` without a uuid will be ignored
1992
+ find_all_packages (where :: Union{Module,PkgId} , name:: String ) =
1993
+ find_all_packages (something (identify_package (where , name)))
1994
+
1995
+
1795
1996
"""
1796
1997
@__FILE__ -> AbstractString
1797
1998
0 commit comments