I've been using CMake on my LLVM project for several years now (ever since I abandoned using SCons, and before that, autoconf). During that time I've grown to both love and hate CMake - that is, I love the idea, but hate the language and its limitations. However, due to the recent thread on this list, I realized that I wasn't the only person that felt that way - that there were a lot of folks who hated the CMake language just as much as I did. And after thinking about this for a while, I came to the conclusion that "I bet I could do better". Of course, this is not the first time I've considered writing my own build system - I have notes going back quite a few years on this subject, which I first started thinking about while working at Electronic Arts - one of the tools I wrote there was a Python script for generating Visual Studio project files. (And yes, I've looked at almost every other build system out there - I've even used NAnt...and Jam before it was part of Boost...) In any case, I decided to take a vacation from working on Tart, and instead work on this new thing I call "Mint" (think of minting a coin - it's a synonym of "make"). It's been about six weeks now, and I now have something that is able to perform configuration tests, generate its own config.h file, rebuild itself (along with google test and RE2) - and does so concurrently. (It's currently hard-coded at 4 subprocesses, but I plan to make that a parameter.) I'm still working on automatic dependency generation, as well as generation of makefiles and IDE project files. (At the moment I'm in the middle of adding support for exporting the build configuration as XML, so that other tools can easily be written to operate on a build tree.) One big advantage over CMake is that all of the "knowledge" of how to invoke the C++ compiler isn't hard-coded, but is expressed in terms of the Mint build language, which is a hybrid declarative/functional language for describing build dependencies and actions. Compilers like clang and gcc are simply objects in the Mint standard prelude - here's what the 'clang' object currently looks like: # ----------------------------------------------------------------------------- # Definitions for the clang C/C++/Objective-C compiler # ----------------------------------------------------------------------------- from compiler import compiler, linker # Note these objects aren't intended to be invoked directly, it's usually # a target object which instantiates this and fills in all of the params. clang = { # clang, when used as a compiler 'compiler' = compiler { # Inherit from generic compiler prototype # Inputs param flags : list[string] # Compiler-specific flags param include_dirs : list[string] param sources : list[string] param outputs : list[string] param source_dir : string param warnings_as_errors : bool # Generic compiler flags param all_warnings : bool # " # Outputs compile => [ # '=>' means dynamic evaluation message.status("Compiling ${sources[0]}\n") command('clang', ['-c'] ++ (all_warnings and [ '-Wall' ]) ++ (warnings_as_errors and [ '-Werror' ]) ++ flags ++ # Include dirs by default are relative to source root # Syntax for closures is 'x => expr(x)' include_dirs.map(x => ['-I', path.join(source_dir, x)]).merge() ++ ['-o', outputs[0]] ++ path.join_all(source_dir, sources)) ] }, # clang, when used as a linker 'linker' = linker { # Inherit from generic linker prototype # Inputs param flags : list[string] param lib_dirs : list[string] param libs : list[string] param sources : list[string] param outputs : list[string] param warnings_as_errors : bool param all_warnings : bool # Outputs build => [ message.status("Linking program ${outputs[0]}\n") command('clang', (all_warnings and [ '-Wall' ]) ++ (warnings_as_errors and [ '-Werror' ]) ++ flags ++ libs.map(x => '-l' ++ x) ++ # Lib dirs are relative to build dir by default lib_dirs.map(x => ['-L', x]).merge() ++ ['-o', outputs[0]] ++ sources) ] } # TODO: clang, when used as gendeps } This lack of hard-codedness means that it's relatively straightforward to support new compilers and new languages. Here's another short example, this is from the configuration test that can be used to detect whether a given struct has a particular member: Now I don't want to generate too much traffic on this list, because I realize that this isn't really LLVM-related, other than the fact that the initial impetus came from here. If it turns out that people are interested in this, I'll have to find some sort of appropriate forum for this... In any case, what I mainly want at this point is criticism of the design. There are docs here: https://github.com/viridia/Mint/wiki/Mint-Introduction, and the source is browsable here: https://github.com/viridia/Mint. I'm interested in any and all feedback... -- -- Talin -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20111220/a262548f/attachment.html>