This post answers specifically the title question:
How does
Needs["foo`"]
find the file that defines context"foo`"
?
Get
, Needs
, Install
, OpenRead
, etc. all use FindFile
. How FindFile
resolves file names is discussed in:
FindFile
, Details section
I don't know the full details (it's complicated), but roughly FindFile
translates a context to a file path as follows:
FindFile["foo`"]
either
- Looks on
$Path
forfoo.m
,foo.wl
,foo.mx
or directoryfoo
- If a paclet declares the context
foo`
in itsKernel
extension, it translates to the correspondingRoot
directory: See PacletInfo.m documentation project, Kernel extension section.
Then if the result was a directory dir
, it continues to look for
dir/init.m
(orinit.wl
)dir/Kernel/init.m
(orinit.wl
)
If the result was a directory dir.mx
, it continues to look for
dir.mx/$SystemID/dir.mx
(insert the value of$SystemID
)
This is useful because .mx
files are not compatible across different platforms.
FindFile["foo"]
, where foo
is not a context,
- Looks on
$Path
forfoo
.
If the result is a directory, then it continues to look for
foo/$SystemID/foo
This is useful with Install
, when we need a separate executable for each $SystemID
, but we want to be able to use the same name to refer to them on any platform.
Notes
The context of a package, i.e. the context given in BeginPackage
that will contain the public package symbols, does not play any role in how FindFile
resolves a context name to a file path.
However, Needs
is different from Get
in that it expects the context passed to it to appear in $ContextPath
after the package has been loaded. This is why one needs to use BeginPackage
and not merely Begin
when writing a package. BeginPackage
will also add that context to $Packages
. Needs
uses $Packages
to determine if a package has already been loaded and avoid double-loading.
(Something interesting: BeginPackage
also updates Internal`$PackageDependencies
based on its second argument. I do not know where this is used.)
In short, the file names and the context of a package doesn't strictly need to be the same. But if it isn't, Mathematica will get confused.
All the rules above explain why the standard application directory structure is as described here. There is nothing strictly enforcing this particular structure, but the system is designed in a way that it expects to find structure in any package.