1. 36
    1. 12

      […] it’s more than complete enough to build complex real-world projects

      I went looking for some complex, real-world projects. In my experience, Boost and Qt are good reality checks: Boost is “wide”, meaning it has 150+ highly inter-dependent libraries while Qt is “deep”, meaning it builds a few large libraries with fairly complex builds: large number of external dependencies, autoconf-style configuration probing, a lot of generated source code (moc), plugins, etc, etc.

      There is no Qt in the repository, but there is Boost and here is its build.zip. Some friendly critique from someone trying to achieve similar goals but using a different approach (separate build system/package manager with a purpose-built language):

      • It looks like it’s a single package with all 150+ libraries. Ideally you want to split Boost into one package per library.

      • The build.zig file is quite hairy with a lot of accidental complexity due to using a general-purpose and fairly low-level language. I think this will only get worse if it is split across 150+ packages because now you will also need to capture package/library dependencies, etc. A purpose-built language would have hidden a lot of this resulting in a more succinct build description. Here is a representative example:

        obj.addAssemblyFile(.{ .cwd_relative = b.pathJoin(&.{ ctxPath, "asm/make_i386_ms_pe_clang_gas.S" }) });
        
      • Some boost libraries have external dependencies (ICU, OpenSSL). These don’t seem to be handled.

      • I see a lot of hardcoded compiler flags like -Wextra, -D_CRT_SECURE_NO_WARNINGS. These have no place in a buildfile of a reusable package. For background, see: https://github.com/build2/HOWTO/blob/master/entries/compile-options-in-buildfile.md (EDIT: actually, -D_CRT_SECURE_NO_WARNINGS is ok provided it’s not exported.)

      1. 1

        a thing to know is that addCSourceFile(s) will take flags per file. these are never exposed to anything except the file(s) added with the function. So -Wextra is only ever passed to the files which are compiled with these flags.

        It looks like it’s a single package with all 150+ libraries. Ideally you want to split Boost into one package per library.

        Why that? The design philosophy here is that you configure the package in such a way that you receive a libboost which implements everything you need. You can enable and disable all components of boost depending on your needs.

        If you have a project which requires differenr subsets, you just instantiate the boost dependency multiple times and you’re good to go. so less work for the maintainer and less work for the user.

        Important note: Zig buildsystem packages can ship none to (basically) infinity number of build artifacts, depending on what’s reasonable to do.

        Another possible design would’ve been to expose each boost library as it’s own library, but that’s just a philosophical design decision

        1. 4

          If you have a project which requires differenr subsets, you just instantiate the boost dependency multiple times and you’re good to go. so less work for the maintainer and less work for the user.

          in the general case that does not work as enabling / disabling features in different “instantiations” may change ABI or create ODR issues.. my experience from building fairly large C++ project is that the only way to have something that works consistently is really enforce the same build flags for the entire source tree, otherwise you don’t know if some library deep inside your dependency tree won’t have some header file with:

           class some_lib {
             private:
             #if __has_include(<boost/regex.hpp>)
                boost::regex m_rx;
            #else
                std::regex m_rx;
            #endif
           };
          

          so .e.g if you build boost.asio you have to make sure that in your entire project all its dependencies (boost.system, boost.datetime etc) are also built with the exact same options they will be when building boost.asio (and the depenency link can be both ways, e.g. some libraries can have logic such as “even though I’m a dependency of libfoo, but if I know I’m being built as part of libfoo through #ifdef FOO_LIB i’ll enable some specific optimization options”)

      2. 3

        It’s always good to look for alternatives to CMake. That being said, a lot of C projects have really minimal build scripts. Lua for instance uses pure Make which is a 30+ year old technology and the build script is relatively short versus the zig version:

        Lua build.zig

        Lua Makefile

        As a maintainer I’d be a little reluctant to just have a build system alternative checked into my repo unless that build system was also part of the CI.

        1. 10

          But - do you get

          • Windows support
          • trivial cross compilation
          • caching
          • file system watching
          • ability to have dependencies

          Looks like the last one doesn’t matter for lua, since it truly has no dependencies - not even a copy-pasted miniz implementation.

          1. 1

            It seems the library and Makefile is compatible with MinGW so, to at least the first point, basically yes?

          2. 5

            For comparison, here is the build2 version. Note that we split Lua into the liblua (library) and lua (interpreter) packages, so there are two buildfiles:

            liblua buildfile

            lua buildfile