Make was created in 1976 by Stuart Feldman at Bell Labs to help build C programs. But how can this 40+ year old piece of software help us develop and maintain our ever-growing amount of cloud-based microservices?
30. $ ls cf-templates/
EC2Machine.yaml
MonitoringRoleCreation.yaml
$ ls
README.md
cf-templates/
cloudtrail_crawler.service
event_rules/
inventory_crawler.service
inventory_crawler.timer
inventory_rules/
resourcemanagement/
setup.py
tests/
tox.ini
How do you deploy that?
32. How do you test that?
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
33. How do you test that?
composer install
composer test
npm ci
npm test
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
34. How do you build that?
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
35. DIST_DIR=$(pwd)/dist
mkdir -p "${DIST_DIR}"
composer install --no-interaction --no-dev --optimize-autoloader --profile
npm ci
npm run build
# custom commands
S_CONSOLE="php -d memory_limit=2048M ./app/${APP_NAME}/console"
${S_CONSOLE} cache:clear --no-warmup --env=prod --no-debug
${S_CONSOLE} cache:warmup --env=prod --no-debug
${S_CONSOLE} assets:install --env=prod --no-debug ./web/"${RELEASE_VERSION}$
{RELEASE_SUBVERSION}"
${S_CONSOLE} company:asset-dump --env=prod --no-debug
${S_CONSOLE} company:container-dump --env=prod --no-debug
# remove stuff we don't want in our packages'
rm web/*_dev.php web/*_integration.php web/*_jenkins.php
rm -rf node_modules test-js test test-trupi
# package PHP
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf
"${DIST_DIR}/${PACKAGE_ID}.tar.gz" *
# package PHP cache
pushd /var/www/prod
find ${PACKAGE_ID} -type d -exec chmod 777 {} +
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf
"${DIST_DIR}/${PACKAGE_ID}.cache.tar.gz" ${PACKAGE_ID}
popd
# package assets
cd ./web
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf
“${DIST_DIR}/${PACKAGE_ID}.assets.tar.gz" v*
How do you build that?
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
36. DIST_DIR=$(pwd)/dist
mkdir -p "${DIST_DIR}"
composer install --no-interaction --no-dev --optimize-autoloader --profile
npm ci
npm run build
# custom commands
S_CONSOLE="php -d memory_limit=2048M ./app/${APP_NAME}/console"
${S_CONSOLE} cache:clear --no-warmup --env=prod --no-debug
${S_CONSOLE} cache:warmup --env=prod --no-debug
${S_CONSOLE} assets:install --env=prod --no-debug ./web/"${RELEASE_VERSION}$
{RELEASE_SUBVERSION}"
${S_CONSOLE} company:asset-dump --env=prod --no-debug
${S_CONSOLE} company:container-dump --env=prod --no-debug
# remove stuff we don't want in our packages'
rm web/*_dev.php web/*_integration.php web/*_jenkins.php
rm -rf node_modules test-js test test-trupi
# package PHP
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf
"${DIST_DIR}/${PACKAGE_ID}.tar.gz" *
# package PHP cache
pushd /var/www/prod
find ${PACKAGE_ID} -type d -exec chmod 777 {} +
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf
"${DIST_DIR}/${PACKAGE_ID}.cache.tar.gz" ${PACKAGE_ID}
popd
# package assets
cd ./web
tar --exclude=".svn" --exclude=".git" --exclude=".tar.gz" -czf
“${DIST_DIR}/${PACKAGE_ID}.assets.tar.gz" v*
How do you build that?
Obviously 🙄
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
37. How do you deploy that?
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
38. How do you deploy that?
Good luck with that 🍀
$ ls
CHANGELOG.md
Dockerfile
Patches/
README.md
app/
bin/
composer.json
composer.lock
package-lock.json
package.json
php72-compatibility-fix.patch
postcss.config.js
sonar-project.properties
src/
src-js/
test/
test-trupi/
web/
webpack.config.js
69. #!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install
--no-interaction
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz
web/
vendor/
70. #!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install
--no-interaction
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz
web/
vendor/
a bit verbose
71. not simple to just run the tests
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install
--no-interaction
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz
web/
vendor/
72. re-installs all npm packages
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install
--no-interaction
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz
web/
vendor/
73. more and more cases
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install
--no-interaction
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz
web/
vendor/
74. more and more scripts
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install
--no-interaction
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz
web/
vendor/
75. good luck 🍀
#!/usr/bin/env bash
set -ex
if [ -n "$1" ]; then
echo "PACKAGE_ID missing"
return 1
else
export PACKAGE_ID="$1"
fi
composer install
--no-interaction
--no-dev
composer test
npm ci
npm run build
npm test
tar czf dist/"$PACKAGE_ID".tar.gz
web/
vendor/
89. test: dependencies
composer test
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies:
composer install --no-interaction --no-dev
npm install
.PHONY: test build dependencies
90. test: dependencies
composer test
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies:
composer install --no-interaction --no-dev
npm install
.PHONY: test build dependencies
test: dependencies
composer test
npm test
91. test: dependencies
composer test
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies:
composer install --no-interaction --no-dev
npm install
.PHONY: test build dependencies
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
92. test: dependencies
composer test
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies:
composer install --no-interaction --no-dev
npm install
.PHONY: test build dependencies
dependencies:
composer install --no-interaction --no-dev
npm install
93. test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
94. test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
95. test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
# alias for the actual Artifact
build: dist/my_project.tar.gz
dist/my_project.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/my_project.tar.gz web/ vendor/
96. test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
97. test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
$ make build PACKAGE_ID=my_package-1.2.9
98. test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
$ make build
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
99. test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
100. test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
# guard against being called without parameters
# or environment variables
env-%:
test -n "${$*}"
(echo "Argument $* not set"; exit 1)
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js env-%
dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID
[…]
# guard against being called without parameters
# or environment variables
env-%:
test -n "${$*}"
(echo "Argument $* not set"; exit 1)
101. test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID
npm run build
mkdir -p dist/
tar czf dist/"${PACKAGE_ID}".tar.gz web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
# guard against being called without parameters
# or environment variables
env-%:
@test -n "${$*}"
(echo "Argument $* not set"; exit 1)
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js env-%
# guard against being called without parameters
# or environment variables
env-%:
@test -n "${$*}"
(echo "Argument $* not set"; exit 1)
102. test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
# guard against being called without parameters
# or environment variables
env-%:
@test -n "${$*}"
(echo "Argument $* not set"; exit 1)
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js env-%
dist/${PACKAGE_ID}.tar.gz: env-PACKAGE_ID
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
103. VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := “my_package-${VERSION}"
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := "my_package-${VERSION}"
104. VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := “my_package-${VERSION}"
test: test-php test-js
test-php: dependencies-php
composer test
test-js: dependencies-js
npm test
# alias for the actual Artifact
build: dist/${PACKAGE_ID}.tar.gz
dist/${PACKAGE_ID}.tar.gz:
npm run build
mkdir -p dist/
tar czf $@ web/ vendor/
dependencies: dependencies-php dependencies-js
dependencies-php:
composer install --no-interaction --no-dev
dependencies-js:
npm install
.PHONY: test test-php test-js build dependencies
dependencies-php dependencies-js
VERSION := $(shell git rev-parse --short HEAD)
PACKAGE_ID := "my_package-${VERSION}"
115. common Makefile targets
libraries applications docker-based projects
all ✅ ✅ ✅
build ✅ ✅ ✅
debug ✅ ✅ ✅
test ✅ ✅ ✅
release ✅ ✅
push ✅
deploy ✅ ✅
clean ✅ ✅ ✅
distclean ✅ ✅
install ✅
up ✅
see make manual — 16.6 Standard Targets for Users
116. - FOO = "value"
The value is recursively computed every time FOO is being
accessed.
- FOO := "value"
The value is directly computed and assigned once to FOO.
- FOO ?= "value"
The value is lazily set if FOO has no value at the moment it
is being accessed.
- FOO += "value"
The value is appended to FOO with a space in front.
see make manual — 6.2 The Two Flavors of Variables
The different kinds of assignments
117. As an argument (better):
$ make target FOO=bar
As an environment variable (requires ?= in Makefile):
$ FOO=bar make target
Setting variables on make invocation
see make manual — 9.5 Overriding Variables
118. Dos
- Fan out from general targets to more specific ones: deploy " deploy-nomad, not deploy-
nomad " deploy TOOL=nomad, use pattern rules to accomplish this with similar targets.
- Split up big targets into smaller chunks; that gives the user the possibility to not
have to run everything but only the parts the they are interested in: test " test-unit,
test-integration, test-codestyle, then the user can just call make test-unit.
- Name targets after files where-ever possible. It will reduce the amount of unnecessary
computation for invocations.
- Use variables to abstract common command calls. ${MAKE} and ${CC} are standards. Why not
add ${GREP} or ${DOCKER}, so users can point your Makefile to the binary they want to
use for those programs?
119. Don’ts
- Use :: to define phony rules. That’s just a side-effect of their main purpose (to split
one target over multiple Makefiles).
- Add to many targets that aren’t used on a regular basis. They will become stale without
you noticing.