Hacker News new | ask | show | jobs
by th3typh00n 1517 days ago
Why would you have spaces in source code filenames? It's just a bad idea in general and makes things more complicated for no reason, regardless of what build system you're using.
2 comments

Spaces in filenames aren't inherently bad, they become bad because tools that don't support them are bad. Saying "why would you have spaces?" as a counterargument to "make doesn't support spaces" doesn't work, because the problem with spaces is exactly that make doesn't support them.
Random trivia: Microsoft explicitly added the space in "Program Files" to ensure developers know they need to handle spaces in paths
Joke's on them, I just use PROGRA~1.
Sounds like you've got a handle on it!
You can't depend on that working. It could be another digit.
Or short file names might be turned off.
Same thing with My Documents.
Really? Huh, that's very interesting, thanks!
I'm not entirely sure why "make, but it handles filenames properly and allows spaces for indentation as well as tab" isn't the most popular fork of make.

Does it even exist?

Probably not. You'd need a lot more than a petty switch to grammar regarding indentation to convince people to switch to an unproven alternative, though.
My snarky response is that unproven is better than disproven, but my personal feelings aside, make undeniably has marketshare and people aren't likely to switch to something else unless it offers them a more significant improvement than naming/indentation.
I think, maybe, both of these responses are missing the fact that make is not a program, but rather, a number of programs with the same invocation name.

A fork of one of these makes, which did not have the two glaring flaws I refer to, but was otherwise make, would still be, well, make.

I don't understand why one would use make in 2022 when there's cmake+ninja which is a much better and faster way to build things
Agree that ninja+something can be much better than make. ninja is such a clean minimal design. But cmake? Really? cmake —-help-full is like 2.5MB, and looks like it refers to any know library and tool in the universe. Mahbe I am missing by a mile, but did we not get enough magic exposure with autoconf already?
You believe this because you probably know well how to use cmake+ninja, but you do not know how to use make (more precisely GNU make, which has valuable extensions over the traditional UNIX make).

Almost all the makefiles that I have ever seen used in open source software projects are in my opinion horrible garbage, and they do not use properly the GNU make features and they are extremely difficult to understand and modify.

That does not mean that makefiles must be written in this way. It is possible to write makefiles in such a way, so that the per project makefiles become extremely simple and extremely easy to maintain.

All the cmake+ninja projects that I have seen were also exceedingly complex and hard to maintain, much worse than when using good makefiles. Maybe there also is a better way to organize the cmake+ninja projects, but I have never bothered to study in detail cmake or ninja, to see how that could be done, because I already knew how to write proper makefiles and I have not seen yet any additional feature that I would want when building a software project.

Regarding the claim that cmake+ninja is faster, I have not seen any realistic benchmark proving this in the right way, i.e. not by comparing some good cmake+ninja project with a bad makefile.

I do not see why any of the 2 methods for building a project could be faster for the other. For a properly written makefile, the time spent in executing make is completely negligible in comparison with the compilation/linking steps.

I assume that the same must be true for cmake+ninja, so the speed must be the same when using the same speed-up methods for both, e.g. when providing the same number of source files per compiler invocation, to minimize the compiler start-up times.

Because the same work must be done, there is no way to for one tool to be faster than the other. Any trick that you might do to accelerate the compilation with one tool can also be done when using the other tool.

The only difference between tools is in the maintenance effort that must be done by the programmer when making changes in the project, e.g. when adding source files, deleting source files, renaming source files, moving source files and so on.

With properly written makefiles, nothing must be done for any of the operations mentioned above.

I do not know if this is also possible with cmake, because I have never seen such a well-written cmake project, but even if it is possible, the maximum that cmake could do would be to match what could already be done with make many years before cmake existed.

So I have never seen any reason to switch to cmake or cmake+ninja. All the arguments in their favor that I have ever seen were given by people who have never read the GNU make manual, so they compared their favorite tools with bad makefiles, not with good makefiles, so those arguments had nothing to do with make itself, but only with good ways or bad ways to organize the building of a software project.

Can you link to some examples? I've dug around on Github and tried to find some examples of projects using different kinds of build systems, but I don't think they are great examples of what you're talking about. For others who may want to see examples, here they are:

- A Makefile that runs cmake which generates ninja build files that build the project. Neovim: https://github.com/neovim/neovim/blob/master/Makefile

- Cmake that generates ninja build files which build the application. OBS (Open Broadcaster Software) : https://github.com/obsproject/obs-studio/wiki/build-instruct...

- A makefile that builds the application. Localstack : https://github.com/localstack/localstack/blob/master/Makefil...

- Cmake that generates Makefiles that build the application. Krita : https://docs.krita.org/en/untranslatable_pages/building_krit...

The makefiles indicated by you are relatively simple makefiles, they do not belong to the class "horrible".

The main problem even with these simpler makefiles is that they are too complicated, because they are written for a single project.

This is something that is difficult to change in the case of most open-source projects, which are intended to be independent from other software projects.

The greatest simplifications in makefiles are done by extracting the common parts of the makefiles from all projects in generic makefiles that should be included in the specific makefiles of each project.

Doing this, it is possible to reduce the effort of writing makefiles for 100 or 1000 software projects to little more than the effort of writing a single makefile.

Besides the reuse of the same make rules, of the same make targets and of many make definitions in all projects, other important simplifications are to never write manually the file dependencies when they can be detected automatically, which is normally true for all compilation tasks, and to never write manually the lists of source files, because they should also be detected automatically.

> Almost all the makefiles that I have ever seen used in open source software projects are in my opinion horrible garbage

I mostly agree with your post, and I use Make for my own projects. But, I think there's a reasonable argument that if a tool is so hard to use that no one uses it correctly, it's not a very good tool.

> Almost all the makefiles that I have ever seen used in open source software projects are in my opinion horrible garbage, and they do not use properly the GNU make features and they are extremely difficult to understand and modify.

if tomorrow a pen maker released a new pen where the majority of artists using it only managed to produce shit artworks, everyone would rightly call this pen shit. Even if it's theoretically possible to use the pen in such a way as to not produce shit artworks. Likewise for, say, a tool used for making musical instruments, etc.

How well a tool lends itself to be used by the average person is an important property when choosing to use a technology over another.

Can you provide e.g. a project with your good makefiles, that I could use to build debug and release builds, with any of MSVC, GCC, and Clang ?

> I do not see why any of the 2 methods for building a project could be faster for the other. For a properly written makefile, the time spent in executing make is completely negligible in comparison with the compilation/linking steps.

can you provide one such makefile ? in my experience make always takes a few seconds for projects with >1k targets when changing a single file. Ninja is consitently instantaneous.

> How well a tool lends itself to be used by the average person is an important property when choosing to use a technology over another.

The reason why most makefiles in open source projects are horrible has nothing to do with the qualities of make, because typically those makefiles are not written by some human who knows about make, but they are generated by some other tools, e.g. autoconf/automake.

Moreover, the generation method typically used is flawed, because it generates the entire makefiles, in an incomprehensible form. The maintenance of those makefiles would have been much easier if makefiles carefully written by a human would have been used, which would have included a file generated by the autoconf tools, and the included file should have contained only definitions, and neither rules nor targets.

Unfortunately there is an ancient tradition established decades ago to generate the makefiles in an overly complicated way and nobody has the courage to change that in any project, because probably nobody understands any more what would happen if changes would be made.

> Can you provide e.g. a project with your good makefiles, that I could use to build debug and release builds, with any of MSVC, GCC, and Clang ?

I never had any need to compile something for MSVC, GCC and Clang.

Nevertheless, many of my projects had to be cross-compiled for various embedded CPU targets.

The way I do that is that I have one makefile that contains only definitions for each project building target, so I could have e.g. 3 makefiles, 1 for a MSVC/Windows target, 1 for a GCC/Linux target and 1 for a Clang/Mac OS target.

Each such makefile will have definitions for the names of all executables that may be needed for bulding a software project, e.g. compilers, assemblers, linkers, librarians, object file converters, copy commands, move commands, rename commands and so on.

It will also have definitions for all the command-line option flags that must be provided to each executable in order to perform whatever tasks are needed for building a software project.

Writing such a makefile for a compilation is a one-time effort, I might write it in an hour or more, while searching through the documentation of various tools, but then I might use it unchanged, during many years, for all projects that I intend to build for that target.

I need to write such makefiles only infrequently, when I begin to use a new CPU, or a new operating system, or a new compiler.

These makefiles with definitions dependent on the compilation target are included with an include directive in the complete makefiles that build the software project.

I build each final file, e.g. executable file, dynamic library or static library in a separate build directory. When I want to build multiple files with a single command, then their build directories are subdirectories of a directory where there is a makefile which will invoke the makefiles in the leaf subdirectories and maybe move or copy the built files somewhere else, if the end result needs them to be in certain relative positions in a directory hierarchy.

The makefile in the build directory of some file has only a few lines. It includes the makefile with the definitions dependent on the compilation target and a makefile that is the same for all my projects, with general definitions, rules and targets.

Besides the include directive, there are a few definitions, the type of file that must be built, the name of the built file (when absent, the current directory name is assumed, with an appropriate extension) a list of directories that must be searched to find the source files (if absent the current directory is assumed) and an optional prefix for the list of directories.

Because make searches itself for source files and automatically generates their dependencies, I do not have to do anything when I add/delete/move/rename source files.

For debug and release I obviously have these 2 make targets in the included makefile with general make rules and targets, which are the same for all projects. The included files per compilation target have definitions for the debug and release command-line flags for all the tools, e.g. compilers, assemblers, linkers.

So if MSVC, GCC and Clang on certain operating systems would be my targets, I would have no problem to build these 3 targets either separately or simultaneously, without having to write a single word in the makefiles of the project. When I would create the build directories for the project, I would copy in each build directory the appropriate template makefile, in which I would change, if needed, only the name of the built file and the name of the directory or directories where the source files are located.

> in my experience make always takes a few seconds for projects with >1k targets when changing a single file. Ninja is consitently instantaneous.

You are right that this is the only case when the tool used to build the project can make a difference.

When a large number of files must be compiled, there is no chance to see any difference due to the speed of the project build tool.

However, when only a single file is recompiled, then there can be a significant difference.

Ninja is very simplified in comparison with make, so I have no doubt that it is faster.

For your example with many thousands of files where you frequently recompile only a couple of files, one could use make + ninja instead of cmake + ninja.

The general rules from my makefiles could be modified to generate ninja input files instead of invoking the build commands,

In that case I would invoke make only after making changes like add/delete/rename source files, and then ninja for the actual recompilation.

Nevertheless, I never had to do this until now, because the speed of make was always acceptable, which might be due to the fact that I have always used fast CPUs with generous quantities of installed memory and with fast SSDs.

Moreover, I usually think a lot before making changes and then I do all of them, so for me it happens very infrequently to need to recompile a single file many times in a row.

In conclusion, I agree with you that there is a use case for ninja, for very large projects where frequent recompilations of only a few files are needed.

Nevertheless, there are a lot of software developers who will never encounter this use case, so for them make is enough.

On the other hand for cmake I am not aware of any useful application, because all the examples that I have seen of cmake projects were not simpler than if those projects would have used make.

It is possible that I have seen only examples where cmake was not used well, but in any case, the best that cmake can hope is to be as easy to use as make.

Do you have any links to share that teach using make in the manner you're promoting?
See my reply above
The spaces may be in parent folders, such as c:\Users\Jean-Michaël Celerier\Documents\Visual Studio Projects\Project 1\...

Which your uni's group policy would make it so that you can only write there (thankfully mine was sane, but I've seen it aha).

And even then, a build system that does not support arbitrary file names is just terminally broken and should not be used, even less encouraged.

That's "only" a problem for absolute paths, most projects which use makefiles use relative paths.
Spaces are the lesser evil. Try newlines in file names. Also try having a directory that contains ":" in PATH.