Live data from Hacker News

Everything you never wanted to know about CMake (redux)

izzys.casa

51–58 of 58 posts

Re: Everything you never wanted to know about CMake (redux)

#51
post #4

Whenever I have to work with CMake, I find it useful to adopt a “please sir, may I have another” attitude.

It feels a lot more like "The beatings will continue until morale improves" to me.

I flinch just a little bit every time I open a CMakeLists.txt in my editor.

Re: Everything you never wanted to know about CMake (redux)

#52

Earlier quoted context omitted.

Good questions. To be frank, Bazel is the build system that comes closest. The biggest selling point of mine against Bazel would be usability (I hope). So really, I would suggest people stay on Bazel if they already are. (In fact, I would suggest that CMake users stay on CMake. I only want to capture new projects to start. If my build system then proves itself, people who need to will switch by themselves.) About dyn…

> But there is no way to make the preprocessed file depend on the headers because its dependencies must be defined before the build and the list of included files is generated during the build. The actual dependencies of a build action in Bazel are defined at build-time, dynamically, after the action runs. So you do not need to know the exact dependencies ahead of time, you just need a superset—not something you can…

> So you do not need to know the exact dependencies ahead of time, you just need a superset—not something you can really avoid, as far as I can tell, because the way that #include will search multiple paths.

You can avoid it with the system I laid out. You don't need to specify any dependencies, but specify them while the target is executing. You could even create the dependency while the target is executing.

I forgot about the multiple include paths, but this is another reason that dynamic dependencies may be useful.

That's not to say that Bazel is bad! It's just a different model. And dynamic dependencies may not be useful. That's perfectly fine! It's also why I would only suggest trying out such a build system on a new project. Don't break what's not broken!

> I know this is possible in other languages as well, such as C++, I just don’t use those tools.

Not really; you can't know what compilation unit will contain a function in C and C++. Well, you could try to hack it with grep or something, but I consider that less desirable.

> “Dependency information in the actual source” is what you get with Gazelle. There is some duplication in the build files, but I like this and find it useful—you can redirect dependencies to be fulfilled by other targets than what would be the default, for example.

This is really cool!

I need to clarify that you would still be able to redirect dependencies. Unlimited power is unlimited, after all.

But Gazelle sounds cool, and Bazel sounds cool. I don't mean to put them down.

Re: Everything you never wanted to know about CMake (redux)

#53

I cannot overemphasize how enjoyable it is to work with cargo when I'm coding in Rust; I think it's one of the major reasons why Rust took off. As complicated as Rust is, it's a breeze to not only build projects but also add existing projects as dependencies.

Most of my Rust projects, even large and complex ones, don't have any build script at all. That made me realize how much time and energy I used to waste on maintaining build scripts.

Re: Everything you never wanted to know about CMake (redux)

#54

Earlier quoted context omitted.

> But there is no way to make the preprocessed file depend on the headers because its dependencies must be defined before the build and the list of included files is generated during the build. The actual dependencies of a build action in Bazel are defined at build-time, dynamically, after the action runs. So you do not need to know the exact dependencies ahead of time, you just need a superset—not something you can…

> So you do not need to know the exact dependencies ahead of time, you just need a superset—not something you can really avoid, as far as I can tell, because the way that #include will search multiple paths. You can avoid it with the system I laid out. You don't need to specify any dependencies, but specify them while the target is executing. You could even create the dependency while the target is executing. I forgo…

I’m not sure what you mean by specifying dependencies while the target is executing. The thorn here is generated headers—if they aren’t built before your compiler runs, then you are stuck.

> Not really; you can't know what compilation unit will contain a function in C and C++. Well, you could try to hack it with grep or something, but I consider that less desirable.

This is solvable and has in fact been solved. You use a function in your C++ file, the analysis system knows which header contains that function declaration, and the build system knows which library must be linked in for the header file. This is basically how it works in Go, with some extra steps to associate headers with libraries. But all the pieces are there—you do not need grep, if you want to implement a similar system yourself.

The catch here is that these systems are a bit inexact—any given function could be supplied by more than one library, and the header files may require some specific ordering to work correctly. The solution is to store the dependencies in the build scripts, rather than try and figure them out from sources each time. The general problem, of figuring out the correct headers and libraries necessary to compile a given piece of C++ code, is just too much of a pain in the ass to make it completely automatic—you want a human in the loop. It’s not just a problem with exactness, you also have multiple configurations with their own preprocessor flags, you have dependencies which are specified indirectly but which should be direct (how do you detect that?)

The ecosystem, such as it is, is a chaotic mixture of tools used interactively during development or non-interactively during the build. One of the super useful properties of Bazel build files is that you can modify them programmatically, using a tool called Buildozer. This can be used for things like automatic refactoring of your build system, and it can also be used to make automatic changes to the build system as you edit source code. Part of the “sauce” that makes it work is the way rules are rigidly defined in build scripts. As you make build scripts more complicated, it gets harder and harder for the tooling to keep up—and often, that means more manual work to keep everything set up right.

Re: Everything you never wanted to know about CMake (redux)

#55
we can also try xmake. https://github.com/xmake-io/xmake

Xmake can be used to directly build source code (like with Make or Ninja), or it can generate project source files like CMake or Meson. It also has a built-in package management system to help users integrate C/C++ dependencies.

Re: Everything you never wanted to know about CMake (redux)

#56

Earlier quoted context omitted.

> So you do not need to know the exact dependencies ahead of time, you just need a superset—not something you can really avoid, as far as I can tell, because the way that #include will search multiple paths. You can avoid it with the system I laid out. You don't need to specify any dependencies, but specify them while the target is executing. You could even create the dependency while the target is executing. I forgo…

I’m not sure what you mean by specifying dependencies while the target is executing. The thorn here is generated headers—if they aren’t built before your compiler runs, then you are stuck. > Not really; you can't know what compilation unit will contain a function in C and C++. Well, you could try to hack it with grep or something, but I consider that less desirable. This is solvable and has in fact been solved. You u…

> I’m not sure what you mean by specifying dependencies while the target is executing. The thorn here is generated headers—if they aren’t built before your compiler runs, then you are stuck.

I understand why you think this. It took me a while to understand dynamic dependencies too.

But that is actually not the case. A target that may have dynamic dependencies will run and figure out the required headers or figure out the required imports. At that point, it tells the build system that it needs those dependencies, but if those dependencies are already up-to-date, it can be considered up-to-date too.

The build system checks those dependencies, finds that they are up-to-date and marks the first target done and doesn't finish running it.

> The general problem, of figuring out the correct headers and libraries necessary to compile a given piece of C++ code, is just too much of a pain...to make it completely automatic—you want a human in the loop.

This is what I meant. Sure, you can make good assumptions (and in my monorepo, functions are arranged in specific ways in files, so I could do that), but it's not generalizable.

> One of the super useful properties of Bazel build files is that you can modify them programmatically, using a tool called Buildozer. This can be used for things like automatic refactoring of your build system, and it can also be used to make automatic changes to the build system as you edit source code.

This is really cool. It shows that Bazel is just a different model, and in the eyes of many people, better than mine. That's okay! I'm sure that plenty of people would prefer Bazel's model, including yourself. That's great! Diversity of build systems is good.

Re: Everything you never wanted to know about CMake (redux)

#57
post #34
post #7

> CMake Now Has Dictionaries! (Sort of!) It may be time to update Zawinski's Law to something like: "Every tool expands until turning complete" At the point where cmake is fungible with a programming language, why not just write the build system in the one you know? As with the various markup languages that purport to "simplify" HTML, we're given enough PITA learning curve (how does this doo-hicky do anchor tags, sin…

> "Every tool expands until turning complete" In that respect I think PreMake (Lua based configuration and build system) is more humanistic. Lua syntax provides pretty comfortable, DSL alike, way of defining rules/declaration. And at the same time it is regular and compact PL with established runtime for those 10% of cases when static declarations are not enough. CMake, IMHO, went wrong way of defining DSL first and…

I had meant 'Turing'.

Re: Everything you never wanted to know about CMake (redux)

#58

Earlier quoted context omitted.

“Local minimum” is a great way to put it. I recognize that CMake has done a lot to make people’s lives easier, but it has so many design flaws that the entire design seems almost purposefully user-hostile. I know it’s not hostile on purpose, but it sometimes feels that way.

Begin by throwing the language away. Provide a tool that converts existing CMake scripts to the new, sane language. Use an established programming language for the new language.

If the new language would allow such a tool to work, it probably wouldn't be much better. It would either still have most of the awful behavior that the old language had or the converted scripts wouldn't be better than before.

The way the cmake_policy system works isn't bad, but those unfortunately have only limited impact on the language itself.

Post reply on HN