# A Makefile to make some Cheese

Excuse the pun in the title, the project this is for is called "Cheddar" (a type of cheese).

Github

I have recently been writing a language, in which I was using grunt. Grunt was very slow for me, and had a lot more problems for me, so I switched my tasks over to . Most of my makefile knowledge is based off examples, googling, and brief tutorials, which is why I want to ask here on how it could be improved.

PREFIX=./node_modules/.bin
## Compiler
# The compiler to use for the ES7 -> ES5 transformation
JC=$(PREFIX)/babel # Location of files SRC=src/ OUT=dist/ # The flags to pass to the compiler JCFLAGS=$(SRC) -d $(OUT) # The binary files to copy BIN=cli/cheddar EXE=cheddar BIN_MAKE=$(foreach BIN_FILE,$(BIN),chmod 755$(SRC)$(BIN_FILE) && cp$(SRC)$(BIN_FILE)$(OUT)$(BIN_FILE)${\n});\
ln -s $(OUT)cli/$(EXE) ./$(EXE) ## Tests TESTRUNNER=$(PREFIX)/babel-node
COVERAGE=$(PREFIX)/babel-istanbul TEST=$(PREFIX)/_mocha
TFLAGS=cover $(TEST) ## Rules all: default # The default task # The **production** build default:$(JC) $(SRC)$(JC) $(JCFLAGS) --minified$(BIN_MAKE)
# This builds and includes source maps
build: $(JC)$(SRC)
$(JC)$(JCFLAGS) --sourceMaps="inline"
$(BIN_MAKE) # Runs install with default method install: ./bin/install ./bin/install --method=path # Performs testing, including coverage # At the moment uses mocha for testing # and babel-istanbul for coverage test:$(TESTRUNNER) $(COVERAGE)$(TEST)
$(TESTRUNNER)$(COVERAGE) \$(TFLAGS)
clean:
rm -rf ./dist/
.PHONY: test


Essentially what this does is it defined a couple of tasks:

• default: This is the default task. It runs babel, a compiler I need to compile the code from the src/ directory to the output dist/ directory. It passes the --minified flag because this is a production build so we don't want big unminified code. Following is BIN_MAKE. This runs code to chmod 755 a list of binary files stored in the BIN variable. It then moves these files from the source dir src/ to the destination/output dir dist/.

Lastly, it symlinks the executable to start the program from deep within the dist/ directory to the root for easy-access.

• build:: This is a development build. It compiled the same as above, but instead of --minify passes a --sourceMap arg to create source maps
• install: This runs an install script to install the application into the user's computer. The script's contents is located in an external file.
• test: This runs a file to simply test the application. If you know Mocha or Istanbul, this just runs that. _mocha and babel-istanbul are in variables as they are located in ./node_modules/.bin/.
• clean: This removes the dist/ dir which is the location of the output of the build.
• Do you have some superfluous indentation before .PHONY? – 200_success Jun 22 '16 at 6:26
• @200_success Yes, it appears I do :/ The tutorial I was looking at had it. Must of been a typo... – Downgoat Jun 22 '16 at 6:27

• In makefiles you can have spacing around equals signs, making the code slightly easier to read. For example PREFIX = ./node_modules/.bin.
• UPPER_CASE variables are generally used for things that the end user might want to change when running, while lower_case is for internal use variables. The user is presumably not supposed to change for example the source directory, so SRC should really be src.
• Single-use internal variables like BIN make the code harder to read for no tangible benefit. Introduce a variable when you need to reuse the value.
• Any target which has a name that doesn't correspond to an actual file created by that target should IMO be marked as .PHONY (because it is).
• BIN_MAKE would be much easier to understand as either commands in a target or as a separate script.
• The default target of a Makefile is the first one, by definition. That makes it redundant to have two phony targets (all and default) to do the default thing.
• There should be an end user variable for each command you run that's assumed to be on the PATH, for example CP = /bin/cp. This may be considered overkill, especially for common tools, but it allows people who have tools off the PATH (for example after building it themselves) to run your Makefile without modifying it. A common use case (just like with RVM or Virtualenv) is if you want to test your Makefile with a tool version not installed on your base system.
• RM is an implicit variable at least in GNU Make for just this purpose.