Have you ever had software work on your machine and not work on a different machine? Missing dependencies is a common cause of software that works in one environment and not in another. Modern software is most often a tapestry of libraries and frameworks that must be present on a machine to function properly. Even the simple “Hello World” programs that are used so frequently to introduce programming languages require additional libraries to run outside of a development environment.
Windows Environments: Dependency Walker
Dependency Walker (depends.exe) is a tool that has been around for many generations of Microsoft windows. This tool is your basic tool for diagnosing Windows dependency problems in the field.
Unix Like Environments: ldd
ldd is a shell tool used to list dynamic dependencies (shared object files). This is your basic tool for diagnosing dependency problems in unix like environments.
Resolving Dependency Errors
Why is the dependency required?
Sometimes project templates for ISEs can incorporate common dependencies that you may not need for your specific application. Always determine why a dependency is needed. You should be able to account for every library and framework your application requires. This knowledge may also extend to the dependencies of your dependencies and so forth. While this process of tracing dependencies can be tedious, it can teach you a lot about the frameworks you use and the operating systems you distribute on. In simple terms, don’t carry any more dependencies than necessary. Each dependency is a potential vector for bugs and vulnerabilities.
Is your application built correctly?
Some operating systems (i.e. Windows) distinguish between debug and release libraries. There will often be restrictions on distributing debug libraries. Debug libraries are often associate with the developer’s machine only. The QT framework on windows is a perfect example of this. Applications linked for debug link to QT libraries with ‘d’ prefix (qt5cored, qt5guid, qt5networkd, etc.) This is case where something working on a developer machine and not on another machine is expected behavior.
Can static linking help your problem?
Some languages, such as C++, allow dependencies to be completely pulled into an executable at compile time. This means that the operating system does not need to resolve the dependency at run time because everything is already in the executable. The downside of static linking is that executable sizes increase and dependency updates require an entirely new application to be published. This had helped me in the past to incorporate Boost program options into an application. Static linking is not only language dependent, but also conditional on your licensing situation. Static linking to certain GPL works can cause the linking application to become a derivative of the GPL work and force the linking application to comply with the GPL terms. Consult appropriate legal and technical council for more information on this advanced topic.
Can you detect when the dependency is not met?
One common approach to dependency resolution is to check that prerequisites are met before allowing the application to be used. Visual Studio is a prime example of this approach. Visual Studio requires a number of companion technologies to be installed before the application can be installed. This does not prevent future problems because a user can uninstall still remove a prerequisite at a later time but it does set a nice baseline for what the publisher expected the system state to be.
Would setting minimum system requirements help?
Sometimes large sets of dependent files are distributed as a single update. An example of this would be service packs for Microsoft operating systems or names versions on Mac OSX (Yosemite, Snow Leopard, Mountain Lion, etc.) Requiring a specific update set can be an easy way to ensure dependencies are met. For example, when a minimum version of the .NET frame work is required.
Are you future proof?
Libraries change. Sometimes the way your software is written can cause unnecessary coupling between your application and the specific version of a library. My work supporting applications on Ubuntu has highlighted this idea. At some point in the life the software specific library versions get referenced. When transitions occur between 10.04 ->12.04, 12.04 -> 13.04 and 12.04->14.04 the default distribution for a library is based lined at a newer version. The older version are simply not present in the canonical package repositories. Sometimes members of the community post back borts for commonly used libraries but there is no guarantee this will be the case.
How Should the dependency be resolved?
Should you distribute the dynamic library or shared object file with your application? Should you rely on a different installer or updater to put the files there? How you answer these questions depends on the technology being used. Software is generally licensed, not sold. Software licensing dictates the terms that the software can be used and distributed. License restrictions may forbid the component from being installed by a third party. Others may all distribution as long as attribution is made. Some operating systems forbid or discourage the distribution of key system components. For example, it would be inappropriate to distribute a component that is normally updated through Windows Update. In some instances it may be appropriate to rely on a different installer to add and remove a component. The OpenSSL libraries are a good example of this. Every piece of software on the planet has bugs, including mission critical security software. Once you take ownership of distributing a library you are also on the hook for distributing all future security updates. If you don’t do this your software can either be disabled by system administrators or used as an attack vector against flawed software.