Many developers are tempted to create their own automated system to build and install their software and other programs and libraries required by it. This strategy, known as bundling invariably creates more problems than it solves for both developers and end-users.
There are many problems with bundling:
You cannot begin to imagine what your build system will encounter on the end-users' systems. They may be running different operating systems and many of their systems are badly managed. Your build system will fail much of the time, leading to many inquiries from frustrated users.
Attempts to help all of these users will result in a build system that grows in complexity without limit and becomes a major drain on your time. Many developers get frustrated with this situation and end up limited support to one or a few platforms. This is very unfortunate for the community, since different developers use different platforms, and end-users may not be able to install their programs on the same machine as a result, unless they resort to using virtual machines or other systems adding unnecessary overhead.
Libraries you bundle may conflict with installed versions during build and/or at run time.
Bundling dependencies also makes it more difficult for people to create packages for your software, since package managers are designed to handle things in a modular fashion.
You might argue that you need a specific version of a library that it different from what's available in your operating system's package manager. It may actually be easier to make your software work with the mainstream version of the library than to bundle another version. Even if you really need an alternative version, it's generally less problematic to create a separate package that can coexist with the mainstream version than it is to bundle the library with your software.
If you need to modify a function in a dependent library, then rather than bundle the entire library, you could simply include the modified function in your code until your patches have been incorporated "upstream". If the linker finds the function in your program, it simply won't look for it in the installed library. Hence, your patched version will take precedence.
Bundling is, in effect, inventing and maintaining your own esoteric package manager. If you try it, you will soon discover how complex the task is and end up regretting it.
If you instead aim at making it easy to include your software in existing package managers, you will be free from all these headaches and able to focus on developing your code. Ways to do this are described in the section called “Package-friendly Software”.
99% of software projects can be built using a simple Makefile.
Most unnecessary complexity in build systems is due to misguided attempts to automate the building of a package and some or all of the other packages on which it depends, such as libraries and build tools.
Package managers like Debian packages, FreeBSD ports, Gentoo Portage, MacPorts, pkgsrc, etc. are designed to automatically install software and track dependencies. They are used and maintained by many people around the world and thus are very sophisticated and reliable.
If you make your software easy to deploy with one package manager, it will be easy to deploy with all of them.
For example, if you develop on Debian, Ubuntu, or any other Debian-based system, maintain a Debian package instead of a custom build system.
At first it may seem that this will only serve Debian users, but in fact it will serve everyone better in the long run. If you devise a simple Makefile that the Debian packaging system can use without patching or custom environment settings, then it will be easy for others to create a FreeBSD port, a MacPort, a Portage port, a pkgsrc package, an RPM, etc.
You won't have to discuss deployment issues with end users. You'll only need to deal with a handful of very savvy people who create and maintain various packages for deploying your software.
Package managers provide by far the easiest way to install, uninstall, and upgrade software.
Unfortunately, many software developers attempt to replicate the functionality of package managers with esoteric, custom build systems designed only for their software and the software it depends on.
Almost none of them work well, because developers don't have the time or resources to test them in any environment other than their own. They usually make it more difficult for end users to install your software. Your custom build system cannot come close to replicating the capabilities of a highly evolved package manager.
As a software developer, you can help yourself and end-users immensely by simply making it package-friendly, i.e. making it easy to incorporate your software into existing package managers. Ways to do this are described in the section called “Package-friendly Software”.
Doing so leverages the work of thousands of other people, saving you a lot of work, and saving end users a lot of problems.
If you let package managers do what they're meant for, even Makefile generators like CMake and GNU autotools are largely unnecessary. A simple Makefile that respects the environment, utilizing standard make variables like CC, CXX, FC, LD, CFLAGS, CXXFLAGS, FFLAGS, and LDFLAGS, will allow your software to be easily built by most package managers.
The argument for using autotools, cmake, or some other high level build system is usually based on past problems using makefiles. However, those problems are usually the result of badly written makefiles, not the make system itself. If people don't know how to write a good makefile, adding another layer of software that they don't know how to use is not a solution. First master the use of make and do your best to make it work for your project. Then see if you really need more complexity in the build system.
Alan Greenspan famously stated that any bank that's too big to fail (without impacting the economy) is too big. Similar language can be used to describe software build systems: A project that is too big for a simple Makefile is too big, and should probably be divided into several smaller projects that can be built and installed independently of each other.
Package managers all want basically the same thing: The ability to control the building and installation of your software. If you provide a simple Makefile that works well with one package manager, it will be easy to port your software to any other. Your software will become easy to install and widely available with very little effort on your part.
For example, if you develop on a Debian-based system and focus on supporting installation your software via Debian packages, you will quickly learn how to make it easy to create a Debian package for your software. In doing so, you will fortuitously make it easy for yourself or others to create a Cygwin package, a FreeBSD port, a MacPort, an RPM, a pkgsrc package, etc. Others can even use your Debian package as a model, to see what other packages it requires, etc.
All in all, this will minimize the development effort needed to make the software accessible on numerous platforms, as well as minimize problems for end-users trying to install it.