[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)