Last update March 7, 2012

Library Naming Convention



A Modest Proposal for Standardization in Naming of D Libraries

(by Gregor Richards)

Purpose

As there is much misunderstanding of the purpose of this library naming convention, it's outlined here:

  • The Library Naming Convention has no bearing whatsoever over the names or arrangement of source files.
  • The Library Naming Convention does not intend to replace build-style 'build everything from source with include paths' building, it intends to be a superior alternative
  • The Library Naming Convention is a convention. That is, no one is required to conform to it, but (were it to be accepted) it would be inconveniencing not to.
  • The Library Naming Convention allows the source maintainer to choose what packages to make into libraries, with what level of division, etc. It has few rules over what is and what isn't in given libraries, as its purpose is the naming of libraries, not the content.
    • The only rule is that any module must be in the most specifically-named library corresponding to that module. That is, if you have libD.a.b.so.0 and libD.a.so.0, the module a.b.c should be in libD.a.b.so.0, not libD.a.so.0. Doing otherwise is fairly ridiculous anyway.

Scope

I apparently need to put this up front: This is not intended to replace the common standard of simply compiling the entire application from source, with dependencies in an -I'd directory sent to build. That works fine if that's how you want to build your libraries.

This is intended to be a standard for the use of library files, if you choose to use them instead of using the current (non-scalable) standard.

Outline

At present, there is to my knowledge no standard for naming D libraries (that is, .a, .lib, .so and .dll files). There's no immediate problem posed, as there is very little D code that forms libraries - but with any luck, the amount of code doing so will increase.

For background on me and why I'm interested in this, both my job and several of my personal projects involve compilation and linking of software in UNIX environments, so I'm constantly exposed to inconsistent libraries.

My experience with the D community has shown that few people at the moment seem to care about the creation of libraries, but I believe that desire will increase as popularity does. I consider the fact that there is no standard for naming to be a significant problem, as a lack of a clear naming convention will likely make the entire compilation process difficult (think of autoconf).

I intend to outline in this document a naming convention for library files which will be easily parseable by both humans and software (such as [d]build).

Note that this document does not currently target Windows .dll and .lib files, as I know nothing whatsoever about them. If anyone would like to step in and make modifications, feel free.

Don't Break from Convention

(This section applies only to UNIX)

The UNIX convention for naming of libraries is:

  lib<name>.so.<major>.<minor>.<revision>

with symlinks for each of:
  lib<name>.so.<major>.<minor>
  lib<name>.so.<major>

and also a symlink of:
  lib<name>.so

if the library has its headers installed. There is no compelling reason to break from this standard, so it is left as such in this document.

For .a files, the file name is more simple:

  lib<name>.a

Packages and Libraries

Package names should be directly reflected in the file name of the library. Furthermore, the fact that this library is written in D should also be in the name of the library. To this end, the basic form is:

  libD.<package name>.<extension>

Of course, packages are more complex than that, with subpackages, etc. The division becomes more complex for packages such as:
  • a.*
  • a.b.*
  • a.c.*
  • a.d.*
Each library must only be inclusive of /modules/ in the package, not subpackages (though it may be either at the creator's whimsy). One way to comply would be to simply have a single file:
  • libD.a.<extension>
However, if one wanted to subdivide, it could also have several:
  • libD.a.b.<extension>
  • libD.a.c.<extension>
  • libD.a.d.<extension>
Now let's imagine the situation of the package 'a' having a module named 'foo'. a.foo doesn't belong in its own library (we've already determined that libraries correspond to packages), so it will go into its own file:
  • libD.a.<extension>
With this subdivision, we now have four files:
  • libD.a.<extension>
  • libD.a.b.<extension>
  • libD.a.c.<extension>
  • libD.a.d.<extension>
The advantage is that any given module can always be found in the most specifically named, existant library.

Now, in all likelihood, one or all of the subpackages depend on modules in the superpackage. Luckily, this is trivial to detect in a build system. Since all of the imports will be iterated over, a list of packages to test for can be built that is guaranteed to capture everything (so long as the installation is complete).

Version Hell

(Or DLL Hell as it's affectionately known on Windows)

Version Hell isn't too bad with UNIX-style .so files, so long as people comply to this simple standard:

  1. Only increase the major version if you've made non-backwards-compatible changes.
  2. Increase the minor version when you've added but not removed symbols.
  3. Increase the revision for all other changes.
  4. Don't worry about D ABI changes - they're well beyond the scope of library naming convention.
I would recommend that a .DLL version of this spec incorporate some sort of versioning similar to UNIX.

Build Tools

With this setup, a build tool (such as build) has a very trivial task to generate a library name from an import. It simply needs to, starting with the most specific and expanding to the most general, check whether files corresponding to the name exist. So, if an import for a.b.c.d.e.f.g had been specified and the imported file was not in the included source, it would search (in this order):

  • libD.a.b.c.d.e.f.g.so
  • libD.a.b.c.d.e.f.g.a
  • libD.a.b.c.d.e.f.so
  • libD.a.b.c.d.e.f.a
  • libD.a.b.c.d.e.so
  • libD.a.b.c.d.e.a
  • libD.a.b.c.d.so
  • libD.a.b.c.d.a
  • libD.a.b.c.so
  • libD.a.b.c.a
  • libD.a.b.so
  • libD.a.b.a
  • libD.a.so
  • libD.a.a
If none of them were found, it fails. Because each import carries its own imports with it, it will simply resolve dependencies in the next iteration over all the imports.

Generation

The generation of these files is a bit more complex, but any build tool would only need a tiny bit of input from the user (exactly where to subdivide) to be able to produce them.

Basically, the build tool (eg build) should default to building one library for each top-level package directory. For software that wish to diverge from this default, a 'libraries' file (with a name to-be-determined, perhaps just 'libraries') is put in the top directory of the software's package, which contains a list of every library it wants to produce. The build tool parses this, and for each .d file figures out which library it should be in. This file is simply a newline-delimited list of libraries, in this format:

 lib: a
 lib: a.b
 lib: a.c
 lib: b

If the 'libraries' file neglects to include one or more package directory at all, the build tool simply falls back to the default of one library per top-level package directory for those packages.

Platform-Specific? Modules

The 'libraries' file must also include version information, to explicitly disclude or include files for unmatching version strings (that is, compilers and platforms). For this purpose, there is a 'version' directive for 'libraries' files as well.

To specify that a specific module or package should only be included when a specified version string is set (in this case, only a.Win on Windows):

 version(Windows): a.Win

To specify that a specific module or package should only be included when a specified version string is unset (in this case, only a.Nowin on non-Windows):
 version(!Windows): a.Nowin

The version directive should also take some simply logic:
 version(Windows || Posix): a.NotFreakyOs?

Note that this version info is not currently exposed by the compiler, so, until it is, build tools will need to be a bit hackish to detect only a specified set of version strings, for example:
 bool versionMatches(char[] ver) {
   if (ver == "DigitalMars") { version (DigitalMars) { return true; } else { return false; } }
   if (ver == "GNU") { version (GNU) { return true; } else { return false; } }
   if (ver == "Windows") { version (Windows) { return true; } else { return false; } }
   if (ver == "Posix") { version (Posix) { return true; } else { return false; } }
   if (ver == "linux") { version (linux) { return true; } else { return false; } }
   // et cetera
   return false;
 }

Questions?

Please post any questions about this document here, and I'll try to respond.

Furthermore, the fact that this library is written in D should also be in the name of the library Why? Does any other languages do such things? Give examples. For me it feels like that would keep D from becoming a first-language citizen in a way.


FrontPage | News | TestPage | MessageBoard | Search | Contributors | Folders | Index | Help | Preferences | Edit

Edit text of this page (date of last change: March 7, 2012 20:36 (diff))