aboutsummaryrefslogtreecommitdiffstats
path: root/meson/docs/markdown/Design-rationale.md
diff options
context:
space:
mode:
Diffstat (limited to 'meson/docs/markdown/Design-rationale.md')
-rw-r--r--meson/docs/markdown/Design-rationale.md261
1 files changed, 261 insertions, 0 deletions
diff --git a/meson/docs/markdown/Design-rationale.md b/meson/docs/markdown/Design-rationale.md
new file mode 100644
index 000000000..462129e9c
--- /dev/null
+++ b/meson/docs/markdown/Design-rationale.md
@@ -0,0 +1,261 @@
+---
+title: Design rationale
+...
+
+This is the original design rationale for Meson. The syntax it
+describes does not match the released version
+==
+
+A software developer's most important tool is the editor. If you talk
+to coders about the editors they use, you are usually met with massive
+enthusiasm and praise. You will hear how Emacs is the greatest thing
+ever or how vi is so elegant or how Eclipse's integration features
+make you so much more productive. You can sense the enthusiasm and
+affection that the people feel towards these programs.
+
+The second most important tool, even more important than the compiler,
+is the build system.
+
+Those are pretty much universally despised.
+
+The most positive statement on build systems you can usually get (and
+it might require some coaxing) is something along the lines of *well,
+it's a terrible system, but all other options are even worse*. It is
+easy to see why this is the case. For starters, commonly used free
+build systems have obtuse syntaxes. They use for the most part global
+variables that are set in random locations so you can never really be
+sure what a given line of code does. They do strange and unpredictable
+things at every turn.
+
+Let's illustrate this with a simple example. Suppose we want to run a
+program built with GNU Autotools under GDB. The instinctive thing to
+do is to just run `gdb programname`. The problem is that this may or
+may not work. In some cases the executable file is a binary whereas at
+other times it is a wrapper shell script that invokes the real binary
+which resides in a hidden subdirectory. GDB invocation fails if the
+binary is a script but succeeds if it is not. The user has to remember
+the type of each one of his executables (which is an implementation
+detail of the build system) just to be able to debug them. Several
+other such pain points can be found in [this blog
+post](http://voices.canonical.com/jussi.pakkanen/2011/09/13/autotools/).
+
+Given these idiosyncrasies it is no wonder that most people don't want
+to have anything to do with build systems. They'll just copy-paste
+code that works (somewhat) in one place to another and hope for the
+best. They actively go out of their way not to understand the system
+because the mere thought of it is repulsive. Doing this also provides
+a kind of inverse job security. If you don't know tool X, there's less
+chance of finding yourself responsible for its use in your
+organisation. Instead you get to work on more enjoyable things.
+
+This leads to a vicious circle. Since people avoid the tools and don't
+want to deal with them, very few work on improving them. The result is
+apathy and stagnation.
+
+Can we do better?
+--
+
+At its core, building C and C++ code is not a terribly difficult
+task. In fact, writing a text editor is a lot more complicated and
+takes more effort. Yet we have lots of very high quality editors but
+only few build systems with questionable quality and usability.
+
+So, in the grand tradition of own-itch-scratching, I decided to run a
+scientific experiment. The purpose of this experiment was to explore
+what would it take to build a "good" build system. What kind of syntax
+would suit this problem? What sort of problems would this application
+need to solve? What sort of solutions would be the most appropriate?
+
+To get things started, here is a list of requirements any modern
+cross-platform build system needs to provide.
+
+### 1. Must be simple to use
+
+One of the great virtues of Python is the fact that it is very
+readable. It is easy to see what a given block of code does. It is
+concise, clear and easy to understand. The proposed build system must
+be syntactically and semantically clean. Side effects, global state
+and interrelations must be kept at a minimum or, if possible,
+eliminated entirely.
+
+### 2. Must do the right thing by default
+
+Most builds are done by developers working on the code. Therefore the
+defaults must be tailored towards that use case. As an example the
+system shall build objects without optimization and with debug
+information. It shall make binaries that can be run directly from the
+build directory without linker tricks, shell scripts or magic
+environment variables.
+
+### 3. Must enforce established best practices
+
+There really is no reason to compile source code without the
+equivalent of `-Wall`. So enable it by default. A different kind of
+best practice is the total separation of source and build
+directories. All build artifacts must be stored in the build
+directory. Writing stray files in the source directory is not
+permitted under any circumstances.
+
+### 4. Must have native support for platforms that are in common use
+
+A lot of free software projects can be used on non-free platforms such
+as Windows or OSX. The system must provide native support for the
+tools of choice on those platforms. In practice this means native
+support for Visual Studio and XCode. Having said IDEs invoke external
+builder binaries does not count as native support.
+
+### 5. Must not add complexity due to obsolete platforms
+
+Work on this build system started during the Christmas holidays of 2012.
+This provides a natural hard cutoff line of 2012/12/24. Any
+platform, tool or library that was not in active use at that time is
+explicitly not supported. These include Unixes such as IRIX, SunOS,
+OSF-1, Ubuntu versions older than 12/10, GCC versions older than 4.7
+and so on. If these old versions happen to work, great. If they don't,
+not a single line of code will be added to the system to work around
+their bugs.
+
+### 6. Must be fast
+
+Running the configuration step on a moderate sized project must not
+take more than five seconds. Running the compile command on a fully up
+to date tree of 1000 source files must not take more than 0.1 seconds.
+
+### 7. Must provide easy to use support for modern sw development features
+
+An example is precompiled headers. Currently no free software build
+system provides native support for them. Other examples could include
+easy integration of Valgrind and unit tests, test coverage reporting
+and so on.
+
+### 8. Must allow override of default values
+
+Sometimes you just have to compile files with only given compiler
+flags and no others, or install files in weird places. The system must
+allow the user to do this if he really wants to.
+
+Overview of the solution
+--
+
+Going over these requirements it becomes quite apparent that the only
+viable approach is roughly the same as taken by CMake: having a domain
+specific language to declare the build system. Out of this declaration
+a configuration is generated for the backend build system. This can be
+a Makefile, Visual Studio or XCode project or anything else.
+
+The difference between the proposed DSL and existing ones is that the
+new one is declarative. It also tries to work on a higher level of
+abstraction than existing systems. As an example, using external
+libraries in current build systems means manually extracting and
+passing around compiler flags and linker flags. In the proposed system
+the user just declares that a given build target uses a given external
+dependency. The build system then takes care of passing all flags and
+settings to their proper locations. This means that the user can focus
+on his own code rather than marshalling command line arguments from
+one place to another.
+
+A DSL is more work than the approach taken by SCons, which is to
+provide the system as a Python library. However it allows us to make
+the syntax more expressive and prevent certain types of bugs by
+e.g. making certain objects truly immutable. The end result is again
+the same: less work for the user.
+
+The backend for Unix requires a bit more thought. The default choice
+would be Make. However it is extremely slow. It is not uncommon on
+large code bases for Make to take several minutes just to determine
+that nothing needs to be done. Instead of Make we use
+[Ninja](https://ninja-build.org/), which is extremely fast. The
+backend code is abstracted away from the core, so other backends can
+be added with relatively little effort.
+
+Sample code
+--
+
+Enough design talk, let's get to the code. Before looking at the
+examples we would like to emphasize that this is not in any way the
+final code. It is proof of concept code that works in the system as it
+currently exists (February 2013), but may change at any time.
+
+Let's start simple. Here is the code to compile a single executable
+binary.
+
+```meson
+project('compile one', 'c')
+executable('program', 'prog.c')
+```
+
+This is about as simple as one can get. First you declare the project
+name and the languages it uses. Then you specify the binary to build
+and its sources. The build system will do all the rest. It will add
+proper suffixes (e.g. '.exe' on Windows), set the default compiler
+flags and so on.
+
+Usually programs have more than one source file. Listing them all in
+the function call can become unwieldy. That is why the system supports
+keyword arguments. They look like this.
+
+```meson
+project('compile several', 'c')
+sourcelist = ['main.c', 'file1.c', 'file2.c', 'file3.c']
+executable('program', sources : sourcelist)
+```
+
+External dependencies are simple to use.
+
+```meson
+project('external lib', 'c')
+libdep = find_dep('extlibrary', required : true)
+sourcelist = ['main.c', 'file1.c', 'file2.c', 'file3.c']
+executable('program', sources : sourcelist, dep : libdep)
+```
+
+In other build systems you have to manually add the compile and link
+flags from external dependencies to targets. In this system you just
+declare that extlibrary is mandatory and that the generated program
+uses that. The build system does all the plumbing for you.
+
+Here's a slightly more complicated definition. It should still be
+understandable.
+
+```meson
+project('build library', 'c')
+foolib = shared_library('foobar', sources : 'foobar.c',\
+install : true)
+exe = executable('testfoobar', 'tester.c', link : foolib)
+add_test('test library', exe)
+```
+
+First we build a shared library named foobar. It is marked
+installable, so running `meson install` installs it to the library
+directory (the system knows which one so the user does not have to
+care). Then we build a test executable which is linked against the
+library. It will not be installed, but instead it is added to the list
+of unit tests, which can be run with the command `meson test`.
+
+Above we mentioned precompiled headers as a feature not supported by
+other build systems. Here's how you would use them.
+
+```meson
+project('pch demo', 'cxx')
+executable('myapp', 'myapp.cpp', pch : 'pch/myapp.hh')
+```
+
+The main reason other build systems can not provide pch support this
+easily is because they don't enforce certain best practices. Due to
+the way include paths work, it is impossible to provide pch support
+that always works with both in-source and out-of-source
+builds. Mandating separate build and source directories makes this and
+many other problems a lot easier.
+
+Get the code
+--
+
+The code for this experiment can be found at [the Meson
+repository](https://sourceforge.net/p/meson/code/). It should be noted
+that it is not a build system. It is only a proposal for one. It does
+not work reliably yet. You probably should not use it as the build
+system of your project.
+
+All that said I hope that this experiment will eventually turn into a
+full blown build system. For that I need your help. Comments and
+especially patches are more than welcome.