4
1
Fork 0
4 Makefile
Ambrose Chua edited this page 2019-10-23 13:32:15 +00:00

Each component should have a Makefile to describe how to build, test and sometimes run the program. The Makefile should provide the following information:

  1. Required programs
  2. Important build files
  3. Build (or run if not applicable) command
  4. Test command
  5. Cleanup operations

This is so that any developer can—after installing dependencies—run, build and test the module, without consulting other documentation or the README file. This also means that the README file should not contain such information—it should all be only found in the Makefile.

Example

See backend-core for now.

Sections

We'll divide the Makefile into a few sections

Header

This is a simple header to refer back to this document. It should be the following lines, and end with two blank lines.

# See https://git.makerforce.io/beep/best-practices/wiki/Makefile

Definitions

This is where we define common variables. Each section should be seperated by a blank line, and preferrably not contain blank lines.

Programs

Any programs used by the Makefile MUST be defined here. The side effect of defining them in this section also serves as a list of all the dependencies required to run, test or build the program. This makes it reasonable to assume that if a program is used here, one can simply install the program and run make. Otherwise, comments should be made on what hidden dependencies exist, within reason. An example of such a hidden dependency could be a program that go generate might call.

#
# Programs
#
GOCMD?=go
...

Local

It is common to need variables to describe constant attributes of the local build. For example, the output filename or folder. These should be declared in this section.

#
# Local
#
BINARY_NAME=core

Files

The Makefile might need to reference special files used in the build, or maybe a entrypoint to be compiled. This can be defined in this section. A common example would be integration test docker-compose files.

#
# Files
#
DOCKERCOMPOSE_INTEGRATION_CONFIG?=docker-compose.integration.yml

Tasks

This section is where we define all the tasks. It should start with the following header, surrounded by two blank lines.

#
# Tasks
#

all

The default task should be a quick way to test and compile the program. This might involve a unit test and build subtasks.

# Let's do a quick unit test and then build backend-core
all: test_fmt test_unit build

build

Should not mutate any code or do any tests.

build:
	$(GOBUILD) -o $(BINARY_NAME) -v

deps

If the build requires dependencies that can be installed purely using the toolchain, you can call it from here. Make sure it does not affect the host system.

deps:
    $(FLUTTER) pub get

test

This task should perform all tests that can be done, including integration and UI testing.

test: test_fmt test_unit test_integration

test_fmt

This is neat, it should test that the code follows convention formatting.

test_fmt:
    $(GOFMT_PROG) -l .

test_unit

Don't depend on any running process.

test_unit:
    $(GOTEST) -tags=unit -v -cover

test_integration

This can spawn docker-compose setups using test_integration_prepare followed by performing the test.

test_integration: test_integration_prepare
    $(GOTEST) -tags=integration -v -cover

test_integration_prepare, test_integration_cleanup

Note that the cleanup step must be manually called to clean up. This enables us to reuse the database. Tests should try their best to clean up after themselves.

This also means that the preparation step should only create one instance of any services, and the cleanup step must be manually run to shut that service down. A good example is show below. It checks for any running instances (including non-docker instances) and brings up the services required if it's not running.

test_integration_prepare:
	$(GORUN) scripts/testutils.go isrunning || $(DOCKERCOMPOSE) -f $(DOCKERCOMPOSE_INTEGRATION_CONFIG) up -d
	$(GORUN) scripts/testutils.go wait
test_integration_cleanup:
	$(DOCKERCOMPOSE) -f $(DOCKERCOMPOSE_INTEGRATION_CONFIG) down

clean

Note that this is not a cleanup and should not call cleanup. It should be in charge of removing any compiled assets or build cache.

clean:
    $(GOCLEAN)
    rm -f $(BINARY_NAME)