5 ms·
CMake is awful, and the fact that the C++ community seems to be settling around it will be a massive disadvantage in the long run. Why do you think so many WASM
by dfgdghdf 8y ago
CMake is awful, and the fact that the C++ community seems to be settling around it will be a massive disadvantage in the long run. Why do you think so many WASM examples use Rust? It's because Cargo is a sane build system that makes cross-compilation easy.
Issues with CMake:
- The DSL is not very good. It needs proper functions, for one.
- Non-hermetic builds (also mentioned in this thread)
- No ability to easily query the build DAG
- Headers are not modelled properly (they should be a dictionary of paths -> paths, not a list of include directories)
- Build folders cannot reliably be used between configurations, leading to confusion and cache misses
- Everything is convention driven. It does not model the build graph properly.
- Globs do not work properly (maybe that has changed recently?)
- The cache is not portable across a network, or even between folders on the same machine
The C++ community deserves better!
Others in this thread have mentioned some modern alternatives:
- Buck Build (Facebook, Uber, AirBnB)
- Bazel (Google)
- Pants (Twitter)
- Please (Thought Machine)
It doesn't actually matter which of these succeeds. They all model the builds in such a way that you can easily transpile between them.
- shmerl 8y agoMany C/C++ projects are switching to Meson these days. Mesa for example.
- dfgdghdf 8y agoMy experience with Meson is that it is much better designed than CMake but very slow.
- shmerl 8y agoMeson itself though is just a meta build system, it's used in combination with Ninja that does the actual build. Ninja is quite fast. But they claim at least that they are working on making Meson itself fast too.
- dfgdghdf 8y agoThis idea of having a "front-end" build system and a "back-end" build system is a bit of a throw-back. It is better to integrate the two for better optimization and configuration management.
- archgoon 8y agoI don't know if that's true. Don't you gain all the usual advantages of decoupling systems (clarifying responsibilities, documentation, independent growth and optimization, interoperability with other programs, easier testing) if you are forced to define a clear interface between the two? It certainly seems like specifying a build process and performing a build process are two distinct things.
- dfgdghdf 8y agoThey are different stages of the build, yes (although in a hermetic system you could in theory run the build process in parallel with the configure step for different targets). The challenge is integration. Things are much simpler when you ship the two programs together since they need to be compatible in terms of folder structures etc. It is also nice to keep things in memory sometimes. In other words, I think they should be different modules of one build system, rather than completely different programs. To your point, integration will allow the two to be iterated upon faster, since changes can be more easily released together.
- shmerl 8y agoBetter integrate may be, but not merge into one. Since super optimized build systems also increase complexity of their usage. So the idea is to have user friendly front end, which produces complex input for very fast backend. Ninja view their build idea as "assembly like". While assembly surely allows you producing very fast results, it's not easy to use at all, if you are using it directly.
- entelechy 8y ago> Since super optimized build systems also increase complexity of their usage. That's not correct. Complexity arises from the inability of expressing what you want and lack of abstractions over platform details. Ninja for instance is to primitive to be productive and does not have any understanding of C++. Buildsystems like buck allow you to express things in a declarative manner eg.: cxx_library( name = 'foo', srcs = glob(['src/**/*.c']), exported_headers = glob(['*.h']) ) cxx_binary( name = 'app', srcs = ['main.c'], deps = [':foo', ':bar'] ) buck implements sophisticated (disk & network) caching, optimization and scheduling strategies to make things fast. Furthermore it will use services like watchman (if available) to precompute what things need building if you change some files for fast incremental builds. Lastly it will strip and sort symbols in your binary to make sure the hash of your binary is always the same for the same set of inputs. All this complexity is handled for you and all you need to do to run your executable is buck run :app
- stefan_ 8y agoI've used Meson to build mesa, as complex a project as you are going to find/make, and as entirely expected, from invocation to Ninja build files it's.. maybe a second? Safe to say, if you are building any C++ specifically, that is entirely and utterly negligible.
- shmerl 8y agoMesa surely builds a lot faster with Meson+ninja than with autotools.
- j1elo 8y agoBuck, Bazel, Pants, Please, Meson, Gn, SCons. I haven't even finished reading comments in this HN entry and I'm already lost
- slavik81 8y agoMy only experience using Bazel involved discovering that build instructions from August have already been broken by deprecations. It was not a good first impression.
- Teknoman117 8y agoCargo (and Rust tooling in general) still have an annoying design flaw in my mind -> they all pretend that system package managers don’t exist. Admittedly with rust not having an ABI you’d still have to rebuild all the packages whenever the compiler was updated, but I’d like to see rustup support toolchains installed to the system.
- Rusky 8y agoThis is a rustup issue, not a Cargo issue. You can use a system-installed Cargo/rustc just fine.
- steveklabnik 8y agorustup link can point to a system installed rust, so should be fine!
- deleted 8y ago[deleted]
- tom_ 8y agoThere's just literally nothing good that can come of calling your product "pants". I don't care if it's amazing! - because I already know that, actually, it isn't, simply on the basis that its authors' judgement is clearly unsound.
- quietbritishjim 8y ago- Headers are not modelled properly (they should be a dictionary of paths -> paths, not a list of include directories) What do you mean by this exactly? Perhaps you are thinking of the old include_directories() function rather than the now-recommended target_include_directories()? This attaches one or more include directories to each target rather than having a single global list of all include directories. You can indeed have a target for each directory of your source files (this is also recommended practice). This even works for imported targets. These are targets that represent existing prebuilt libraries on your system, and they are increasing returned by find_package in place of the old pair of ${FOO_LIBRARIES} and ${FOO_INCLUDE_DIRS}. Internally they call target_include_directories() with PUBLIC (or INTERFACE) modifier that means that those directories will be inherited by other targets that link against this one.
- dfgdghdf 8y agoThe idea (only implemented in Buck AFAIK) is to model not a list of include directories, but a mapping from the path specified in your "#include" to the actual file on disk. So your code might look like this: #include <foo/bar.hpp> The project folder structure might look like this: . └── src └── include └── foo └── bar.hpp And the mapping might look like this: { "foo/bar.hpp": "./src/include/foo/bar.hpp" } This enables the build system to know exactly which headers are meant to be exposed by a library to its dependees. The build system can then tell you: (1) Exactly where a header comes from (2) Exactly which headers a library exports (3) If two libraries will collide in terms of headers (4) If someone is trying to use a header that is not explicitly exported by a library. (5) If some is accessing a header in the wrong way (e.g. abusing the layout of source-files and not using the correct include-path) This approach scales very well. Constructing the mapping can be done via globs (globs work properly in Buck), so most projects just do: exported_headers = subdir_glob([ ('include', '**/*.hpp'), ]) Additionally, the RHS of the mapping might be another build rule, thus supporting generated headers in hermetic builds.
- quietbritishjim 8y agoThe ability to specify at a finer level of granularity than directories sounds wildly overcomplicated, to be honest. I'm not aware of any other language that works at that level either (maybe Python's import mechanism does let you tinker at that level, but it would be extremely niche). Also, I'm also a bit confused about how it can work even in C/C++; surely most compilers don't even have the capability of specifying header file locations on an individual level, as opposed to the classic -I switch (or /I for Visual Studio). I've used generated header files (protobuf) with CMake and they work fine. Although I prefer to create them at configure time rather than build time, even though that's philosophically incorrect, just because that way I can see them in the generated IDE projects and they don't break autocomplete.
- Svenstaro 8y agoI wouldn't recommend bazel to anybody. I've had an absolutely terrible experience with it. First of all, it's slow. Secondly, it keeps breaking builds with every single release. Thirdly, the language is weird and not at all obvious and it makes monkey patching hard (this is important for distro packagers).