On Tuesday, 19 October 2021 at 04:03:38 UTC, Mathias LANG wrote:
> I was looking at a few packages recently, and noticed a bothering trend: Single-module libraries ending up at the top level.
Why is it bothering ? Well, here's a little puzzle: Does the following code compile?
import std.stdio;
void main ()
{
writeln(foo.stringof);
}
The answer is "maybe". In most case, it will not. However, if the file this resides in is named foo.d
, it will compile. Because the module name accounts for an identifier that can be addressed in the scope of the module (and other modules that import it).
This is ultimately because D modules just reuse the notion of import paths from C. I had to find a better way in my language in order to handle self-rebuilds, where there's name collisions between compiler runtime and compiled runtime packages, but the same approach would also solve the issue in D, so let me outline it.
Basically, we replace import paths (-I~/.dub/packages/bla/src
) with package paths:
-Pbla:~/.dub/packages/bla/src
As you can see, each package is named. This allows us to explicitly specify which packages a package can see and access:
-Pmain:src:bla,bla2
As it walks imports, the compiler tracks each module's package. When evaluating an import statement, only files in the current package or a direct dependency of the current package are considered.
Thus, your 'foo' problem will only happen if you directly depend on package "foo" in your dub.json.
This approach is fully backwards compatible with include paths. It also allows other funky things, such as per-package settings (-i, -deps, -g, -debug etc) which solves a whole host of problems with dub. For instance, a static library can safely be used, even if the main program's source is being built with different flags.