Software dependencies: A not-too-technical introduction
- Programs depend on other programs, often with many deep levels of indirection.
- Million of packaged FLOSS programs are available through public repositories for easy dependencies download and installation.
- How dependent programs relate to and interact with each other can be more or less intimate.
- Each program's license FLOSS requirements and obligations vary based on the relationship and interaction between dependent.
You need to know:
- The whole chain of program or package dependencies
- The purpose and use of each program or package in that chain (test, tool, runtime)
- Which dependent are shipped and redistributed with your product, application or library vs. which may be installed by your user
- The license of each dependency in this chain
How software is built
Larger software systems and products are commonly assembled from software components. Each component is some collection of programs. And components frequently re-use other components.
Each component is usually delivered as a package. A package is typically an archive that contains the programs. A package also commonly contains information about the component name & version, what it is about, who wrote it, sometimes its license, etc. And also what other components may be required by this component: this is called a dependent component or a "dependency". In formal terms, I would define a package as the set of exact software programs and files that are distributed or used for a given component.
A program may require one or more other programs to run (the "dependency-ies"). The dependencies can be recursive potentially creating very deep relationships. Sometimes the dependency is only for testing or "building/compiling" the program - i.e. a development or "dev" dependency. Sometimes it is needed to run the program or sometimes it can be optional to provide extra features.
Some dependencies for a program may be rather generic and sometimes implied. At a top level many programs can have a dependency on the operating system, an application server, a database server, etc. These type of dependencies are often not stated explicitly. For example a Java program requires a Java Virtual Machine (JVM) to run and the fact a program is written in Java implies this and is not explicitly stated by most Java packages as many/most Java programs will not depend on a specific version of that JVM.
Dependency management is the approach practiced by software programmers to specify, provision/download, install, update and generally manage the set of dependent code that their product or application relies on.
Managing dependencies is evolving to be rather sophisticated and several tools and public package repositories are helping programmers dealing with eventually large volume of dependencies. A large number of these dependencies are for free, libre and open source-licensed (FLOSS) components and their packages.
Each operating system / language / framework tends to have its own (eco) system for FLOSS components:
- Documenting dependencies in one or more configuration or "meta" files,
- Downloading (aka. provisioning) the dependent components from a public FLOSS or private repository of pre-built or pre-packaged programs.
Some of these repositories and tools include:
- Ruby - GEMs from RubyGems,
- Python - Wheels from Pypi,
- Java - JARs from Maven,
- Linux packages such as RPM and Debian,
- and many more.
Dependencies are often weakly defined and variable: A dependency statement may include a range of possible versions for a dependent package and if a dependecy is "required" or "optional". Package systems compute (aka. "resolve") a list of concrete dependencies when you provision packages using these definitions.
The dependency relationship between two programs may be more or less "intimate":
- At arm's length, when the programs are entirely separated and may only communicate by writing or reading files, or by network interaction
- At a handshake level - where the programs call each other's subroutines (aka "functions") directly and exchange data directly
- Or extremely intimate when merged or combined into a single larger (executable) program
Program dependencies: programs can be compiled and combined
Often programs are compiled: the text of the programs source code is transformed in a binary code that the machine can understand and execute. For instance programming languages such as C, C++, Java, Go require this "compilation".
With the compilation process, several programs can be "combined" into larger (binary) programs. Combining programs together this way is often called "static linking" when things are "merged" into a larger binary program.
Free, libre and open source licensing and dependencies
Many software licenses have different requirements for source code and binaries (BSD, MIT), have specific terms about the implication of how programs are combined and depend on each other (L/GPL). Limited Copyleft and Copyleft licenses impose some constraints on how other (proprietary or not) programs can be combined or interact with copyleft code. For instance, the LGPL explicitly discuss static vs. dynamic linking. These are concepts originally defined in compiled languages such as C and C++.
They do not always translate well to other "dynamic" languages and this require some adjustments to apply these terms in these other language contexts.
Permissive licenses typically impose few constraints on how you can combine programs.
For Copyleft licenses, how (proprietary- or non-Copyleft- licensed) programs and Copyleft-licensed programs are used together, how they depend and interact with each other is the essence of what triggers the Copyleft clauses of the GPL and LGPL.
Remember that the relationship between programs and their dependencies can be more or less intimate: Arm's length, Handshake, Merged. Copyleft licenses may impact proprietary or non-copyleft code based on the nature of the dependencies and interaction between programs:
- Any Handshake or Merged relationship with a GPL-licensed program implies that GPL becomes the primary license for that combined program
- Any Merged relationship with LGPL-licensed programs implies that LGPL becomes the primary license for that combined program
Arm's length is similar to "independent process" or "command line", Handshake is similar to "dynamic linking", Merged is similar to "static linking".
Relationships may flow through the Dependency chains and the related licensing requirement may also follow through.
What if a proprietary program depends on a program that depends on a program that depends on a program that is GPL-licensed? It depends!
- What is the type of relationships at each step?
- What is the license of the intermediate programs?
Eventually the GPL license may flow up or down to the proprietary program for an "Handshake" or "Merged" style of relation, unless some licenses have exception to stop this flow (e.g. such as the Linux in user space exception to the GPL license) or the nature of the interactions changes through this chain. (such as when a dependent program is used at "Arm's length").
An approach to handle dependencies and license compliance
To properly handle dependencies -- whether you write proprietary or FLOSS-licensed code -- my recommended approach would be to:
- Evaluate the likely dependencies for a new component or package during selection / evaluation
- Confirm actual dependencies when you build for your product, application or library
- Ensure that you consider the whole chain of dependencies, both direct and indirect and at full depth
- Consider the purpose of each program or package in that chain (test, tool, compilation, runtime, etc.)
- Determine which dependent are used only at development time, which redistributed with your product, application or library vs. which may be installed by your user
- Determine the license of each dependency in this chain
- Evaluate what licensing requirements flow up or down this chain and determine what actions are required to comply with licensing
Using these elements you can define a policy for which relationship may be acceptable or not in your context and what are the obligations derived from using dependent packages or programs.