Hacker News new | comments | show | ask | jobs | submit login

So he's saying use 'make' instead of gulp/grunt and then he submits an example where it's as easy as piping through GCC.

He's making the wrong assumption that you don't need to setup a build environment when building with make, but to have gcc you will still also need to install g++ and build-tools. Also, he refers to building on Windows using yet another specialist tool, but have you recently tried building anything C++ on Windows when you don't have visual studio, or even worse, CYGWIN installed?

When you make such a statement, then please show me a makefile that's not 10.000 lines long, that will do the same as this, but without NPM and 'downloading half of the internet'.

   gulp.task('scripts', function() {
      // Minify and copy all JavaScript (except vendor scripts)
     return gulp.src(paths.scripts)
I submit that's impossible, simply because this stuff took 2 years to evolve (for the Javascript toolchain, that is) and a lot of people went through hours of frustration trying out alternative methods.

In GNU make, this might look something like:

    %.coffee : %.js
            js2coffee $< > $@

    %.ugly : %.coffee
            uglifyjs $< > $@

    build/js/all.min.js : $(UGLY)
            cat $^ > $@
I'm not that familiar with grunt. Can it do the equivalent of make -j4?

PS. I'll happily agree that collecting the list of script files to operate on -- and the list of script files after the transformation (eg $(UGLY)) -- is slightly annoying in make.

I'm not familiar with the -j4 argument. (something with debug info?)

Also, what your example looks like voodoo. Could you explain what the parameters do?

On top of that, tools like uglifyjs still run on NPM/Node afaik.

-j4 says use up to 4 processes in parallel. Make has a jobserver that does parallelism safely with respect to the dependency graph. So for example, even if we have workers to spare, it won't try to build all.min.js until all of the .ugly files have finished building.

The %.foo : %.bar things are pattern rules. [1]

The $<, $@, and $^ things are called automatic variables. [2] They correspond to the first input file for a rule, the target file for a rule, and the complete list of input files for a rule, respectively. Cryptic at first but really handy.

There's a standalone script for UglifyJS. [3]

[1] https://www.gnu.org/software/make/manual/html_node/Pattern-M...

[2] https://www.gnu.org/software/make/manual/html_node/Automatic...

[3] https://github.com/mishoo/UglifyJS2/blob/master/bin/uglifyjs

This is Make 001: $@ is your output file, $^ are your inputs (prerequisites, technically), and $< is your first input file. They're unfortunately ungoogleable, but just remember that they're called "Automatic variables" in the GNU make documentation.

"-j4" means to run up to 4 jobs in parallel. It's orthogonal to the issues at hand.


The manual is actually pretty good. I've only started to dig my teeth into make.

> this stuff took 2 years to evolve and a lot of people went through hours of frustration

GMake was first released in 1977: http://en.wikipedia.org/wiki/Make_(software)

They've worked on this thing for decades

There are 42,000 issues filed for make. if you could resolve each of these issues in 10 minutes, you'd spend 291 days of frustration. http://savannah.gnu.org/bugs/?group=make

kids these days.

_make_ was first released in 1977, but that was the PWB/UNIX version. The GNU variant of make didn't come along until sometime in the 1980's. It's hard to pin down the exact date of the first release because the developers didn't keep good records prior to the switch from RCS to CVS. However, the earliest ChangeLog entry now is dated July 15, 1988, and 1985 is the earliest date mentioned in any copyright statement in any of the GNU make source files.

My point was referring to the JS toolchain here mostly. I get that for C, make is the tool for the job.

make is a general-purpose tool for describing dependencies for regenerating files. It would be worth your while to learn make, and try it on your example. Understand that it's a declarative language ("A depends on B"; when "B" changes, here's how to update "A"), and not a scripting tool. This is a good thing.

In my experience, make is coupled to Unix. Make is not coupled to C.

On one hand, make typically comes with built-in rules for .c targets.

On the other hand, make can't cleanly handle #include dependency detection. I doubt that there is any major C project where "make extraclean" (or its equivalent) isn't occasionally necessary.

So yeah it's really not very well suited for C.

Well, #include resolution would require that make be able to parse C, which would add a heck of a lot of complexity, and be unscalable. For instance, you'd need to modify make to parse CSS to teach it about @import, or to parse javascript to teach it about require() (but only if you're using RequireJS)

Or, you could use the C preprocessor option "-M" and its variants[0] to get it to generate make rules with C #include resolution for you.

See also Recursive Make Considered Harmful[1] for a good description on how to set up this in combination with GNU make's "include" facility to autogenerate your per-source #include resolution fragments.

[0] http://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html

[1] http://aegis.sourceforge.net/auug97.pdf

What is wrong wrong with just using the following.

Given it would be nice if make had a builtin macro to do this, but it is not too bad to type out.

depend: .depend

.depend: ${SRC} ${CC} -MM -I. ${SRC} > .depend.X && mv .depend.X .depend

include .depend

Makefile: .depend

Because now make can't build a clean source tree.

I believe make parses the entire Makefile before running it.

GNU make restarts when a Makefile: dependency changes. So it works perfectly fine. Try it ...

What happens if you list depend as a dependency of the first target?

In my experience, make is coupled to Unix. Make is not coupled to C.

However, implementations typically include magic that is heavily biased toward building C and C++ code. As someone who works on a lot of projects, some using those languages and some not, I tend to think it's rather too magical at times. Personally, I'd prefer to have that kind of magic explicitly stated in some standard file that comes with the tool, so that file can be included with a one-liner for those projects that want it but there is nothing implicit going on by default.

I think "magic" is much too loaded a way to characterize it.

The rule applicable to C pertains to building foo.o from foo.c. It amounts to two lines in a Makefile:

  %.o: %.c
          $(COMPILE.c) $(OUTPUT_OPTION) $<
where COMPILE.c and the other variable is predefined to trivial values.

There are other rules useful for C++, and for yacc, etc., but they are just as simple.

You can cancel all the implicit rules with "-r", or cancel selected implicit rules by naming them in the Makefile like this:

  %.o : %.c ;
All this is in the make manual, which is quite good: https://www.gnu.org/software/make/manual/html_node/Catalogue...

please show me a makefile [...] that will do the same as this

You mean like this?

    scripts: ${SCRIPTS}
            cat $^ | coffee -sc | uglifyjs -cm > build/js/all.min.js
Have you ever wished there was one-character symbol for the word "pipe", like maybe '|'? Such a symbol would even make all those .'s and ()'s redundant. While we're at it, if our build script is going to be in its own specially-named file, wouldn't it be nice if instead of namespacing under 'gulp', within the special build script file there was a DSL where you could specify the task name and its dependencies with a single character, like ':'? And instead of 'function() { return ...; }', your instructions were delimited with just indentation, like a Tab character?

Your example proves the opposite of the point you're trying to make. Starting from your example and trying to compress it with a DSL, you literally couldn't do better than Make syntax: the "gulp.task('" part is implicit, the "', function() { return gulp.src(" part is a single character (':'), every ").pipe(" is a single character ('|'), and ").pipe(gulp.dest('...'))" is a single character ('>').

I submit that's impossible, simply because this stuff took 2 years to evolve...

Before and during the entirety of those 2 years Make has been a better tool, for those of us JS coders who didn't dismiss it offhand as being always thousands of lines and only for dinosaur C coders.

Make has many problems, but taking thousands of lines to simply pipe together commands has never been one of them. Having to write .pipe() where in shell you could just do '|' has never been one of them, either.

Quite the contrary: I've had to maintain or incorporate some non-standard build processes into several build systems (mostly scons, waf, gyp), and in each one it was difficult or impossible to express what I wanted because the build system wasn't as general as Make.

The single most valuable thing Make has going for it is that the primitive is a Unix pipeline. Anything that can be built with tools you can invoke from your shell can be built with Make, and the language of action is about as universal as it gets for Unix-like systems.

Yes, the dependency syntax is somewhat confusing, and it's not obvious how to understand and debug Makefiles at first glance, but the GNU make documentation is decent, and time spent learning the language and the tools (e.g., "make -d") is much better than trying to reinvent the system, especially without understanding it. Every reinvention I've had the displeasure of working with missed some important (if not widespread) use case.

I think he's saying that re-implementing make in ruby is a bad idea.

Not that I agree with him. Being able to do some printf debugging (or even use a real debugger!) to troubleshoot issues is a big plus that he doesn't mention.

Indeed. Responding to the fetishization of web technologies by fetishizing Unix utilities is missing the point. They're both tremendously important and tremendously useful, but trying to ignore their shortcomings doesn't help anything.

On Windows I build using TDM-GCC (MinGW), and SCons. No MSVC needed.

    $ cat Makefile
      brunch build --production

      brunch build
      karma start

Applications are open for YC Summer 2018

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact