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.<div>

<br></div><div>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".</div>

<div><br></div><div>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...)</div>

<div><br></div><div>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.)</div>

<div><br></div><div>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:</div>

<div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><div><font face="'courier new', monospace"># -----------------------------------------------------------------------------</font></div>

</div><div><div><font face="'courier new', monospace"># Definitions for the clang C/C++/Objective-C compiler</font></div></div><div><div><font face="'courier new', monospace"># -----------------------------------------------------------------------------</font></div>

</div><div><div><font face="'courier new', monospace"><br></font></div></div><div><div><font face="'courier new', monospace">from compiler import compiler, linker</font></div></div><div><div><font face="'courier new', monospace"><br>

</font></div></div><div><font face="'courier new', monospace"># Note these objects aren't intended to be invoked directly, it's usually</font></div><div><font face="'courier new', monospace"># a target object which instantiates this and fills in all of the params.</font></div>

<div><div><font face="'courier new', monospace">clang = {</font></div></div><div><font face="'courier new', monospace">  # clang, when used as a compiler</font></div><div><div><font face="'courier new', monospace">  'compiler' = compiler { # Inherit from generic compiler prototype</font></div>

</div><div><div><font face="'courier new', monospace">    # Inputs</font></div></div><div><div><font face="'courier new', monospace">    param flags        : list[string] # Compiler-specific flags</font></div>

</div><div><div><font face="'courier new', monospace">    param include_dirs : list[string]</font></div></div><div><div><span style="font-family:'courier new',monospace">    param sources      : list[string]</span></div>

</div><div><div><font face="'courier new', monospace">    param outputs      : list[string]</font></div></div><div><div><font face="'courier new', monospace">    param source_dir   : string</font></div></div>

<div><div><font face="'courier new', monospace">    param warnings_as_errors : bool   # Generic compiler flags</font></div></div><div><div><font face="'courier new', monospace">    param all_warnings : bool         #   "</font></div>

</div><div><div><font face="'courier new', monospace">  </font></div></div><div><div><font face="'courier new', monospace">    # Outputs</font></div></div><div><div><font face="'courier new', monospace">    compile => [ # '=>' means dynamic evaluation</font></div>

</div><div><div><font face="'courier new', monospace">      message.status("Compiling ${sources[0]}\n")</font></div></div><div><div><font face="'courier new', monospace">      command('clang',</font></div>

</div><div><div><font face="'courier new', monospace">        ['-c'] ++</font></div></div><div><div><font face="'courier new', monospace">        (all_warnings and [ '-Wall' ]) ++</font></div>

</div><div><div><font face="'courier new', monospace">        (warnings_as_errors and [ '-Werror' ]) ++</font></div></div><div><div><font face="'courier new', monospace">        flags ++</font></div>

</div><div><font face="'courier new', monospace">        # Include dirs by default are relative to source root</font></div><div><font face="'courier new', monospace">        # Syntax for closures is 'x => expr(x)'</font></div>

<div><div><font face="'courier new', monospace">        include_dirs.map(x => ['-I', </font><span style="font-family:'courier new',monospace">path.join(source_dir, </span><span style="font-family:'courier new',monospace">x)]).merge() ++</span></div>

</div><div><div><font face="'courier new', monospace">        ['-o', outputs[0]] ++</font></div></div><div><div><font face="'courier new', monospace">        path.join_all(source_dir, sources))</font></div>

</div><div><div><font face="'courier new', monospace">    ]</font></div></div><div><div><font face="'courier new', monospace">  },</font></div></div><div><div><font face="'courier new', monospace">  </font></div>

</div><div><div><font face="'courier new', monospace">  # clang, when used as a linker</font></div><div></div><div><font face="'courier new', monospace">  'linker' = linker {</font><span style="font-family:'courier new',monospace"> </span><span style="font-family:'courier new',monospace"># Inherit from generic linker prototype</span></div>

</div><div><div><font face="'courier new', monospace">    # Inputs</font></div></div><div><div><font face="'courier new', monospace">    param flags        : list[string]</font></div></div><div><div><font face="'courier new', monospace">    param lib_dirs     : list[string]</font></div>

</div><div><div><font face="'courier new', monospace">    param libs         : list[string]</font></div></div><div><div><font face="'courier new', monospace">    param sources      : list[string]</font></div>

</div><div><div><font face="'courier new', monospace">    param outputs      : list[string]</font></div></div><div><div><font face="'courier new', monospace">    param warnings_as_errors : bool</font></div>

</div><div><div><font face="'courier new', monospace">    param all_warnings : bool</font></div></div><div><div><font face="'courier new', monospace">  </font></div></div><div><div><font face="'courier new', monospace">    # Outputs</font></div>

</div><div><div><font face="'courier new', monospace">    build => [</font></div></div><div><div><font face="'courier new', monospace">      message.status("Linking program ${outputs[0]}\n")</font></div>

</div><div><div><font face="'courier new', monospace">      command('clang',</font></div></div><div><div><font face="'courier new', monospace">        (all_warnings and [ '-Wall' ]) ++</font></div>

</div><div><div><font face="'courier new', monospace">        (warnings_as_errors and [ '-Werror' ]) ++</font></div></div><div><div><font face="'courier new', monospace">        flags ++</font></div>

</div><div><div><font face="'courier new', monospace">        libs.map(x => '-l' ++ x) ++</font></div></div><div><font face="'courier new', monospace">        # Lib dirs are relative to build dir by default</font></div>

<div><div><font face="'courier new', monospace">        lib_dirs.map(x => ['-L', x]).merge() ++</font></div></div><div><div><font face="'courier new', monospace">        ['-o', outputs[0]] ++</font></div>

</div><div><div><font face="'courier new', monospace">        sources)</font></div></div><div><div><font face="'courier new', monospace">    ]</font></div></div><div><div><font face="'courier new', monospace">  }</font></div>

</div><div><div><font face="'courier new', monospace"><br></font></div><div><font face="'courier new', monospace">  # TODO: clang, when used as gendeps</font></div><div></div><div><font face="'courier new', monospace">}</font></div>

</div></blockquote><div><div><br></div><div>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:</div>

<div><br></div><div>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...</div>

<div><br></div><div>In any case, what I mainly want at this point is criticism of the design. There are docs here: <a href="https://github.com/viridia/Mint/wiki/Mint-Introduction">https://github.com/viridia/Mint/wiki/Mint-Introduction</a> , and the source is browsable here: <a href="https://github.com/viridia/Mint">https://github.com/viridia/Mint</a>. I'm interested in any and all feedback...</div>

<div><br></div>-- <br>-- Talin<br>
</div>