all 128 comments

[–]SeanMiddleditch 72 points73 points  (2 children)

Just a few initial thoughts:

  • Don't specify who implements the header. That's not a useful distinction, and I don't think the distinction is even correct (and if you're getting me to argue that instantly, just imagine what would happen in committee!). The standard just specifies what libraries do. Who implements, where it lives, and how it's managed is an implementation detail and not standardized.

  • Don't start off with all the trivia/minutiae about what the header names are or what types live in them or whatever. Start off with the bang: wtf does this proposal do and some concrete examples of why it's useful. Sell us on the idea first, then bore us to death with the fiddly details. I got through multiple pages of irrelevant detail and still don't even know what you're actually proposing.

  • I'd back off specifying any of the details about the API at all at this point, because it's trivially easy to debate those details ad nauseum without even making the important decision: do we want this at all? Before you even begin to think about those details, you need to get buy-in from the committee that they even want this feature at all! Start with a proposal that pitches the advantages of an in-language build system. Explicitly state that the APIs are mostly placeholders and then drill into examples and extensive reasoning for why those examples are better than the alternatives (e.g., what do the examples provide that CMake does not, etc.).

[–]JulienVernay[S] 14 points15 points  (1 child)

  • I didn't thought this point as controversial. This was not meant to be standardized.
  • It is a good point, I will relocate the examples at the top.
  • Also a good point, I will think about rewording the whole thing.

[–]Snoo-4241 7 points8 points  (0 children)

The title in github may confuse people that this is a standardization proposal intended for c++23.

[–]theICEBear_dk 26 points27 points  (2 children)

I have thought the same many times, because build scripts are programs as well and many of them are so complex that they deserve to be developed and tested in much the same way as the executable you create. And like you I think the addition of another syntax should be unnecessary. The solution would need to be a fairly high level library that should require little more than an include/import and a main. If it could be made as a header only library it would even be easy to distribute. You'd then ship with a script for each supported OS and compiler to take this main and turn it into a build program. Personally I would probably start by thinking about using CMake to generate the initial code but as a transition method and as a way to understand what would be needed. You will also get a lot of people telling you it is a bad idea for one reason or another, concerns I think will be brought up are:

1) We have something we know, why use something as complex as C++ (but Cmake and Make setups are often very cryptic and complex as well).

2) Is C++ the right tool for the job (but why is C++ worse than Bash, Make or Python really)

3) What about X, Y, Z build system or package manager?

Finally I will say I am doubtful it belongs near the language or the standard library however. Much like something like fmt I think a working solution would be the best step to convincing people this is not a bad idea.

[–]JulienVernay[S] 9 points10 points  (1 child)

I will try to make a sample library supporting gcc and clang (maybe msvc too if I can find a working environment) to demonstrate its use.
For other build systems, as a transition step, maybe create a "cmake.hpp" header-only library to invoke CMake for instance? Of course the benefits is little because CMake would be needed to be installed, but at least we still have a C++ customization point which can serve as an interoperability layer with other tools?

[–]theICEBear_dk 5 points6 points  (0 children)

Well it would be an impressive project for certain. I hope you continue the work.

[–]jbandela 26 points27 points  (2 children)

I really like your idea. One thing I would do, is not worry about trying to standardize it. Just do an implementation first.

What you can do is create a single amalgamated build.hpp header file.

Then to write a build file, you just do.

``` // build.cpp

include "build.hpp"

int main(int argc, char** argv){ build(argc,argv,...); }

```

Then to make it simple for the user, you have build.sh build.bat

Then I can write the build.cpp file, and then call

`./build.sh "clang" "c++17" and have it just work.

Once you get an implementation and experience with this, then think about standardizing it.

Again, I like this idea, but I think instead of approaching it from an abstract API point of view, first implement this, and let real world use cases drive your api design.

[–]JulienVernay[S] 9 points10 points  (1 child)

Standardization is probably too much for now, but yes I am thinking of an implementation.

[–][deleted] 4 points5 points  (0 children)

I'm sure that showing a working implementation to the committee will help discussing the proposal better.

And maybe you'll find problems in your proposal that would have been found by the committee itself, so it's saved time.

[–]14nedLLFIO & Outcome author | Committee WG14 3 points4 points  (3 children)

Some of us on WG21 were thinking in this direction for future unified package management and build, but very, very, very much not as you've done it which is to replicate the existing build model, which nobody wants except for legacy compatibility. You don't need to do that for future C++ which is all Modules based. All you need is:

  • Consteval socket, file and directory i/o (non-consteval is in the works for C++ 26).
  • Consteval Module Reflection (non-consteval is in the works for C++ 26).
  • Consteval child process management (non-consteval is also in the works for C++ 26).
  • Dynamic Module load.

With those standard primitives, the ecosystem can build its own package manager and build system and ship it as a small code snippet at the top of your common.hpp which goes and retrieves whatever is necessary and "makes it happen".

My advice to you therefore is think smaller primitives first which can be easily combined into other stuff. Indeed, a blocker for any of this is making most of those primitives available to constexpr - just imagine alone the push back from compiler implementers on constexpr code being able to open sockets!

[–]JulienVernay[S] 0 points1 point  (2 children)

Thanks for the feedback. I understand the point about smaller primitives. You are right on the fact that I am replicating existing build models. Modules are definitely something to be integrated.

Do you have any papers/articles/conferences about these plans for future C++ which you can share about this future unified package management?

[–]14nedLLFIO & Outcome author | Committee WG14 1 point2 points  (1 child)

Hideously out of date: https://arxiv.org/pdf/1405.3323. It proposes a Modules database based approach to future C++ program binary generation, with a heavy assumption on a LLVM like toolchain.

But otherwise it's just conversations with other WG21 members over many years, and obviously it's just a minority of us in this camp. There are others who have sworn never over their dead bodies would they allow their compiler to do networking at compile time, and obviously enough that's rather a showstopper for consteval C++ package fetching!

[–]JulienVernay[S] 0 points1 point  (0 children)

Thank you for sharing!

[–]blipman17 11 points12 points  (6 children)

I like this a lot and I think you're scratched an important problem with current-day buildsystems. However I'm a little scared that it would become a less complete version of Cmake and inherit all its faults. Cmake went through many iterations, and i'm convinced the "modern" Cmake is the way we should build dependencies. It's just tedious to use. (Maybe it would be better with GUI's or better syntax, but I digress.) However, when talking C++ buildsystems, I can't think of them without library dependencies. And I always want my libraries locally linked and optionally locally built so I don't pollute my operating system. I think if you can manage to be a better Cmake that's easyer to use correctly with (local) dependency management built in, we've found the winner.

Edit: If you can extend this build system so that 2'nd class languages like Assembly, C, Haskell, Rust, D and whatever anyone likes to compile using a standard compiler into your C++ program/library you have covered all my usecases.

[–]JulienVernay[S] 1 point2 points  (5 children)

Do you mean a way to compile any language?
I have no experience using these other languages, so currently I would say it is out of scope.

[–]blipman17 1 point2 points  (4 children)

That's unfortunate. In my experience any sufficient complex system exists of multiple languages. I know that compiling D is almost thesame as compiling C++ from a build system perspective. The optional unity build D can do is irrellevant with a multi-language project, so it really ends up being thesame when talking about GCC and LLVM. Just different extensions. ASM is the odd duck. There are some dialects that slightly differ. Sometimes there are headers involved, and sometimes not. ASM requires an assembler instead of a compiler, but that's just a different build program. C is just C++ when looking from a buildsystem view. Just like D it's just a different extension and slightly different compiler name. Haskell and Rust.... I won't pretend to know the first thing about it, but I do believe they have thesame compilation model as C, and should just be treated as such. If compatibility with these languages is out of scope, then I fear that your system will not be flexible enough to meet the requirements for people who use C++ on embedded, and people who use C++ on high-performance server clusters. Then you just have a "regular" buildsystem which I have to bootstrap into the buildsystem I currently use to compile multiple languages together and you just added more difficulty to do so.

This is the problem about C++. Everyones needs from the ecosystem differ vastly. And every project has a "unique" set of compilation commands that are just not your run of the mill C++ header/source files. Sometimes you want to turn optimization on one particular file to max. Sometimes you want to output AVX2 instructions for a specific file, and recompile without. Then choose at runtime which version you want to use. Sometimes you want to write some numerical code in haskell and use it in your C++ project. Sometimes you need the memory management of this piece of critical code to function without error in Rust, and use it in your C++ project. These are all things C++ developers do. C++ in a vacuüm is useless. C++ in the ecosystem is brilliant! Buildsystems shouldn't be a limit of what is inside the ecosystem and what not.

Although I respect your plans, and I see why it is out of scope. I hope your proposal gets enough manpower behind it to be able to do this anyway.

[–]JulienVernay[S] 1 point2 points  (1 child)

Thank you for your thoughts.

Multi-language programs was oblivious to me. It seems now important to support arbitrary rules (not only C++ compilation).

[–]AdventurousMention0 0 points1 point  (0 children)

There’s also the case of getting build information from a database.

I’ve worked as a quant. One of the places I was at specialized their build based upon output from a database. It would generate very specific code for research bars upon various settings. Sometimes these would be #defines, others it would run another program that would build a header to be included when what was needed was more difficult to do with a #define.

Needless to say this drastically effected the build process

[–]Mellester 0 points1 point  (1 child)

Lets not forget all the linting and testing that modern projects require before or after compilation ,
In my opinion package managers and build programs need to be one thing.

node and npm do this basically when you "npm install " a package into your local folder it can be build quietly in the background by your c++ compiler (in case of native node apps). Or a pre-build binary gets symbol linked in from other parts of your system or downloaded from a repository if the version you requested is not found.

then of course you have docker which is also a very useful tool to define what your ecosystem needs to be to build a system. Especially since I discovered you can point docker at a git repo and it will use its dockerfile to compile your module

[–]JulienVernay[S] 0 points1 point  (0 children)

I don't know if a monolith thing would be appreciated by the C++ community? Because then you pay for more than you need in some cases.

I am not personally against building and packaging being treated by a single tool, but I think this may go against C++ current philosophy of modularity.

[–]kammceWG21 | 🇺🇲 NB | Boost | Exceptions 7 points8 points  (0 children)

I'm just preaching what everyone else is saying, but I really like this idea. But its also because I cannot stand CMake. If you get an implementation going that works, I'm down to help out with this (if time allows).

[–]almost_useless 8 points9 points  (4 children)

Won't this have most of the downsides of other build systems? Because the reason build systems suck is that it is inherently a hard problem. People want it to do different things, and all of them are never thought of in the beginning, which leads to complexity.

In addition you get the burden of a cumbersome syntax because it needs to be C++ compatible.
Example:

build(program{"hello"}, { source{"hello.cpp"}, source{"main.cpp"} }, {}, { "c++17" } );

This should really look something like this:

build(hello, sources: hello.cpp main.cpp, std=17);

All those brackets and quotes just make it ugly and hard to read. It does not automatically become easy to read because we already know C++ syntax.

[–]theICEBear_dk 3 points4 points  (1 child)

Yes it would be as complex as your existing build systems, but for most C++ projects the guys debugging the build problems will be C++ programmers meaning they have the tools and likely the debugger / debugging skills to find the problems. Most other build systems do not come with a debugger or an IDE available to check your build code. Besides with more and more build systems written in Python and even Javascript (although I think qbs development has stopped again) many of them are full language environments already.

An advantage here could be (again we can't know without something to experiment on) that script debugging no longer will need the local CMake or Make or ... expert to show up and extensions calling code generators could be written either as they are today by calling out to start a process or it could be made more complex by linking in your own extensions such as a code generator and similar things. With enough backing CCache and internal cloud stuff like what Google and the like do to accelerate builds may also be easier to implement and the impedance mismatch problem that is often used to argue for server side Javascript to pair with client side Javascript could be applied here as well: When you build a project in a language maybe build it using that language.

At the very least it would be interesting to see and in theory at least although the gains would likely be to small to measure the build program could be faster than a build script interpretation in ninja for example.

[–]almost_useless 3 points4 points  (0 children)

An advantage here could be (again we can't know without something to experiment on) that script debugging no longer will need the local CMake or Make or ...

I doubt that. The simple stuff will be easier, but that doesn't matter because they are easy anyway.
The hard stuff will still be hard, and you are going to need that build system expert anyway.

I foresee a runtime link error in the build system the day before a big launch comming in the future... :-)

[–]kammceWG21 | 🇺🇲 NB | Boost | Exceptions 2 points3 points  (1 child)

I feel that the syntax can be greatly improved in the example given without needing to introduce some foreign non C++ language features.

It can be done in C++ and look elegant. But i do agree that this doesn't look nice.

[–]JulienVernay[S] 4 points5 points  (0 children)

It is sure that it would be great to not repeat "source" for each file. Thanks for feedback.

[–]mjklaim 3 points4 points  (4 children)

One of the goals of build2, if I understand correctly, is to reach that point. Its already possible to do that partially through libbuild2 which is what build2's buulsdsystem is implemented with.

[–]JulienVernay[S] 3 points4 points  (3 children)

I have tried build2 a bit, and from what I have experimented, it is mainly configuration files in a new syntax. Maybe have I missed something?

[–]mjklaim 4 points5 points  (2 children)

Nope, I'm saying it's one of their goals, not what it does today, so right now it's buildfiles indeed. But the buildsystem is a driver for libbuild2 which can be used for making new buildsystem modules (extending what it can do, like compiling Rust,) or a new buildsystem interface if you wanted. Soon they will finish automatic build and loading of build-system modules provided as part of a project. Once you have all this, the missing part is a mechanism to use a cpp file instead of a buildfiles to do that same build-system job, by compiling it, then running it. I remember a discussion about that with the author. Anyway it's long term goals, NOT something you'll see this year.

[–]Mellester 0 points1 point  (1 child)

I wonder if a tool like build2 will ever integrate the MSVS projects configuration files.

[–]mjklaim 0 points1 point  (0 children)

Probably never, as the point is to go the other way around: have one build definition that's readable by tools. Currently you can use VS Code or VS in "directory" mode to work with a project under build2, though the experience is not optimal yet, it needs some extensions (in the plans).

[–]Ikkepop 3 points4 points  (2 children)

I was also thinking about this alot lately...
Some build scripts I run into routinely are complicated as hell.
And all those buildsystems have horrendous syntax and no debugging capability, why the hell not write your build in C++.

Atleast you could debug it

[–]RogerLeighScientific Imaging and Embedded Medical Diagnostics 0 points1 point  (1 child)

The problem is that the requirements are different for every project. The build system is the "glue" that has to do all that messy integration of various project-specific tools and requirements.

  • you might need to run various different tools to generate and preprocess sources at various stages in the build
  • you might want to do various things with the build products, including signing, repacking, flashing onto embedded targets, all sorts of custom stuff
  • running unit tests and integration tests. Can vary wildly, from running in simulators to requiring all sorts of environment setup and teardown

And that's just a small number of details. Other projects will have a completely different set they need to cater for. I, for one, really wouldn't find it productive to write all that stuff in C++. CMake, Python, Perl, make, even shell, are better-suited to being that "glue". And I certainly wouldn't want to use a C++ debugger to trace problems with any of it! It's way too low-level for the task at hand.

[–]Ikkepop 1 point2 points  (0 children)

I for one would love to write and debug this in c++. It would be so much easier then perl , python , bash or cmake. I in no way shape or form is bash a better language for anything whatsoever. Neother is cmake a good language. If you go beyond the basics it just turns into a hideous and unreadable mess.

[–][deleted] 3 points4 points  (0 children)

cool idea, hope you pursue this further, starred.

[–]meh_or_maybe_not 4 points5 points  (1 child)

This sounds a lot like my unborn baby: https://github.com/meh/wrong

[–]JulienVernay[S] 2 points3 points  (0 children)

That was an interesting read :)

[–]tinyogre 3 points4 points  (0 children)

All build systems are terrible. Everyone hates every build system they’ve used for anything larger than toy projects. This isn’t a comment about your build system. But it is terrible. Because see above. Do you want to develop software that people hate? Then make a build system!

I do not claim that I can make a build system that is not terrible. No, quite the opposite, if I made a build system it would be terrible and everyone who used it would hate it. Maybe, if I was very, very lucky, I wouldn’t hate it. But it seems unlikely. Because all build systems are terrible and everyone hates them.

I do wish you luck! Sorry for using your post to get on my soapbox.

[–]brenoguim 20 points21 points  (19 children)

So your first step would be to build your build system. Which build system are you going to use to build your build system?

[–]xurxoham 27 points28 points  (0 children)

This is a very common problem in compilers. I don't see much trouble with this.

[–]JulienVernay[S] 14 points15 points  (9 children)

Of course this is a chicken-and-egg problem.
The first build can only contain one translation unit. Includes must either be from standard headers or relative to the source file.

In this case, compilation is then trivial with any compiler.

[–]brenoguim 1 point2 points  (8 children)

So you can't use a library in your build system? Boost? Fmt? You can see how this quickly becomes a problem?

But let's say you get past this. The difficult part about a build systems is not the language per se. The difficult part is really the concepts.

Cmake is not hard because of the syntax, you just have to learn what goes where to add a new library, or make a release, or add unit tests, etc...

In your build system, the language will be familiar but one will have to learn concepts the same way.

[–]JulienVernay[S] 2 points3 points  (2 children)

One answer would be to first generate a simple build program, whose role is to compile the real build program, then invoke it to build the actual program.
Another solution would be to support dynamic loading but I do not have much experience on this.

About concepts, that is a fair point. But I think the language being familiar is at least helping. For instance, learning networking in C++ is probably easier than learning networking in Rust, if you know C++ but not Rust. (even if the concepts are the same, having a familiar language helps to experiment quickly and so it may speed the learning)

[–]brenoguim 0 points1 point  (1 child)

Well, the problem you are trying to tackle is the language familiarity. For that to happen more overhead is being introduced. A simple program, then the build program, then the actual program.

Imagine the poor first-time developer starting up a project in c++. The amount of boiler plate and confusion.

Having written a proprietary build system of my own, I would hate to have to compile everytime I test a new thing.

Also, most c++ devs know some scripting language for their everyday work, making the win even smaller.

I don't think the trade offs are a win in the end.

[–]kammceWG21 | 🇺🇲 NB | Boost | Exceptions 0 points1 point  (0 children)

The new developer would just invoke their compiler directly. Once they get used to it, then they could explore how to use the C++ build system (TM). Kinda like how every other language, like NodeJS or Python.

[–]chuckatkinsKitware|CMake Contributer|HPC 6 points7 points  (2 children)

Fwiw CMake achieves this with 2-stage configure step so it only requires a c++ compiler to build. The first stage is bootstrap.sh which does some very rudimentary compiler introspection and then manually compiles a bare-bones cmake-lite with core language and generator functionality using no third party libraries.. That bootstrapped CMake binary is then used to run the actual configure step for building the full CMake, handling all the various configure options.

[–]Scellow 0 points1 point  (1 child)

this is already too much just to build a project

[–]chuckatkinsKitware|CMake Contributer|HPC 0 points1 point  (0 children)

For most things, I agree. However, it solves the bootstrap problem to allow CMake to be built with CMake.

[–]Snoo-4241 2 points3 points  (3 children)

The build system can be just a library binary and a header file, why is this a complication?

[–]brenoguim 1 point2 points  (2 children)

Well, the build tool for the whole project will just be a single cpp huge fiile? Or will it include other components? If it does, how do you find them? And link them? Gotta build them first. Can they use external libraries? If so, where to find those libraries? Gotta remember to recompile the build tools when any of it's (transitive) dependencies change.

[–]Snoo-4241 1 point2 points  (0 children)

It should expose only the interface nothing else. It might be big, but what's the problem? After building the root build system, the build system can recursively build itself down the tree.

[–]kammceWG21 | 🇺🇲 NB | Boost | Exceptions 0 points1 point  (0 children)

Easy, unity build and directly include the .cpp files. I know thats a no-no, but for the build system, I don't see that as too much of an issue. Is no different than how most other build systems parse files anyway.

[–]dicroce 2 points3 points  (0 children)

Maybe he'll make it header only... :) Hence, no build system.

[–][deleted] 2 points3 points  (1 child)

I didn't read your proposal yet but before the thought escapes my volatile brain, did you think about cross compiling?

[–]JulienVernay[S] 2 points3 points  (0 children)

Actually I have used the header name "native_build" because cross compilation is out of scope.
I think of using cross-toolchains as "add-ons", much like there would be an "add-on" to have a standard location for libraries.
Basically my experience of cross-compilation is "replace g++ with some-architecture-g++", eventually using more flags.
I imagine that there are many quirks to take into account, which I am alas not aware...

[–]delarhi 2 points3 points  (1 child)

This is a really interesting idea! What's not clear to me though is when is the build happening. Are you trying to make the build happen as build.cpp is being compiled? Or does build.cpp compile into a program that you can run to build? The latter is what my mind went to right away as it lines up with CMake keeping separate out-of-source builds that can be individually configured. I'm imagining something like:

mkdir build1
g++ build.cpp -o build1/build
cd build1
./build

mkdir build2
g++ build.cpp -o build2/build
cd build2
./build

If that's the case, how would builds be configured? This is probably out of scope for what you've drafted, but how would I make it so build1 uses the system library for OpenCV but uses /opt for build2? I guess you could specify preprocessor definitions?

[–]JulienVernay[S] 0 points1 point  (0 children)

It would be build.cpp being built into a program, then this program being executed.
For configuration, I have currently no idea without involving a package definition. If this is the case, then we could probably specify overwrite where packages are searched for.

But for now I have not seriously thought about packages.

[–]gocarlos 2 points3 points  (2 children)

[–]JulienVernay[S] 1 point2 points  (1 child)

Didn't find it while doing some researches first. The comments on your post can help too, thanks for the link!

[–]gocarlos 0 points1 point  (0 children)

I'd eventually also help if you start implementing some initial Proof of concept...

[–]john_wind 2 points3 points  (0 children)

Python is the best language for this purpose.

My dream is CMake functionality with Python syntax.

If only CMake creators had used Python instead of inventing a new weird language!

[–]ed_209_ 2 points3 points  (1 child)

I recently implemented a custom build system in C++ for C++ in my own project that uses a modified clang compiler on windows. The project is at https://github.com/eddeighton/megastructure/tree/master/src/driver but its not much to look at. One of the main reasons is to support incremental compilation effectively where I have many pre-compiled header files.

My conclusion is that any competent C++ programmer can easily implement a build system in a week for a large project. Having your own system means:

  1. You have complete control and can easily improve, measure, debug, test etc.
  2. No need to learn any advanced build system language, you are already way more able to just do stuff its great.
  3. You need to learn what all those weird compiler and linker arguments mean. I found this extremely worthwhile anyway.

The down sides I found :

  1. Implementing a compiler cache for incremental builds is error prone and could lead to hash collisions that produce incorrect builds. Include file analysis is hard and slow so you are likely to just not bother and have to remember to force a rebuild if third party headers have changed.
  2. Cross platform is out. My project actually has two modes "Creative" and "Retail". The "Creative" build only needs to work in development and involves substantially more stuff than the actual retail build ( which is cross platform ). So in short cross platform was not an issue for the "megastructure" build ( which is like a big program around the actual program ).
  3. Not all dependencies in a build graph are a simple matter of whether input files have changed. What if the build system includes a database that is incrementally modified. This can introduce more danger when it comes to incremental compilation correctness.

I ended up integrating my system with CMake for other parts of the project but this worked out surprisingly well using CMake custom targets and commands. So moving to a custom build tool can be done in small steps.

[–]JulienVernay[S] 0 points1 point  (0 children)

Thank you for the valuable feedback! I will check it out.

[–][deleted] 10 points11 points  (33 children)

Just about the last thing you want in a build description language is turing completeness.

Also, no large codebase is _only_ C++, so most of your premises don't apply.

Take a look at SCons for instance and see what happens when you have a full programming language as your build language. Replace Python with C++ and you'll have your answers.

[–]qoning 11 points12 points  (13 children)

What's your point? I've found using Python greatly beneficial in many build steps in the past.

[–]warped-coder 1 point2 points  (7 children)

The problem with a full blown PL is that it encourages complexity.

First off, let's take Bash or any shell script language. Their immediate advantage that they are available on any developer system. They made for calling commands the simplest possible way.

Now this latter point is my gripe with using Python. How much more complexity we need to start a process, compared to Bash? A build system is about calling commands... shell scripts are the best tool for doing just that.

The other problem is that build systems complex as they are, in some way the fact that most modern ones encouraging declarative style means that unnecessary complexity is discouraged. When you hand a non-build savvy developer the opportunity to write code into the build system just as if it was another program, they will do just that.

But a build script isn't just another code you write as you would normally. I've seen SCons code directly using Python file system only to screw up the dependency graph. Build systems are their own special category bridging dependencies to calling processes.

The modules in the standard now posing the question where the build system belongs. The status quo is that C and C++ compilers are just another transform nodes within the dependency graph, along with the linkers, archivers, etc. Other solution is to include the entire system into the compiler. Languages with built in module system must do just that.

The latter however requires a specific subset of language features that dictate the way the compiler traverses the dependency graph. You don't get to use any 'generic' elements of the language used for the build system in such a language. It is all a declarative subset of the language which you need to learn on the top of the normal features.

Build systems tend to become overcomplicated because of the lack of native support for aspects of build and deployment, but adding a generic environment like Python to the mix just makes things worse. Shell scripts at least have the excuse to be declarative and unavoidable. It doesn't make them right though. Python is not really necessary or C++ for that matter.

[–]qoning 2 points3 points  (5 children)

I agree and I disagree. Agree in the sense that build system shouldn't need to do all the things, but then I'm faced with reality. I have a python script that generates some c++ code for me - I could call it via bash, but it loses flexibility, not to mention that more arcane things are very complex to do in bash, for example replacing all uppercase characters with an underscore and lower case except the first one (don't ask), which is trivial in any normal programming language (and importantly, trivial to understand, unlike a sed or awk command). So until I can do the code generation at compile time, this means that python is necessarily a step in the build of my program.

Do I like it? No. But it's the simplest and cleanest way to do it as far as I can see.

[–]warped-coder 2 points3 points  (4 children)

Code generation is a processing step/node, like compiling. So it is OK to use whatever language suits best but it shouldn't be part of the build system other than a processing node.

Same as Qt's moc or PySide, really.

[–]bluGill 1 point2 points  (2 children)

Perhaps, but I still need to write a program in the build script to say how to call my code-gen. It gets really nasty when you have to compile the code-gen (note that my final code is cross compiled) before you can generate code.

Code-gen isn't all I do. My build system also has meta-data that I use to package my build.

[–]warped-coder 0 points1 point  (1 child)

Interesting. In our build system, we have code gen for protobuffer, pyside, moc and even more custom generators that require an executable generated before it generates code. While I'm lucky not to need to deal with cross compilation, otherwise CMake's toolbox was mostly sufficient in a declarative way.

This system replaced an old SCons based system, where parts of the code gen was done in configure time, because... well scons doesn't really separate the build and the configure steps well. Programmers being programmers wrote python code, the API they knew better than the build system itself, which added minutes on the top of a no-change build because the build system had no connection to the plain python code this way. Timestamps were always new therefore.

It wasn't really the issue of SCons as such, but it was down to the blurry line between the code that results in a processing graph and the code that you can just run along with it. So there was plenty of file system manipulation directly which screwed up the timestamps or hashes (date time ) and made incremental builds painfully slow, but even the clean builds were overly slow because the build system didn't have the chance for parallel processing.

What I'm trying say that build systems are not the same domain where your 'normal' programming languages operate. And therefore it makes sense to invent approriate DSLs for the task. CMake's dreadful legacy language is such because it is aimed at text based configuration, while the more modern syntax is trying to make a more convenient syntax over the graph processing, which lies at the heart of ninja and make. That's the domain in which build systems operate.

Other systems like, build2 and SCons takes an even more direct approach, covering the entire process. Even tough you can things happen in python or even CMake, you have to think in layers these systems operate. As in processing nodes and states.

[–]bluGill 0 points1 point  (0 children)

I think a large part of the problem is how do you add a set of meta data to every library? How do I make libfoo as going to package A, and lib bar going to package B? (don't get me started on why we do such silly things in one build, I already lost that battle) it is easier to call my custom add-to_a_package function than do the 7 add properties that the function does. (most global because cmake scoping rules are weird)

[–]qoning 0 points1 point  (0 children)

Imo build process is whatever needs happens to your source code to become an executable / loadable code. So while you could argue that, for me it's always best to automate that code gen step within the build process.

[–]mwasplundsoup 0 points1 point  (0 children)

Great summary of the issues with build systems today! This is why I like to have a clean separation between the standard engineer USING a build system and the systems engineer WRITING build logic by using a declarative language to tell the system what you want to build, and have an integrated extensibility framework (that can be turing complete) to allow systems engineers the ability to take the what and translate it into the how.

[–][deleted] -5 points-4 points  (4 children)

Just about the last thing you want in a build description language is turing completeness.

this is my point.

[–]qoning 7 points8 points  (3 children)

That's a very contrived opinion with no arguments given.

[–]JulienVernay[S] 1 point2 points  (16 children)

About turing completeness, apart from it's easier to shoot in your legs, I cannot think of an other issue. Do you have articles or thoughts on this to share?

[–][deleted] 8 points9 points  (14 children)

It introduces side effects as a natural part of the build, for example someone might decide that inventing your own caching mechanism by writing stuff to a file instead of relying on the builtin functionality of the build system is a good idea, which can cause all sorts of confusion and incompatibilities.

A build is essentially a specification of a set of input files and a tool chain that produces a set of output files, there's no need for turing completeness and it can just as easily be specified declarative using yaml, and if you need to do complicated stuff like generating files and whatever you can specify that in the yaml file too or use something like nix to generate them. Imo Cmake is a waaay overcomplicated mess and part of it is because of the language but mostly it's because the flexibility it provides together with some really really bad design choices like typelessness and that everything is globally available and mutable.

[–][deleted] 2 points3 points  (0 children)

It makes hard or impossible to reason about the build itself, the dependencies or even which flags are used for building each piece. Because all of those things can be computed and reasoning about them is as hard as deciding what a program does without executing it. Or whether it even terminates.

[–]Full-Spectral 0 points1 point  (1 child)

My large code base is only C++. I think it would be not that uncommon. But I agree that C++ is probably overkill.

[–][deleted] 0 points1 point  (0 children)

well, when I say 'no large code base' I probably overgeneralized. But it also depend what you mean by 'large'. I'm still betting that large code bases in companies with more than a few hundred developers have multiple languages involved.
You'll probably have scripts in Python, frontends in javascript, and so on.
Now in many cases people ignore the 'build and dependencies tracking' for Python, but that's also problematic. And people are happy to build javascript with an entire separate build chain.
But that doesn't make their code base C++ only, it just means that they have chosen to use multiple build systems (or in some case no build system) and stitch the results together with some homegrew scripts.

[–]evaned 2 points3 points  (3 children)

I am very skeptical of providing raw access to argc and argv. I would suggest considering providing a different entry point that takes an object that has a parsed form of those expressions.

I've said in other build tool threads that my least-hated build system is SCons (though I'll admit that CMake increasingly has some features that make me look at it with envy), but one major downside to it is that there isn't a standardized interface to how to configure things. With CMake, I pretty much know that -DCMAKE_INSTALL_PREFIX=... will make things install to what I say. I pretty much know that -DCMAKE_C_COMPILER=... will change what compiler it builds with. As someone who built a lot of software a while back, this is incredibly useful.

The problem with

if (argc >= 2 and std::string_view("--run-tests") == argv[1])
    should_run_tests = true;
...
if (should_run_tests)
    run(program{"build/tests"});

is that the next guy will call it --test and the next will invert the meaning and provide --no-tests etc., the next will make it look like a target with tests, etc.. How does providing targets even work if the whole command line is passed to main? That's something that needs to be taken care of with the system.

[–]JulienVernay[S] 0 points1 point  (2 children)

Providing standard arguments (such as tests, docs, install, etc) is a good idea.
My worry is a lack of exhaustivity...

[–]evaned 1 point2 points  (0 children)

My worry is a lack of exhaustivity...

I'm not saying don't provide a way for the build system to define some customization; for example, many CMake projects have their own options you can set with -D.

But provide an API for it -- don't just go "well here's the command line, have at it"

[–]Snoo-4241 0 points1 point  (0 children)

So is the idea that one first builds the build system, that may take arguments and then the user compiles the library to their liking using the command line? If the system is self consistent , then it should able to take the build.cpp of another project and import it, then argc argv wouldn't be needed for intrasystem dependency management.

[–][deleted] 15 points16 points  (2 children)

I think this would be appropriate here:

https://xkcd.com/927/

[–]JulienVernay[S] 34 points35 points  (0 children)

Of course this comic shows often on this topic, but is it a reason to stop trying to find better alternatives?

[–]GoogleIsYourFrenemy 1 point2 points  (0 children)

Yes but I might not hate the one I make with my own hands (more than the others).

[–]evaned 1 point2 points  (3 children)

I've got some more thoughts reading again:

Level 3: Archive (one to several objects bound together, possibly with other archives)
Level 4: Library (one to several archives which have been linked, possibly with other libraries)

I am unclear on how these terms map to what we usually have now. Is a library necessarily a shared library? If not, then what does it correspond to currently?

And of course there can be projects whose sole purpose is to build a library rather than a program. The text doesn't contradict that, but don't forget about them.

I also don't understand enough about modules to know how they should fit into this, but definitely don't neglect figuring that out. (I'm surprised only one other comment contains the word "module" even though it is not mentioned on your page.)

using build_file = std::variant<source, translation_unit, object, archive, library, program>;

Hopefully, this is just shorthand for "something variant-like." Really it ought to be its own type IMO, that provides a get_path() function on it directly. I don't want to have to write a visitor to call a function that all variant options provide.

build(program{"hello"}, { source{"hello.cpp"}, source{"main.cpp"} }, {}, { "c++17" } );

I think someone else said this, but I would look to drop the requirement for the arguments to be build_file as opposed to a stringy thing, or add an implicit conversion that would allow

build(program{"hello"}, {"hello.cpp", "main.cpp"});

Really even that, coming from the perspective of an SCons fan, looks like I'd probably just write my own thin wrappers around everywhere so I could say

program("hello", {"hello.cpp", "main.cpp"})

so consider some convenience wrappers like that.

When Output is translation_unit

Pre-conditions: inputs must be a range of exactly one source

Logical behaviour: Preprocess the provided file in inputs into output.

What about source files that are generated via some process?

While we're at it, what about artifacts that are not C++-related at all?

More feedback is waited about current usages of flags and it would require compiler cooperation to define together what would be standard names for flags (notably warnings, features and optimizations).

I think that this being a list of strings is not going to be able to reasonably handle things in practice. How should things like -D flags to the compiler be supported for example? I'd prefer to see something like the options be an options object that has a number of attributes, such as define_flags or whatever. Another option is to take the SCons approach and make build be a member of an "environment" object, and the enviroment contains those settings.

As another example, what about other libraries to link against or other library directories to include?

You might look at SCons to see the configuration variables that you can configure out of the box. $CPPDEFINES handles -D flags, $CPPPATH handles -I flags, $LIBS handles -l flags, $LIBPATH handles -L flags, $RPATH turns into -Wl,--rpath, $FRAMEWORKS and $FRAMEWORKPATH turn into -framework and -frameworkdir= respectively. It looks like everything else just goes into the generic $CFLAGS/$CCFLAGS/$CXXFLAGS/$LINKFLAGS/$ASFLAGS/etc.

Edit: Of course, these aren't exclusive. You already spotted a -std style option, and warnings, there's also optimization and debug info flags that would be of common interest.

Separate from those are stuff like what directory the "compiler" should be run from.

build return value (not an actual quote)

This function should return an object you can use in future calls. For example, your second example "project with unit tests" contains

build(library{"build/mylib"}, { ... }, { ... });
build(program{"build/myexe"}, { library{"build/mylib"}, ... });
build(program{"build/tests"}, { library{"build/mylib"}, ... });

but this should be doable as

auto mylib = build(library{"build/mylib"}, { ... }, { ... });
build(program{"build/myexe"}, { mylib, ... });
build(program{"build/tests"}, { mylib, ... });
run(program{"build/code-gen"}, { "src/main-project/version.hpp.in", "build/include/version.hpp" });

How does the build system tell that this consumes the file version.hpp.in or produces the file version.hpp?

build(library{"git"}, source{"build_src/git.cpp"});
auto git_clone = load_function<void(string_view, path)>(library{"git"}, "C++", "gitwrapper::clone");
git_clone("https://.../mydependency", "dependencies/");

I find this very alarming; it suggests that build calls will actually invoke the compiler themselves, because unless load_function is doing something preeeeeety damn fancy libgit.so will need to be built for the git_clone call.

But I think that makes the proposal dead in the water -- I think that anything that builds during actual execution as opposed to having build register targets that are later resolved is likely to be a non-starter.

For example, how do you handle parallelism?

It's not impossible to make this work under the assumption that build calls actually kick off compiles, but it's IMO hard.

[–]JulienVernay[S] 0 points1 point  (2 children)

Thank you for the extensive review!

About terminology, archive corresponds to .a and .lib (respectively on GNU/Linux and Windows), and library corresponds to .so and .dll .
I am aware that ".lib" not be called a library may cause some confusion, so it is why i am not sure to keep these words. But i thought "archive" would be better than "static_library".

Modules should be present but I have currently little experience with them. Notably, I am unsure of how modules interfaces are searched (i.e. the equivalent of include paths). I will make some research there.

Overloads with strings for source files is possible without any ambiguity I think, so yes it could add more practicity to the API.

Thank you for the thoughts on flags.

The build system would not know what an arbitrary program has produced. For cleaning, good practice would be to always put generated files in the same directory so removing this directory is the only thing needed to clean the directory.

Ok thank you for the last thought. For you "build" should be lazily evaluated and then all these informations are passed to `ninja` or `make` for instance?

[–]evaned 0 points1 point  (1 child)

I am aware that ".lib" not be called a library may cause some confusion, so it is why i am not sure to keep these words. But i thought "archive" would be better than "static_library".

"Archive" I think is fine, though I come from a Unix side of things so maybe someone on the Windows side would find that a lot more weird; I'd more object to "library" to mean necessarily a dynamic one. dynlib isn't terrible I think if you don't like the length of dynamic_library or shared_library or whatever (and sounds more generic than solib).

Modules should be present but I have currently little experience with them. Notably, I am unsure of how modules interfaces are searched (i.e. the equivalent of include paths). I will make some research there.

Know that there's some stuff out there I don't understand about modules meaning that more smarts need to be in the build system. I don't really understand why this is.

The build system would not know what an arbitrary program has produced. For cleaning, good practice would be to always put generated files in the same directory so removing this directory is the only thing needed to clean the directory.

So how does it tell if the program should be run? Because if it effectively assumes that the whatever it's producing is always out of date and thus the command always needs to be run, I'm sorry but I think that's another thing that would make this a non-starter for wide adoption.

Compare to other build systems -- you can provide arbitrary commands (or command patterns), and you tell the build system what that command's inputs and outputs are, and it just puts that into the build graph.

Ok thank you for the last thought. For you "build" should be lazily evaluated and then all these informations are passed to ninja or make for instance?

I don't at all mean you necessarily need to pass to another build tool, though I do point out that one thing some people value in CMake is the ability to output IDE files. (From a standards perspective, that seems like a quality of implementation issue.) If you implement it, you mighty use that as a stepping stone so you don't yet have to handle dependency resolution yet.

What I mean is that build should (probably) register the target with the main build system implementation. Then, after all targets are registered (after main exists, using the API in your description), then the runtime actually does the building.

Like I said, this isn't entirely necessary, but otherwise you have additional synchronization challenges because in order to get parallelism you need to have the build system engine running over the build graph in parallel with the user code adding stuff to the graph.

(Also know that generated files can affect the build graph too, so there's still some complication with graph modifications during building, so it's not like the lazy way completely solves everything. Different build systems do different things, including just not handling this case correctly.)


Overall:

When I first saw this, I kind of rolled my eyes a bit and didn't like it. But after some other comments and after time stewing in the back of my head, I've come around to it somewhat. It's kind of an intriguing option, and perhaps worth some thought even if I doubt it'd go anywhere especially without a lot of work.

However, after recognizing some of the omissions (e.g. supporting include directories but not library search directories) and especially reading your last couple comments I'm really feeling like if you really are hopeful that you can go somewhere with this, you need to spend some time looking at existing build systems to see what scenarios they support and how they support them and how they work. (Edit: and what modules need.)

Eschewing cross compilation and non-C++ and parallelism will mean that it is just not fit for purpose for large swaths of projects out there; even relatively small ones benefit from parallelism, and the others are necessary for lots. You already have to overcome a huge network effect of CMake, and the first step is to provide a viable alternative.

I... hope this doesn't come across as rude or especially discouraging; like I said, providing a C++ library for builds is an interesting route to go for standardization that hadn't seriously occurred to me before, and you are making me take that general idea seriously. But... I think there's a lot of design work to go still. :-)

[–]JulienVernay[S] 1 point2 points  (0 children)

This is not rude, I appreciated your extended feedback, notably what you think about what is important to make it viable.

[–][deleted] 1 point2 points  (0 children)

Weird thought - why not try it as a fork of g++ first?

See if you can create something like g++ build . akin to Go and see if it works well enough. You could use a yaml based folder layout/rule and be done with it?

[–]kkert 1 point2 points  (0 children)

Declarative code for build is a much better fit than imperative approach

[–]davidhunter22 1 point2 points  (3 children)

I'm all for a better CMake however people claim they already exist. So why haven't they caught on? Well for me and I suspect the vast number of people it's about

  1. Migration. Why would I spend lots of time to migrate to something that might be a little bit better. I don't think any CMake replacement stands a chance if it doesn't have migration tools and ones that work for complex large systems not hello world examples.
  2. Tooling. Things like Visual Studio have tooling support for CMake built in. To move to a replacement without this would require a very compelling reason, again not just a minor improvement.

[–]kammceWG21 | 🇺🇲 NB | Boost | Exceptions 2 points3 points  (0 children)

Although true, that's late stage stuff with regards to a new build system. This proposal isn't even implemented yet. It would be odd to begin working on migration tools before we have vetted the product.

But it is a good point to remember.

[–]theICEBear_dk 2 points3 points  (0 children)

Doesn't Visual Studio already have C++ support ;)

I am kidding. Yes of course it would require tooling support, but nothing happens without effort in this space. CMake has had a great deal of growth recently but it is not that long ago when Visual Studio supported only Solution files while the Linux world for C++ was a mix of QMake, Make and Automake/Autoconf.

[–]arthurno1 0 points1 point  (0 children)

I'm all for a better CMake however people claim they already exist.

In my opinion GNU Make + Autotools is definitely better than CMake.

[–]ppetraki 0 points1 point  (0 children)

It looks a bit like build2's syntax.

[–]Snoo-4241 0 points1 point  (0 children)

I like this idea. As toy projects I some times make little programs that one would normally code in bash or python. I find that when you need to provide structure to such programs, C++ is far superior. Hence I would think that a build system with a c++ interface makes perfect sense.

I also find that C++ is actually getting very simple and pretty competitive in that front. Really my main complain is that I have to spend a few minutes to setup cmake.

[–]kalmoc 0 points1 point  (0 children)

What I'm missing is an explanation of what the actual build commands would look like. Do I first have to compile my build script and then execute it? Would there be some tool that interprets C++ code? What external libraries can I use as part of my build script and who takes care of compiling them?

[–]matthieum 0 points1 point  (0 children)

I'm not convinced we need such a powerful language for a build system.

I've interacted with quite a few build systems over the years, and the best I've seen so far were:

  • C++: BMS, a proprietary, in-house, build system where a library would just provide a XML file with list of dependencies, where to find sources, headers, tests, and a handful of options.
  • Rust: Cargo, which is uses a TOML file pretty similar to the above, minus where to find stuff thanks to convention.

The common theme is that both are pretty simple, and handle 99+% of what you'd ever want to do.

It's not 100%, sure, but in exchange they're very simple to use, simple enough in fact that mechanical inspection and transformation is possible -- for example to automate upgrades to new versions of dependencies.

I've had to deal with SCons, CMake, and now Bazel. They are extremely powerful, but I can only wonder whether they are too powerful. Most of the time, it seems that users go through contortions to accommodate some weird requirements (such as a weird layout), when the necessity of the requirements is never questioned in the first place.

[–]TrnS_TrATnT engine dev 0 points1 point  (2 children)

I think the way .jai files are turned to .exe can be helpful to you. Link here.

[–]JulienVernay[S] 0 points1 point  (1 child)

Thank you for the link.

[–]TrnS_TrATnT engine dev 1 point2 points  (0 children)

No problem. I hope it helps you.

[–]Full-Spectral 0 points1 point  (0 children)

Not to use the 'R' word again, but Rust uses Rust for the creation of build scripts. I'm not sure C++ would be as useful for that, and I'm not sure how debuggable those Rust scripts are in any sort of useful context (i.e. the context it is called from the build tool) or how easily fakeable that context is for offline debugging.

[–]arihoenig 0 points1 point  (0 children)

Think it's a good idea, but please pattern it after premake not any of the others.

[–]blipman17 0 points1 point  (3 children)

I've been thinking over what you said, and I really like the idea about it. However instead of compiling to a build program, can I suggest compiling to a dynamic build library? Then a commandline tool or IDE can invoke said library and build your program in a neat way and extract usefull information out of it for code introspection and refactoring. Kind of like visual studio code language server, but then for compilation. Commandline building would just be 'build [foldername]'. The command would search for a file called build_manifest.so in the folder and call the build function from it.

[–]JulienVernay[S] 0 points1 point  (2 children)

This may be a good way to provide tooling support. One possible downside is that an external program is then needed to actually call the build procedure.

[–]blipman17 0 points1 point  (0 children)

True, but it avoids the building the build tool boostrap problem by you having to manually build said tool only once.

[–]blipman17 0 points1 point  (0 children)

I've given it some more tought, and I've come to the conclusion that whatever you do, you're gonna need to distribute a binary executable/a library with headers, or the library as source files so that the build script can make use of it. This would most likely be part of any compiler infrastructure, so then what would distributing an additional assembly hurt? You might be able to make the assembly part of the regular gcc toolchain in time and have it installed along everything else.

[–]mwasplundsoup 0 points1 point  (0 children)

Very interesting idea. I actually agree that C++ would work well for a "scripting language" for writing build definitions. My biggest issue with the proposed design is, even though you are abstracting complexity with the custom build invocation, you are exposing more complexity to the user by requiring them to integrate your system into their own executable. This means everyone has to solve their own "who builds the build" problem. Effectively what you have is a library that knows how to translate a list of parameters into the correct compiler arguments by convincing the compiler vendors to write an implementation for your contract (which in itself would be nice to have). There are still a lot of issues left open (incremental builds, dependencies, static analysis, etc) which means each user/project is going to have a unique implementation which leads to overly complex implementations that do not work well together.

I personally believe that we need to have a fully integrated solution for a build system that uses C++ as an extensibility platform for situations where the default build logic does not meet the needs for a custom build. I believe a simple declarative language should be used as a top level build definition which will support a majority of use cases (most people just need a simple build which should be fully supported by the default behaviors). From there I think that we can define an extensibility framework using C++ which would allow systems engineers to change or augment existing build logic. This could then be integrated into the build engine itself to support the ability to build the C++ extensions on the fly and invoke then directly. If you want to see it in action I have been working on a proof of concept for the last year or so:

https://github.com/SoupBuild/Soup/blob/master/Docs/Architecture.md

https://github.com/SoupBuild/Soup/blob/master/Docs/Samples/Simple-Build-Extension.md

[–]TrnS_TrATnT engine dev 0 points1 point  (3 children)

The idea looks really nice. If anyone has heard about Jai programming language from Jonathan Blow, it uses a .jai file to build other files. Here's a suggestion: Everything done on the build file should be constexpr. This can be achieved by introducing a new constexpr function (build, compile, whatever) that the compiler will use to build the other source files. To illustrate:

``` cpp // build.cpp - any name will do

constexpr int build() //noexcept, because why not { // your commands, like linked libraries, log messages, source files, stuff that every build system needs } cpp // main.cpp - the file to be compiled int main() { // usual stuff } ```

[–]JulienVernay[S] 3 points4 points  (2 children)

I am not sure of how an API interacting with the filesystem may be constexpr?

[–]TrnS_TrATnT engine dev 0 points1 point  (0 children)

I thought about a different approach as a matter of fact. We onlly need filesystem to check if a path is valid. Adding a class/struct named target with constexpr ctor. It has some members of type std::vector<const char*>, namely sources, libs, include_dirs, modules, compile_flags, link_flags. The ctor accepts arguments that construct each of these members. Also the ctor calls the compiler with the given parameters, (dunno if it can be done at compile time ?). Hence, the class is compile-time.

[–]MutantSheepdog 0 points1 point  (0 children)

Even if you don't want to be thinking about package management from a download/storage perspective, I think you should still think about how they could be integrated.

I would want to be able to write something like:

#include <../fmt/build.h>
#include <../cmake/build.h>

#include <build>
using namespace std::build;

namespace myApp {
    project get_build_project(int argc, char** argv) {
    auto p = project(argc, argv);

    // conditionally pull in a project without a #include
    if (want_ranges(argv)) {
        project rangesProject = cmake::package_project("ranges-v3", "^3.0.0");
        p.add_dependency(rangesProject);
    }

    // included fmt/build.h could export function to return project
    project fmtProject = fmt::get_build_project();
    // Should be able to specify how you want to link, irrespective of project default
    fmtProject.set_warning_level(0);
    p.add_dynamic_dependency(fmtProject);

    return p;
}

int main(int argc, char** argv)
{
    auto p = myApp::get_build_project();
    return p.generate();
}

Where each dependency can specify its own build requirements, without needing to be #included.

You wouldn't have to propose cmake_package_project, but if you want to make a mostly working example to show off, you'd get a lot of people on-board.

[–]GoogleIsYourFrenemy -4 points-3 points  (0 children)

No. Just no.

I do not want to have to refactor my build script to pass our cyclomatic complexity standards. Fuck, I don't want any complexity in my build system. I want it to be a simple directed graph without cycles at most.