[HN Gopher] Using Make - writing less Makefile
       ___________________________________________________________________
        
       Using Make - writing less Makefile
        
       Author : todsacerdoti
       Score  : 141 points
       Date   : 2023-12-26 18:05 UTC (4 hours ago)
        
 (HTM) web link (text.causal.agency)
 (TXT) w3m dump (text.causal.agency)
        
       | Augenstern wrote:
       | I like the man page format each post has on this website. A
       | little hard to read, but very unique.
        
         | edoardo-schnell wrote:
         | Yeah, 2023 and reflowing documents still isn't a thing. Yay
        
           | Conscat wrote:
           | I read this on mobile and it felt pretty tedious. I wonder if
           | maybe some minimal HTML could make the paragraphs reflow
           | properly while still looking like a manpage everywhere.
        
           | 082349872349872 wrote:
           | What sort of keyhole are you peering through that needs to
           | reflow 72-character text lines?
        
             | plugin-baby wrote:
             | A phone? Your comment is 3 lines for me.
        
               | 082349872349872 wrote:
               | Just checked; both my comment and TFA appear full width
               | on my phone. Bog-standard Android.
        
               | dxdm wrote:
               | Maybe other people do not use the same setup as you do.
        
               | 082349872349872 wrote:
               | That much is obvious given that a vanilla phone setup has
               | no trouble with even 90-character lines. Not my business
               | to stop anyone making things hard on themselves, though,
               | so good luck you all!
               | 
               | (do we have anyone who reads HN over a morse clacker?)
        
               | pests wrote:
               | I have vision problems, I increase zoom a lot in desktop
               | and mobile.
               | 
               | What do you have against people different than youself?
        
           | Karellen wrote:
           | Which is ironic, given that man pages do reflow on different-
           | width terminals and have done since the '90s.
        
         | esafak wrote:
         | This is supposed to be functional, not art work. Make it
         | legible; easy to digest.
         | 
         | I guess it's fitting for a tool (make) that is also unergonomic
         | by today's standards.
        
           | Augenstern wrote:
           | I wouldn't use this format myself either, but that's the fun
           | with personal websites: you can do whatever you want. There's
           | no law that says stuff needs to be functional. People who
           | dislike it will just move on.
        
           | mosselman wrote:
           | Typography isn't set. It _is_ about legibility and therefore
           | proper typography _is_ functional.
           | 
           | This site simply isn't as legible as it could be. I am not
           | suggesting it needs to be "pretty". A monospaced Markdown
           | file would be more legible.
        
       | bsdpufferfish wrote:
       | It's unfortunate that most programmers only experience with make
       | is automake/autoconf. This is fantastic.
        
         | NelsonMinar wrote:
         | Autoconf, that's the thing that spends time making sure that my
         | software can be built on BeOS and A/UX?
        
           | asveikau wrote:
           | More like some obscure pre-c89 stuff. But it takes actual
           | work and testing to get that working, so most people have a
           | configure script that checks for all that stuff but when it's
           | time to compile it only actually works on their specific
           | Linux distro.
        
           | im3w1l wrote:
           | I have a vivid memory of autoconf. I set it up best as I
           | could. Someone ran ./configure. It triggered a new run of
           | autoconf that failed because of mismatching autoconf version.
           | Why it triggered a new run? Because of mtime checks. Why did
           | they say it needed to be rerun? Because git doesn't preserve
           | mtime. I guess it's just assumed that everyone distributes
           | their sources as a tarball. I spent many hours trying to
           | figure out how to bypass those issues, and I can't remember
           | if I ever succeeded.
           | 
           | These days I just use Rust instead.
        
       | raggi wrote:
       | If you want to write less makefile, you don't need to define foo.
       | `foo` is an implicit target if foo.c is present.
       | 
       | You can use make in this form without a makefile even:
       | ~ % echo '#include <stdio.h>\nvoid main() { printf("OHAI\\n"); }'
       | > ohai.c         ~ % make CFLAGS=-DDEFINE_ME_A_MAKEFILE ohai
       | cc -DDEFINE_ME_A_MAKEFILE    ohai.c   -o ohai         ~ % ./ohai
       | OHAI
        
         | gumby wrote:
         | In that case why call make at all?
        
           | raggi wrote:
           | CC, CFLAGS and LDFLAGS conventions, and freshness checks (if
           | you do it twice, it won't build again as it performs an mtime
           | check on the source).
        
       | assbuttbuttass wrote:
       | For larger programs, the compiler can also generate the
       | dependency tree for incremental compilation:
       | https://www.gnu.org/software/make/manual/html_node/Automatic...
        
       | gumby wrote:
       | With gnu make you don't even need OBJs for a simple directory in
       | which all the .c files are compiled:
       | .SECONDEXPANSION              foo: $$(patsubst
       | %.c,%.o,$$(wildcard *.c))              $(CC) $(LDFLAGS) $^
       | $(LDLIBS) -o $@
       | 
       | Admittedly you can end up with short, very general Makefiles that
       | look like they were a Prolog program written in TECO. But the
       | advantage is that they _are_ general so don 't need to be fiddled
       | with as your project grows.
       | 
       | I make small Makefiles like this that ensure my local build
       | environment is correct for the package, then call cmake.
        
         | JadeNB wrote:
         | > a Prolog program written in TECO
         | 
         | I know we don't do general-purpose "I like this" comments on
         | HN, but, as a Christmas treat to myself, I'm just going to say
         | how happy this little sentence fragment makes me.
        
         | bigdict wrote:
         | Just do:                   .SECONDEXPANSION         foo:
         | $$(patsubst %.c,%.o,$$(wildcard *.c))
         | 
         | No need to tell Make how to invoke a C compiler for the
         | billionth time.
        
           | rurban wrote:
           | But then you need the name it GNUmakefile when you use
           | incompatible GNU extensions.
           | 
           | BSD makefiles do look better.
        
         | Brian_K_White wrote:
         | Globbing is possible but unwise. You do want a variable with an
         | explicit list of sources, even while trying to make things as
         | automatic and dynamic as possible.
        
           | adrian_b wrote:
           | Why would you want that?
           | 
           | In decades of programming in very varied environments I have
           | never compiled any software project otherwise than by using
           | globbing, so that I have never needed to waste time to write
           | the name of any source file in a Makefile.
           | 
           | I have never seen any reason to do otherwise.
           | 
           | For instance, using special compilation flags for a certain
           | source file, different from the others, is something that I
           | consider a mistake. Whichever is the goal, it certainly can
           | be achieved by other means (e.g. pragmas or attributes).
           | 
           | Moreover, the dependencies for any file must always be
           | generated automatically, they must never be written
           | explicitly, which removes the main reason why in bad
           | Makefiles the names of the source files are written
           | individually.
        
             | dataangel wrote:
             | globs don't even prevent you from special casing one file
             | to have different flags
        
               | adrian_b wrote:
               | True, but this should better be avoided, because in such
               | cases reading the source files is not enough to
               | understand what they do.
               | 
               | You must also be aware that they are compiled in a
               | different way and you must search a frequently too big
               | and obfuscated Makefile to discover which is the
               | applicable compilation rule.
               | 
               | Unfortunately I have seen many projects that had used
               | such tricks and it was always painful for maintenance to
               | discover how they were supposed to work.
        
             | an_d_rew wrote:
             | Globbing means that the directory has to be scanned on
             | every build
             | 
             | Once you start getting to multiple directories and
             | recursive scans, the build time begins to grow enormously
             | and usually unexpectedly.
             | 
             | Not a problem at all for small or even medium size projects
             | on a fast machine.
             | 
             | But once you start building very large projects with
             | multiple sub-projects, it generally slows the machine to a
             | crawl on every build.
        
               | adrian_b wrote:
               | When the project is so big that this is a problem, you
               | can make an additional make target, e.g. "make
               | file_lists" that would use globbing and store the lists
               | into a file that will be used by any other make commands.
               | 
               | The bigger a project is, the more important is to use
               | only globbing and never write explicitly any file name
               | lists.
               | 
               | However, I believe that this, the slowness of globbing
               | for big directories, might be a strictly Windows-specific
               | problem, if it exists. At least on Linux and with XFS
               | (which has B-tree directories) I have never seen globbing
               | to take any noticeable time, even on directories with ten
               | thousand files or more, where it still appears to be
               | instantaneous at the human reaction time scale.
               | 
               | There is no reason to ever do recursive scans. All the
               | directories with source files of a project must be
               | scanned to search for any source files located there, but
               | only once.
        
             | mort96 wrote:
             | When you're using git, it's easy to end up with extra
             | uncommitted files; maybe you're working on a new file, want
             | to checkout the previous version and forget to stash. If
             | you glob, the build of the old commit will include your new
             | file. If the list of files is committed into the repo
             | somehow (such as by being listed in the Makefile), your new
             | file gets ignored.
             | 
             | You might say that it's possible to end up with an
             | accidentally modified file too, and that's true. But from
             | experience, it's much easier to accidentally end up with an
             | extra uncommitted file than to accidentally end up with a
             | modified file; 'git checkout <commit>' will shout at you if
             | files in the index are modified.
        
           | dataangel wrote:
           | Globbing is how you avoid pointless busy work. If there's a
           | file in the directory that shouldn't be built, it shouldn't
           | be there.
        
             | adrian_b wrote:
             | When you want to exclude a file from compilation without
             | deleting it, it is enough to change its extension, e.g.
             | from ".c" to ".c_".
             | 
             | Alternatively, you could move such a file to a subdirectory
             | "Attic" or the like.
             | 
             | Any of these solutions would ensure that globbing will no
             | longer include that file in the list of source files,
             | without needing to update any project configuration files.
        
         | adrian_b wrote:
         | Using these and other similar features of GNU make it is
         | possible to write a generic Makefile that works for any
         | software project.
         | 
         | It appears that almost nobody reads the manual of GNU make,
         | despite the fact that it is extremely instructive.
         | 
         | I have read the GNU make manual once, about 25 years ago. Then
         | I have written a set of small Makefiles that I have used in all
         | my software projects forever, until now, with only extremely
         | small changes during the years, e.g. when new compiler options
         | have appeared, or when I have added compilers for additional
         | CPU targets or operating system targets or additional
         | programming languages.
         | 
         | It is possible to make such generic Makefiles that will work in
         | any software project.
         | 
         | For example, in the simplest case, when the source files are in
         | a single directory and you use that directory also for building
         | the project, copying a small template Makefile that includes
         | the appropriate generic Makefile for the target CPU and
         | operating system is enough so that "make" will identify all the
         | kinds of source files that exist in the directory, generate the
         | necessary dependence rules, invoke the appropriate compilers
         | and build the executable.
         | 
         | For any more complex project, only a minimum of information
         | needs to be added to the template Makefile. For instance, when
         | the build is done in another directory than the one with
         | sources, or when there are multiple directories with source
         | files, a list of directories with source files must be added
         | into the template Makefile. Lists of non-standard libraries,
         | non-standard library directories and non-standard include
         | directories may be added. Additional options can be specified,
         | e.g. building a shared library, not an executable file.
         | 
         | Besides these, nothing specific to the project needs to be
         | written. Adding or deleting source files or renaming source
         | files do not need any changes in the Makefile, but after such
         | changes a "make clean" is recommended.
         | 
         | Instead of this simple and obvious approach, more than 99% of
         | the software projects that I have seen use absolutely horrible
         | huge and unmaintainable Makefiles, or they use "make"
         | replacements like "cmake", which are much worse than the
         | traditional "make", so I fail to understand why anyone would
         | want to use them.
        
           | dagmx wrote:
           | I think you're overstating the ease of use of make.
           | 
           | How does one discover dependencies in a cross platform way
           | with Make without writing the logic themselves to stay up to
           | date with platform changes? Or picking up configuration
           | options from dependencies.
           | 
           | How does one make use of Ninja with make? Or discover changes
           | to sdk paths for new platforms?
           | 
           | You end up having to duplicate that logic across every repo
           | that needs it. Along the way you accrue lots of variance.
           | 
           | In the end you get something that is difficult to debug and
           | scale. That's why so many projects moved to cmake. It scales
           | better. In much the same way that you can technically do
           | anything you need with C, but other languages add ergonomics
           | and scalability that people value more.
        
             | adrian_b wrote:
             | Discovering the dependencies depends on the compilers, not
             | on the platform.
             | 
             | I have stopped using MSVC many years ago, so I do not know
             | how it handles dependencies, but with gcc or clang that
             | works regardless of platform, with compiler-specific
             | options.
             | 
             | Moreover, software for one platform can be built on another
             | platform. What matters is only the platform used for
             | building, where the compilers are hosted, not the target
             | platform for the built software.
             | 
             | In general, I extract all platform specific definitions,
             | like the names of the compilers and their command-line
             | options in platform-specific Makefiles, which include a
             | generic Makefile with platform-independent definitions,
             | rules and make targets. Any platform-specific details, like
             | SDK paths, are encapsulated in the definitions of certain
             | "make" variables.
             | 
             | While I have not needed to update the platform-independent
             | generic Makefile for decades, the platform-dependent
             | sections need updates from time to time, but that is not
             | done for every software project, but perhaps once per year
             | or more seldom, when significant new compiler versions,
             | standard libraries or other new software tools become
             | available.
             | 
             | I have not seen yet any reason for using Ninja. Perhaps it
             | may be faster when building something like chrome, but
             | because chrome is likely to be the slowest-compiling
             | software project known to mankind, it is hard to tell how
             | much is gained by Ninja. For software projects of more
             | typical sizes I have not seen noticeable advantages of
             | Ninja. Traditional make can keep all the cores of a CPU
             | 100% busy, so there is no way any other building system can
             | be appreciably faster. There are some very bad Makefiles
             | that are intrinsically slow, but that is a problem of those
             | Makefiles, not of "make" in general.
             | 
             | Using a minimum project-specific Makefile section can only
             | simplify debugging, not making it difficult.
             | 
             | I have never seen any evidence for the claim that cmake
             | scales better. On the contrary, at least in all the open-
             | source software projects that I have ever seen cmake is a
             | major source of bugs in the building process that nobody
             | knows how to fix. (i.e. after some software updates cmake
             | fails to find files that exist, presumably due to some
             | errors in the cmakelists that appear to be arcane enough
             | that they are hard to find) I have never seen in projects
             | that use traditional make such errors that are so frequent
             | in all the projects that use cmake.
             | 
             | I have a lot of experience in building software projects,
             | because for many decades, with the exception of a few
             | professional programs without alternatives, I have been
             | using only programs that I compile from sources. Most
             | projects have very bad Makefiles, which are very hard to
             | maintain, i.e. to change when files are added, deleted,
             | moved or renamed, but at least they build the software
             | projects reliably. Whenever cmake is used, building a new
             | version is always an adventure with unpredictable outcome.
        
               | dagmx wrote:
               | I should rephrase, by dependencies I mean third party
               | dependencies. That is on the build system not the
               | compiler.
               | 
               | And make tends to fall significantly behind Ninja in my
               | experience for cold builds. Here's a post from someone
               | else that echoes my experience
               | https://david.rothlis.net/ninja-benchmark/ but for a
               | sufficiently complex project even a well tuned make build
               | is about 20% slower on CI, which is about 10-20m per
               | build.
               | 
               | It also seems like you're giving make the benefit of the
               | doubt when it comes to "most being badly written but my
               | own is great", while simultaneously deriding cmake
               | without giving it the same benefit.
               | 
               | But to your larger point, and going back to my C vs other
               | languages comparison (let's pick C++ for arguments sake).
               | Yes Make can do it all, but even by your own statements
               | it requires doing everything correctly and there's
               | significant room for error. CMake makes the trade off of
               | abstraction for a more consistent experience with minimal
               | work.
               | 
               | This is similar to C and C++, where yes C can (1) do much
               | better in the very very specific right hands, but (2) it
               | can also become an unwieldy mess. C++ with RAII may seem
               | perplexing from the lens of only accepting the former and
               | not the latter. It lets people spend their time elsewhere
               | on the system.
        
               | t43562 wrote:
               | Ninja excludes a lot of features of make which are
               | expensive to implement and cause it to be slower to parse
               | so naturally in certain situations ninja runs faster than
               | make. If your build time is actually affected by how long
               | it takes to read the makefiles then this is important
               | (e.g. building Android).
        
               | adrian_b wrote:
               | The link provided by you shows correctly that Ninja is
               | indeed faster in the cases when almost nothing is
               | recompiled, so the command execution time is dominated by
               | the dependency evaluation time, which is faster in Ninja.
               | 
               | However, the same link shows that even for relatively
               | large projects the time difference is less than a second,
               | which matches my experience.
               | 
               | Ninja requires much more effort for using, as it is not a
               | complete build system, but just the execution component,
               | and except for projects of chrome size that effort is not
               | worthwhile for a subsecond gain in certain scenarios.
               | 
               | I cannot imagine how a "tuned" make build can be 20%
               | slower, except when the project is not really tuned, but
               | it is badly organized. A "tuned" Makefile means that the
               | execution time for "make" is negligible in comparison
               | with the execution times of the compilers and linkers.
               | The time spent in "make" should be measured in seconds an
               | most, never in minutes.
               | 
               | Only a very slow file system can cause "make" to be much
               | slower than Ninja, because the dependencies for "make"
               | are stored in many ".d" files, equal in number with the
               | source files. On modern SSDs or RAM disks, that is never
               | a problem.
               | 
               | Perhaps you are right about CMake. I cannot be certain
               | whether CMake is good or bad, because I have never
               | created a CMake project myself, I have only built CMake
               | projects created by others. Nevertheless, there certainly
               | is a problem with the CMake users that I have not seen
               | yet one that can explain which are the advantages of
               | CMake. All the tutorials that I have ever seen about
               | CMake were showing how to do in a complex way things that
               | can be done in a simple way with GNU make, so I never had
               | any reason to investigate any further.
               | 
               | What I know for sure is that much fewer understand how to
               | use CMake than how to use make, because the frequency of
               | bugs in CMake projects is much greater.
               | 
               | I would like to see how CMake can provide minimal work.
               | With GNU make, creating a new very simple project needs
               | only copying a template Makefile in the source directory.
               | For a more typical software project, the template
               | Makefile must be edited to add a list of directories,
               | those that must be searched for source files (I actually
               | write the list as a prefix directory plus a list of
               | subdirectories for it, as that is how I normally
               | structure the projects).
               | 
               | For external dependencies, up to 3 lists may be added, of
               | libraries, include directories and perhaps of library
               | directories, which should suffice.
               | 
               | That is all. Nothing more. I normally build each target
               | file of a project, e.g. executable or library, in a
               | separate subdirectory of the build directory, so by
               | default it is not necessary to write it in the Makefile,
               | because the name of the subdirectory will be used for the
               | target. The root of the build directory contains a
               | Makefile that I never change and which descends in each
               | subdirectory to build its target.
               | 
               | How can be CMake more simple to use than this? All CMake
               | projects that I have ever seen were much more complicated
               | and they were very difficult to modify, with a very large
               | number of project-specific details that have no place in
               | the project configuration files, because the building
               | system should be able to deduce them.
        
             | nocombination wrote:
             | Funny to mention cmake when GNU Autotools handles all of
             | these dependency resolution with grace using only a few
             | lines of autoconf.
             | 
             | CMake script syntax is not very great, and debugging is no
             | better than sprinkling printf's throughout... So, why not
             | use Autotools? /bin/sh has been the norm for most build
             | processes, to the point where the later-designed YaML
             | syntax is _effectively_ the SAME as a Makefile with /bin/sh
             | statements running the pipeline procedures.
             | 
             | CMake came along and tried to fix complexity in Autotools
             | while adding kitchen-sink baggage along the way.
        
               | dagmx wrote:
               | I'm not specifically advocating for CMake , though it is
               | my preference for many reasons like faster uptake of
               | multiple platform and compiler features.
               | 
               | I was merely pushing back on the person perplexing on why
               | people don't just use Make.
        
           | bigdict wrote:
           | Love this, I aspire to build a similar system myself one day.
        
           | zabzonk wrote:
           | i have a couple of old blog articles that cover the basics of
           | writing a generic makefile for c++ here:
           | https://latedev.wordpress.com/2014/11/08/generic-
           | makefiles-w...
        
           | t43562 wrote:
           | In this kind of situation the templates tend to become
           | complicated stores of knowledge and choices about how to
           | build something for a particular platform and there are
           | usually some complications about maintaining them.
           | 
           | It all depends on what you want out of the system - the
           | ability to build against multiple versions of a specific
           | linux distribution or compatibility across unix or even to
           | non-posix operating systems. So the complexity exits from the
           | generic parts where you say:                 PROGRAM:= bob
           | SOURCE:=fred.c       LIBRARIES=gtk       include
           | build_program_template_$(PLATFORM).mk
           | 
           | and enters the templates for building on a specific platform
           | where you have to decide if that's gtk4 or gtk2 and how to
           | tell the program what it can/cannot do on that specific
           | platform.
           | 
           | If you're the one building the code and deciding where to do
           | it that's cool. For people who want to port your code to some
           | other platform, cmake or autoconf will help discover what
           | needs to be done on some specific plaform, check that the
           | needed features/libraries are on the platform and construct a
           | build with all the features it can support on that platform
           | turned on and the ones it cannot cope with turned off.
           | 
           | They automatically doing the job you do manually and that has
           | many uses when you're giving the build to someone who just
           | wants it to work and doesn't know the ins and outs like you
           | do.
        
         | lhamil64 wrote:
         | I admittedly don't have a ton of experience with make, but
         | what's the point of doing this vs just throwing the compile
         | commands in a shell script?
        
           | dpkirchner wrote:
           | The biggest practical difference is that make can test to see
           | if a file needs to be rebuilt before proceeding.
        
             | adrian_b wrote:
             | Yes, exactly this.
             | 
             | When compiling a project once from a clean state make has
             | no advantage over a shell script that would use a tool like
             | "parallel" to distribute compilation jobs over all
             | available CPU cores.
             | 
             | However writing such a script for parallel compilation is
             | more complex than writing a simple Makefile executed with
             | "make -j N".
             | 
             | As you say, the main reason of existence of "make" is the
             | acceleration of the recompilation of a project after
             | incremental changes in the source files, when only the
             | files that depend on the changes are recompiled, saving
             | time.
        
           | f1shy wrote:
           | Make will automatically do all the steps, and stop if any one
           | fails. Of course you van do manually, but is much more work.
        
             | dagmx wrote:
             | The irony is that the same argument would work against Make
             | in the description further up vs other build systems like
             | CMake etc.
             | 
             | Not to take away from your correct answer of course
        
           | nrclark wrote:
           | Imagine you had all your build commands in a shell-script.
           | 
           | Now imagine that you want to run individual steps from time-
           | to-time, instead of running your whole script start to
           | finish. So maybe you write functions that represent each
           | group of commands, and call the function based on an input
           | argument.
           | 
           | Now imagine that some of your build-steps have dependencies
           | on other build-steps, and you want to be able to say "run
           | step X, plus whatever else you have to do first." Or maybe
           | you want to retry a failed build from wherever it left off,
           | instead of having to re-run the entire thing.
           | 
           | Now imagine that you'd like to have some level of isolation
           | between build-steps, so that one step doesn't mess the other
           | one up.
           | 
           | As you add more and more things to your build script, you'll
           | find yourself writing an implementation of Make pretty
           | quickly.
           | 
           | Now
           | 
           | - sometimes - your build fails halfway though, and you want
           | to resume from where-ever it failed. S
        
           | t43562 wrote:
           | Make is declarative. You tell it how to make an A from a B
           | and a B from a C or a D and it then works out that it needs
           | to do "CBA" or "DBA" depending on whether it finds a D or a C
           | in the filesystem.
           | 
           | The more possible variations you have to cater for the worse
           | build scripts get.
           | 
           | If you try not to redo work that you did 5 minutes ago that's
           | still valid then build scripts become even more complicated
           | whereas the makefile doesn't change at all.
           | 
           | Other tools do declarative even more than make does but as
           | they get easier to understand they tend to lose abilities.
        
       | bigdict wrote:
       | The article reads                 I think an important thing to
       | know about make(1) is that you don't need to write a Makefile to
       | use it.  There are default rules for C, C++ and probably Fortran.
       | 
       | And then it goes against its own spirit... You don't need this:
       | OBJS = foo.o bar.o baz.o            foo: $(OBJS)           $(CC)
       | $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
       | 
       | Just do:                 foo: foo.o bar.o baz.o
       | 
       | It respects LDFLAGS and LDLIBS.
        
         | Brian_K_White wrote:
         | Lists of files are usually used more than once for more than
         | one task.
         | 
         | This would only make sense if the only rule that needed the
         | list was that one that builds, no
         | install/uninstall/package/modify/audit/hash/clean/etc, which is
         | never the case. You also don't want to use globbing in place of
         | an explicit list either.
        
           | bigdict wrote:
           | Sure, I'm just pointing out you don't need to type out the C
           | compiler invocation again.                 FOOOBJS = foo.o
           | bar.o baz.o       foo: $(FOOOBJS)
        
             | Brian_K_White wrote:
             | Ah got it. Agreed.
        
         | Karellen wrote:
         | But the OBJS allows for an easy `clean` target, defined further
         | down the article.
         | 
         | Personally, I'd have gone with                   foo: $(OBJS)
         | $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS)
         | 
         | to put all flag options first, then the output option, then
         | using `$<` for the compiler inputs, and additional linker
         | inputs last.
         | 
         | But that is somewhat idiosyncratic, and based on some half-
         | remembered ideas of second-hand stories of how compilers used
         | to process their command parameters back in the '90s (and
         | before). Those ideas probably aren't relevant now (and may not
         | have been entirely correct originally), so if the recipe in the
         | original article works for the author, I'm not going to say
         | they're "wrong" for doing it the way they did.
        
           | adrian_b wrote:
           | You do not need to write any project-specific definition for
           | OBJS.
           | 
           | You can include in the project Makefile a generic Makefile
           | that may contain a definition like:                 OBJS :=
           | $(CPP_FILES:.cpp=.o) $(CXX_FILES:.cxx=.o) $(CC_FILES:.cc=.o)
           | \        $(C_FILES:.c=.o) $(SS_FILES:.S=.o) $(S_FILES:.s=.o)
           | $(F_FILES:.f=.o) \        $(L_FILES:.l=.o) $(Y_FILES:.y=.o)
           | $(RC_FILES:.rc=.o) $(O_FILES)
           | 
           | which builds an OBJS list from all the lists of source files
           | that "make" has previously gathered from all the directories
           | with source files that belong to the project.
           | 
           | The automatically defined OBJS can be used for linking, and a
           | similar list without $(O_FILES), i.e. without .o files that
           | are source files, not intermediate files, can be used for
           | cleaning.
        
       | yegle wrote:
       | The author should mention the automatic variables:
       | https://web.mit.edu/gnu/doc/html/make_10.html#SEC94
       | 
       | Specifically, in one of the examples, to list all dependencies as
       | the input to `cc`, you can write `$^` instead of `$(OBJS)`.
        
         | djbusby wrote:
         | OBJS is a little more clear to what's happening than $^
         | 
         | I'm not a fan of magic symbols. Hard to remember, especially
         | across languages.
        
       | gosub100 wrote:
       | I say this as someone with 20+ years of experience in C and C++:
       | Using make is a huge waste of time in 2023 (and forward).
       | Learning the intricacies of make actively blocks you from Getting
       | Things Done.
       | 
       | Yeah, make is kind of neat because it has this functional
       | what_I_want : how_to_get_it syntax, but it doesn't actually work
       | that way. It's a huge waste of time to try to get it working so
       | unless you meet the following caveats, make is not for you:
       | 
       | - you love trivia like "who was the 19th president of the US?"
       | 
       | - on a road trip, you stop to look at historical markers
       | 
       | - you love steampunk/tube amplifiers/making pasta from
       | scratch/listening on vinyl/home brewing beer
       | 
       | If those sound like you, you're the type who won't regret wasting
       | a couple days getting make running and getting cut on the sharp
       | edges. Most of us who just want to get there will be better with
       | cmake, meson, or hand-rolling a script using a _modern_ language
       | (inb4  "make does more than compile code" - yeah, so does ruby or
       | python or any other script, without making false promises or
       | wasting your time)
        
         | mathematicaster wrote:
         | Lost me at "making past from scratch".
        
           | jjgreen wrote:
           | In some sense, don't we all make the past from scratch?
           | [stares into middle distance]
        
         | spicybright wrote:
         | 3 paragraphs of rambling for "make is a waste of time because
         | of sharp edges"
         | 
         | Can you actually articulate what those sharp edges are?
        
       | WesolyKubeczek wrote:
       | Make is fantastic. I'm using it to rebuild and run containers in
       | my development environment, and to build a full repository of RPM
       | packages (that my project needs and which CentOS Stream lacks). I
       | suppose that if javascript tooling was more amenable to process
       | one file at a time, I could do parallel transpiling and bundling
       | of a hefty frontend with make as well.
       | 
       | I also use its metaprogramming and late evaluation capabilities a
       | lot, heavily inspired by the tricks Buildroot is doing.
       | 
       | If you are mindful of its restrictions (it works with "words"
       | separated by spaces, thus paths with spaces in them are a problem
       | unfixable without severely breaking backward compatibility; and
       | your targets need to be expressible as files with meaningful
       | modification timestamps), it's a very powerful tool. It deserves
       | more appreciation.
        
       | amelius wrote:
       | Things get messier when you start to add automatic dependencies.
        
       | rochak wrote:
       | Gonna get downvoted to oblivion for saying this but I haven't
       | hated a tool more than make.
        
         | bxparks wrote:
         | Not downvoting you. Just curious to know why? In my experience,
         | as long as the Makefile is less than about 100 lines, it's the
         | most useful workflow system ever. Because it's available almost
         | everywhere. After about 100 lines, yeah, it is not great. And
         | that idiotic tab character. I know the historical reason why
         | tab was used. I wish they had fixed it to accept both tabs and
         | spaces a long time ago.
        
           | nickelpro wrote:
           | It's a poor, verbose, arcane solution in every problem space
           | it purports to solve.
           | 
           | It was fine in its era, but that's a long time ago. It is
           | completely unsuitable as a project management tool and a poor
           | task runner.
           | 
           | Also its ubiquity is overhyped. There's no build platform
           | where I can't download the tools I need, that's the entire
           | point of a build platform, and most such platforms come with
           | far more advanced tools pre-installed.
        
       | masspro wrote:
       | Quasi-tangent: I was once hit by a coworker doing first code
       | review on a new repo with "hmm...I'm not familiar with using
       | Makefiles as a project management tool. So something something
       | [don't remember] we should replace that." It struck me as weird
       | because I don't see `make` as a build tool so much as automating
       | shell script snippets you would/could type at the command line.
       | From that POV I conceptually see Makefiles as like a `bin/`
       | folder full of little scripts, honestly the main advantage for me
       | being every command runs with working dir being where the
       | Makefile is; no need to write a `set -euo pipefail;
       | HERE="$(dirname "$0")"` dingleberry at the top of every
       | hypothetical `bin/` script. And having incremental rebuild is
       | sugar on top when it's possible to write :)
       | 
       | I don't see `make` as a build tool because I believe from
       | experience that is where the road to hell begins. But it _is_
       | convenient to have `make` call CMake /Cargo/pip/whatever. Plain
       | `make` can build, `make test` can test, `make fmt` can auto-
       | format... Even better if you alias `m` to `make` in your shell.
        
         | gregwebs wrote:
         | Your coworker's experience is more principled: Make is a
         | mediocre tool for executing commands. It wasn't ever designed
         | for that. Although it is pretty common to see what you are
         | mentioning in projects because it doesn't require installing a
         | dependency.
         | 
         | For a repo where an easy to install (single binary) dependency
         | is a non-issue, consider using just. [1] You get `just -l`
         | where you can see all the command available, the ability to use
         | different languages, and overall simpler command writing.
         | 
         | [1] https://github.com/casey/just
        
           | MrOxiMoron wrote:
           | just is great, I add it everywhere, just test, just run, just
           | fix, just shell. just works ;-)
        
           | masspro wrote:
           | This looks really nice. But then should `j` alias to `just`
           | or to `jobs`? Non-starter. /s
        
           | sime2009 wrote:
           | A similar tool is `task` https://taskfile.dev/ . It is quite
           | capable and also a single executable. I've grown to quite
           | like it.
        
             | wirrbel wrote:
             | I prefer task over just, while I am not a huge fan of YAML,
             | we now use it everywhere so it just makes sense to not
             | learn yet another DSL for Just and just use YAML.
        
               | bravura wrote:
               | How do task and just compare to SCons?
               | 
               | Although SCons is Python (which is a pro or con depending
               | upon your perspective), it has strong dependency
               | management. Or is the argument that dependency management
               | is part of build, not general project maintenance?
        
           | hathawsh wrote:
           | With all due respect, I don't understand the first part of
           | your comment. Make's core purpose is to execute commands,
           | isn't it? How was it not designed to execute commands?
        
             | nine_k wrote:
             | No, no. Make's principal purpose is to put a set of files
             | into a desired state. It can "make" a particular file by
             | invoking a dependent graph of commands that produce that
             | file from other files. It checks timestamps and only run
             | steps where the resulting files are older than some of the
             | (transitive) source files.
             | 
             | You can invoke it by naming a named rule, not a file, but
             | the logic will remain.
             | 
             | If this is not what you want to be doing, and if your
             | Makefile is full of .PHONY targets, you likely need a
             | Justfile instead, or (worse) plain shell scripts.
        
             | alwillis wrote:
             | > Make's core purpose is to execute commands, isn't it? How
             | was it not designed to execute commands?
             | 
             | Some people on HN believe that if you use make _primarily_
             | as a task runner that you 're doing it wrong and should use
             | something else. I don't agree.
             | 
             | Most users are using multiple aspects of make: as a task
             | runner and as something that handles the dependency graph
             | when building something digital.
             | 
             | As a web developer, I used npm scripts, grunt, gulp, etc.
             | to manage web builds but now I only use make and have
             | gotten off the merry-go-round of web build tools.
        
           | mort96 wrote:
           | Honestly Make is a pretty decent tool for executing shell
           | snippets plus some lacklustre dependency resolution stuff on
           | top.
        
         | hathawsh wrote:
         | I really like GNU Make because it has a hidden superpower: the
         | "-j" parameter enables instant, easy parallelization. I have a
         | project with many subprojects and I use "-j16" to invoke the
         | same command in all of the subprojects, 16 at a time. It saves
         | a lot of time and it works for all commands that don't touch
         | other subprojects.
         | 
         | Like you, I use Make as a front end to lower level build tools.
         | It seems to fit that role well.
        
         | t43562 wrote:
         | Make is declarative - that's roughly the point of it. You give
         | it rules and dependencies and variables and it's supposed to
         | work out what to do.
         | 
         | If your build is extremely simple and fast then it cannot add
         | value.
        
       | datadeft wrote:
       | Is there any reason to use make over ninja and gn?
        
         | baq wrote:
         | Yeah, if you have no effing idea what ninja or gn are
        
         | spicybright wrote:
         | I'm a seasoned developer for the past 15-ish years and I
         | haven't heard of ninja or gn before.
         | 
         | I tried typing them in my command line (macos 12.4) and I would
         | need to install them to use them.
         | 
         | I was taught make in CS101 in college and the majority of other
         | people's projects I've looked at use it.
         | 
         | So the advantage is ubiquity and familiarity. Which can be
         | useful if your code base is going to have a large number of
         | people working on it.
        
           | datadeft wrote:
           | Ninja was super easy to pick up even after using make for
           | some time (10+ years). GN is just a ninja generator that is
           | optional.
           | 
           | https://gn.googlesource.com/gn/+/main/docs/quick_start.md
           | 
           | https://ninja-build.org/
        
           | nickelpro wrote:
           | > I'm a seasoned developer for the past 15-ish years and I
           | haven't heard of ninja or gn before
           | 
           | Then you also stopped learning about developments in your
           | field 15 years ago.
           | 
           | > I tried typing them in my command line (macos 12.4) and I
           | would need to install them to use them.
           | 
           | Ok? And? We don't pre-installed dev tools on consumer
           | operating systems. You won't find valgrind or vcpkg either.
           | 
           | > I was taught make in CS101 in college
           | 
           | The tools we taught to beginners over a decade ago are
           | perhaps not suitable in all cases, in fact they are suitable
           | in very few cases. Not much Pascal usage either anymore.
           | 
           | > the majority of other people's projects I've looked at use
           | it.
           | 
           | Then you live in a very tiny bubble.
        
       | markhahn wrote:
       | nice: friendly but also succinct.
       | 
       | how many people are using systems like cmake (or worse) who would
       | be fine with just make (used right)?
        
       | 01100011 wrote:
       | Nice to see some love for make.
       | 
       | Sure, make syntax sucks. It's arcane and hard to google much of
       | what is going on. (Shower thought... a tool that lets you view a
       | Makefile, giving explanatory tooltips for syntax elements would
       | be great).
       | 
       | But make is widespread, ancient and eternal. It isn't going
       | anywhere, despite decades of new tools trying to take its place.
       | It does many jobs relatively well.
       | 
       | I'd go so far as to say that the platonic ideal of development
       | is:
       | 
       | make # Build the project
       | 
       | make install # Install the project
       | 
       | make clean # Clean the project build dir(s)
       | 
       | and whatever new tool you use to actually build things should
       | live behind make. You should have a _really_ good reason to
       | deviate from this. It 's approachable and familiar to most
       | devs(well, ok, older C/C++ devs). Sure, write your own custom
       | tool that builds everything in a chroot jail or docker container,
       | has sub tools for installing, running tests, etc... but please,
       | put it behind a make frontend.
        
         | ParetoOptimal wrote:
         | > the platonic ideal of development is:
         | 
         | > make # Build the project
         | 
         | > make install # Install the project
         | 
         | > make clean # Clean the project build dir(s)
         | 
         | IMO, that last step of cleaning is very not platonic ideal.
         | However... maybe a reality of incremental development?
         | 
         | The first step for me in a lot of cases is just trying a
         | project to see if it's worth my time, typically that would just
         | have been:
         | 
         | cd /tmp git clone http://foo/some/project.git cd project #
         | build steps, perhaps in a good case your make example above, a
         | dockerfile, or nix flake
         | 
         | These days though I'm ecstatic when I see a Nix flake because I
         | can reduce that to:                   nix run
         | github:some/project
         | 
         | Then for development I could even avoid cloning things myself
         | (and sometimes do) with:                   nix develop
         | github:some/project         cd /tmp         unpackPhase
         | 
         | Make some change:                   genericBuild
         | 
         | I guesss all this to say my platonic ideal includes getting
         | more busy work out of the way and at least in the case of
         | building, not having to worry about cache affecting
         | reproducibility.
         | 
         | I guess all of the above is to try and express my different
         | idea of platonic ideal here and how Nix gives it to me (and
         | could perhaps to you, for a cost).
        
         | hawski wrote:
         | For make there is much better option than googling:
         | https://www.gnu.org/software/make/manual/make.html and hit
         | Ctrl+F. You can even easily search for things like $<. Nothing
         | else is really needed. And for BSD make the man page is all you
         | need.
        
       | andreynering wrote:
       | If you're looking to an alternative, you could take a look at
       | Task:
       | 
       | https://taskfile.dev/ https://github.com/go-task/task
        
         | Arcuru wrote:
         | +1 to this recommendation. Everyone likes to point to 'just' as
         | a makefile replacement, but having tried both I slightly prefer
         | Task.
        
       | ataylor284_ wrote:
       | Make's killer feature was conditionally rebuilding based on
       | changed dependencies. Back in the day, it was easy for a medium
       | to largish software project to take many hours, or even days to
       | fully rebuild. C and C++ were especially bad, especially "every
       | file transitively includes every header" type projects. Make
       | saved a lot of that pain, even if linking still sucked.
       | 
       | I love make and still spin up a minimalist Makefile like in the
       | article regularly for tiny projects, but I'd hate to have to
       | actually maintain one for a real world project.
        
         | ataylor284_ wrote:
         | You could do something like this to get dependencies almost for
         | free:                   .depends: $(SRCS)             $(CC)
         | $(CFLAGS) -MM $(SRCS) -o .depends              -include
         | .depends
        
       | hgs3 wrote:
       | You can use Autotools to generate your Makefiles. Autotools gets
       | a bad rep, but I've found it perfectly fine for simple projects.
       | To get started you only need two files, configure.ac and
       | Makefile.am, you can ignore all the autogenerated stuff.
        
       | bb88 wrote:
       | This page just makes me miss manpages in general.
       | 
       | Not every manpage was great, but many were, and many could also
       | be formatted, printed out, and placed into a binder for easier
       | reading.
       | 
       | What I enjoyed most about manpages is that the people that cared
       | to write good documentation on things like bash.
       | 
       | If you have groff installed you can:                   man -Tpdf
       | man >man.pdf
       | 
       | The only problem with groff is that it doesn't support system
       | standard fonts.
        
         | enriquto wrote:
         | Why do you speak in the past tense? Man pages are alive and
         | well, and we use them every day.
        
       | Tyr42 wrote:
       | I honestly wish that make's default rules were available to
       | import and view the source of, but we're disabled by default.
       | 
       | Then a beginner could see what happens in a straightforward way,
       | and then import the C rules. And we could all stop importing the
       | VCS rules which aren't useful anymore.
        
       | regurfvjtdf wrote:
       | There's so much of it in this thread I couldn't possibly reply to
       | all of it, but wow. Lot of make/shell wankery going on. No one is
       | impressed with your pipe.
        
       ___________________________________________________________________
       (page generated 2023-12-26 23:00 UTC)