Hacker News new | ask | show | jobs
by megous 1924 days ago
you might like ninja build then
4 comments

I really like the idea of Ninja; I really want Ninja to be a single C(++) file that I can build with a trivial command line. I don't want a single C(++) file because of "elegance" so much that I want to ship Ninja as an inline dependency but now I've got two problems: I have to build my code and I have to build Ninja. If Ninja was a single file: boom! Trivial build line.

Other than that, I think Ninja is perfect.

You could probably post-process samurai (a rewrite of ninja into C) into a single-file: https://github.com/michaelforney/samurai
Ninja precompiled is a self contained 200kb single file binary.

Small enough to even checkin into git. Your compiler, cmake and all the other tools you use is going to be a much bigger problem.

ninja is normal part of Linux distros. So at least there you don't have to worry.
Ninja has as goal to be simple and fast, but not necessarily convenient for humans to write. It's in the second paragraph of their home page.

While ninja is great for many uses i would not recommend to use it for hand written rules. In fact any simple dependency-resolver like make or ninja will be lacking a lot of language context about includes and other transitive dependencies so you always want some higher level abstraction closer to the language as your porcelain.

ninja specifically handles includes automatically.

Anyway, not having any other automagical behavior and hidden rules and the fact that it can do incremental and correct job re-runs even when the rules change is exactly why I like to use ninja.

Here's one such use case, maybe not the cleanest:

https://megous.com/git/p-boot/tree/configure.php

I especially love it in projects involving many different compilers/architectures/sdks at once (like when doing low level embedded programming), where things like meson or autotools or arcane Makefile hacks become harder to stomach.

Thats quite a stretch of what handles means. While it supports integrating such use cases but you still have to do the gcc -M dance with https://ninja-build.org/manual.html#ref_headers.

As opposed to say cmake, bazel or other higher level abstractions where you just ask it to take all c-files in this directory and solve the rest.

Take meson. It's at the same level of cmake, but what handles the C include dependencies for it is ninja. cmake also has ninja backend. Not sure how it works exactly, because I don't use cmake, but I assume it will be ninja handling include deps too in that case.

Yes, ninja basically handles it for you, compared to what you have to go through when using Makefiles, to have autogenerated dependencies.

I will check it out, thank you for the suggestion - what I glanced at seems very interesting.
Shake would also be interesting to look at. It has much more sophisticated dependency graph handling.

Gnu Make is almost pathetic.

See eg http://simonmar.github.io/bib/papers/shake.pdf

That was a great read - thanks for linking.

I had trouble with the one class in uni that used Haskell and I've been working in a Python shop for some years now, so I kept expecting to encounter some impenetrable section that would make my eyes glaze over and my hand close the tab. I was probably closest around page 9!

But the writing was excellent, and clear, and satisfying, and little concepts I was so close grokking kept catching my eye and pulling me back in until I understood them, then their neighbors, then the section, then I was done.

Guess I've picked up some things since college. Wish I could go back and take that class again. I think learning to use Rust's Option type in anger on a personal project helped me understand monads more than anything in that class.

Also, I'm happy to see the method described by the paper does seem to have become the official GHC build system [0].

0. https://gitlab.haskell.org/ghc/ghc/-/wikis/building/hadrian

This is their minimal example to compile a single C file:

1 module Main(main) where

2 import Development.Shake

3 import System.FilePath

4

5 main :: IO ()

6 main = shake shakeOptions $ do

7 want ["foo.o"]

8

9 "∗.o" %> \out → do

10 let src = out -<.> "c"

11 need [src]

12 cmd "gcc -c" src "-o" out

Maybe there is a need for super sophisticated graph handling, but for most use cases the way more complicated syntax of Shake is not a worthy tradeof.

https://shakebuild.com/ agrees with you:

> Large build systems written using Shake tend to be significantly simpler, while also running faster. If your project can use a canned build system (e.g. Visual Studio, cabal) do that; if your project is very simple use a Makefile; otherwise use Shake.

For what it's worth, if I remember right, Shake has some support for interpreting Makefiles, too.

> [...] the way more complicated syntax of Shake [...]

For context, Shake uses Haskell syntax, because your 'Shakefile' is just a normal Haskell program that happens to use Shake as a library and then compiles to a bespoke build system.

Also:

> The original motivation behind the creation of Shake was to allow rules to discover additional dependencies after running previous rules, allowing the build system to generate files and then examine them to determine their dependencies – something that cannot be expressed directly in most build systems. However, now Shake is a suitable build tool even if you do not require that feature.