Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simpler and more robust Makefile #451

Open
wants to merge 2 commits into
base: gh-pages
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 47 additions & 75 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,73 +1,51 @@
# Use /bin/bash instead of /bin/sh
export SHELL = /bin/bash

## ========================================
## Commands for both workshop and lesson websites.

# Settings
MAKEFILES=Makefile $(wildcard *.mk)
JEKYLL_VERSION=3.8.5
JEKYLL=bundle install --path .vendor/bundle && bundle update && bundle exec jekyll
PARSER=bin/markdown_ast.rb
DST=_site

# Check Python 3 is installed and determine if it's called via python3 or python
# (https://stackoverflow.com/a/4933395)
PYTHON3_EXE := $(shell which python3 2>/dev/null)
ifneq (, $(PYTHON3_EXE))
ifeq (,$(findstring Microsoft/WindowsApps/python3,$(subst \,/,$(PYTHON3_EXE))))
PYTHON := python3
endif
endif
# #################################################
# ** Customizations **

ifeq (,$(PYTHON))
PYTHON_EXE := $(shell which python 2>/dev/null)
ifneq (, $(PYTHON_EXE))
PYTHON_VERSION_FULL := $(wordlist 2,4,$(subst ., ,$(shell python --version 2>&1)))
PYTHON_VERSION_MAJOR := $(word 1,${PYTHON_VERSION_FULL})
ifneq (3, ${PYTHON_VERSION_MAJOR})
$(error "Your system does not appear to have Python 3 installed.")
endif
PYTHON := python
else
$(error "Your system does not appear to have any Python installed.")
endif
endif
# Markdown parser (requires 'kramdown' & 'json')
PARSER ?= bin/markdown_ast.rb

# Local directory where
DST ?= _site

# Address and port of a locally rendered website
# Used by `jekyll` or Docker
HOST ?= 127.0.0.1
PORT ?= 4000
# ##################################################

# Controls
.PHONY : commands clean files
# Variables & commands
-include tools.mk
-include commands.mk

# Default target
.DEFAULT_GOAL := commands

## I. Commands for both workshop and lesson websites
## =================================================
.PHONY: serve site docker-serve repo-check clean clean-rmd

## * serve : render website and run a local server
serve : lesson-md
${JEKYLL} serve
@$(JEKYLL_SERVE_CMD)

## * site : build website but do not run a server
site : lesson-md
${JEKYLL} build
@$(JEKYLL_BUILD_CMD)

## * docker-serve : use Docker to serve the site
docker-serve :
docker run --rm -it --volume ${PWD}:/srv/jekyll \
--volume=${PWD}/.docker-vendor/bundle:/usr/local/bundle \
-p 127.0.0.1:4000:4000 \
jekyll/jekyll:${JEKYLL_VERSION} \
bin/run-make-docker-serve.sh
@$(DOCKER_SERVE_CMD)

## * repo-check : check repository settings
repo-check :
@${PYTHON} bin/repo_check.py -s .
@$(REPO_CHECK_CMD)

## * clean : clean up junk files
clean :
@rm -rf ${DST}
@rm -rf .sass-cache
@$(JEKYLL_CLEAN_CMD)
@rm -rf bin/__pycache__
@find . -name .DS_Store -exec rm {} \;
@find . -name '*~' -exec rm {} \;
Expand All @@ -82,43 +60,18 @@ clean-rmd :
##
## II. Commands specific to workshop websites
## =================================================

.PHONY : workshop-check
.PHONY: workshop-check

## * workshop-check : check workshop homepage
workshop-check :
@${PYTHON} bin/workshop_check.py .
@$(WORKSHOP_CHECK_CMD)


##
## III. Commands specific to lesson websites
## =================================================

.PHONY : lesson-check lesson-md lesson-files lesson-fixme

# RMarkdown files
RMD_SRC = $(wildcard _episodes_rmd/??-*.Rmd)
RMD_DST = $(patsubst _episodes_rmd/%.Rmd,_episodes/%.md,$(RMD_SRC))

# Lesson source files in the order they appear in the navigation menu.
MARKDOWN_SRC = \
index.md \
CODE_OF_CONDUCT.md \
setup.md \
$(sort $(wildcard _episodes/*.md)) \
reference.md \
$(sort $(wildcard _extras/*.md)) \
LICENSE.md

# Generated lesson files in the order they appear in the navigation menu.
HTML_DST = \
${DST}/index.html \
${DST}/conduct/index.html \
${DST}/setup/index.html \
$(patsubst _episodes/%.md,${DST}/%/index.html,$(sort $(wildcard _episodes/*.md))) \
${DST}/reference/index.html \
$(patsubst _extras/%.md,${DST}/%/index.html,$(sort $(wildcard _extras/*.md))) \
${DST}/license/index.html
.PHONY: lesson-md lesson-check lesson-check-all \
unittest lesson-files lesson-fixme

## * lesson-md : convert Rmarkdown files to markdown
lesson-md : ${RMD_DST}
Expand All @@ -128,15 +81,15 @@ _episodes/%.md: _episodes_rmd/%.Rmd

# * lesson-check : validate lesson Markdown
lesson-check : lesson-fixme
@${PYTHON} bin/lesson_check.py -s . -p ${PARSER} -r _includes/links.md
@$(LESSON_CHECK_CMD)

## * lesson-check-all : validate lesson Markdown, checking line lengths and trailing whitespace
lesson-check-all :
@${PYTHON} bin/lesson_check.py -s . -p ${PARSER} -r _includes/links.md -l -w --permissive
@$(LESSON_CHECK_ALL_CMD)

## * unittest : run unit tests on checking tools
unittest :
@${PYTHON} bin/test_lesson_check.py
@$(UNITTEST_CMD)

## * lesson-files : show expected names of generated files for debugging
lesson-files :
Expand All @@ -152,7 +105,26 @@ lesson-fixme :
##
## IV. Auxililary (plumbing) commands
## =================================================
.PHONY: commands bundle lock

## * commands : show all commands.
commands :
@sed -n -e '/^##/s|^##[[:space:]]*||p' $(MAKEFILE_LIST)

## * bundle : create or update .vendor/bundle.
bundle : .vendor/bundle

.vendor/bundle : Gemfile
@$(BUNDLE_INSTALL_CMD)

## * lock : create or update Gemfile.lock from the current Gemfile
lock : Gemfile.lock

Gemfile.lock : Gemfile
@$(BUNDLE_LOCK_CMD)

Gemfile :
ifeq (, $(wildcard ./Gemfile))
$(error Gemfile not found)
endif

1 change: 1 addition & 0 deletions bin/boilerplate/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ defaults:
# Files and directories that are not to be copied.
exclude:
- Makefile
- *.mk
- bin/
- .Rproj.user/
- .vendor/
Expand Down
69 changes: 69 additions & 0 deletions commands.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# JEKYLL_BUILD_CMD
# JEKYLL_CLEAN_CMD
# JEKYLL_SERVE_CMD
ifneq (, $(JEKYLL))
JEKYLL_CMD := jekyll
# We can use 'bundle exec' only if we have a Gemfile
ifneq (, $(wildcard ./Gemfile))
ifneq (, $(BUNDLE))
JEKYLL_CMD := bundle exec $(JEKYLL_CMD)
endif
endif
JEKYLL_BUILD_CMD := $(JEKYLL_CMD) build
JEKYLL_CLEAN_CMD := $(JEKYLL_CMD) clean
JEKYLL_SERVE_CMD := $(JEKYLL_CMD) serve -H $(HOST) -P $(PORT)
else
JEKYLL_BUILD_CMD = $(error Can't build a website: Jekyll not found)
JEKYLL_CLEAN_CMD = rm -rf $(DST) .jekyll-metadata .sass-cache
JEKYLL_SERVE_CMD = $(error Can't serve a website: Jekyll not found)
endif


# DOCKER_SERVE_CMD
ifneq (, $(DOCKER))
DOCKER_SERVE_CMD = \
$(DOCKER) run --rm -it \
--volume ${PWD}:/srv/jekyll \
--volume ${PWD}/.docker-vendor/bundle:/usr/local/bundle \
--publish $(HOST):$(PORT):4000 \
jekyll/jekyll:${JEKYLL_VERSION} \
bin/run-make-docker-serve.sh
else
DOCKER_SERVE_CMD = $(error Can't serve the site using Docker: Docker not found)
endif


# REPO_CHECK_CMD
# WORKSHOP_CHECK_CMD
# LESSON_CHECK_CMD
# LESSON_CHECK_ALL_CMD
# UNITTEST_CMD
ifneq (, $(PYTHON))
REPO_CHECK_CMD = ${PYTHON} bin/repo_check.py -s .
WORKSHOP_CHECK_CMD = ${PYTHON} bin/workshop_check.py .
LESSON_CHECK_CMD = ${PYTHON} bin/lesson_check.py -s . -p ${PARSER} -r _includes/links.md
LESSON_CHECL_ALL_CMD = ${PYTHON} bin/lesson_check.py -s . -p ${PARSER} -r _includes/links.md -l -w --permissive
UNITTEST_CMD = ${PYTHON} bin/test_lesson_check.py
else
REPO_CHECK_CMD = $(error Can't check repository settings: Python 3 not found!)
WORKSHOP_CHECK_CMD = $(error Can't check workshop homepage: Python 3 not found)
LESSON_CHECK_CMD = $(error Can't validate lesson files: Python 3 not found)
LESSON_CHECK_ALL_CMD = $(error Can't validate lesson files: Python 3 not found)
UNITTEST_CMD = $(error Can't perform unit testing: Python 3 not found)
endif


# BUNDLE_INSTALL_CMD
# BUNDLE_LOCK_CMD
ifneq (, $(BUNDLE))
define BUNDLE_INSTALL_CMD
$(BUNDLE) install --path .vendor/bundle
$(BUNDLE) update
endef
BUNDLE_LOCK_CMD = $(BUNDLE) lock --update
else
BUNDLE_INSTALL_CMD = $(error Can't create .vendor/bundle: Bundle not found)
BUNDLE_LOCK_CMD = $(error Can't create/update Gemfile.lock: Bundle not found)
endif


88 changes: 88 additions & 0 deletions tools.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Detect required programs: Gem, Docker, Rscript, Jekyll
GEM ?= $(shell which gem 2>/dev/null)
DOCKER ?= $(shell which docker 2>/dev/null)
RSCRIPT ?= $(shell which Rscript 2>/dev/null)
JEKYLL ?= $(shell which jekyll 2>/dev/null)
BUNDLE ?= $(shell which bundle 2>/dev/null)
CURL ?= $(shell which curl 2>/dev/null)
SED ?= $(shell which sed 2>/dev/null)


# Determine Jekyll version that we need
ifeq (,$(JEKYLL_VERSION))
ifneq (, $(SED))
# Obtain required version from https://pages.github.com/versions
ifneq (, $(CURL))
JEKYLL_VERSION_GITHUB := $(shell curl -s https://pages.github.com/versions.json | sed -n "s|.*\"jekyll\":\"\([^\"]*\)\".*|\1|p")
endif

# Get Jekyll version from the Gemfile.lock
ifneq (, $(wildcard ./Gemfile.lock))
JEKYLL_VERSION_LOCK := $(shell sed -n "/jekyll\ (=.*)/s|.*(= \(.*\))|\1|p" Gemfile.lock)

# Compare two verions of Jekyll, if both are available
ifneq (, $(JEKYLL_VERSION_GITHUB))
ifneq ($(JEKYLL_VERSION_LOCK),$(JEKYLL_VERSION_GITHUB))
ifneq ($(MAKECMDGOALS),lock)
$(warning )
$(warning GitHub requires Jekyll $(JEKYLL_VERSION_GITHUB))
$(warning Gemfile.lock specifies Jekyll $(JEKYLL_VERSION_LOCK))
$(warning Please update Gemfile.lock by running 'make -B lock')
$(warning )
endif
endif
endif
endif
else
$(warning Your system does not have Sed installed.)
endif
endif


# Python
# Check Python 3 is installed and determine if it's called via python3 or python
PYTHON3_EXE := $(shell which python3 2>/dev/null)
ifneq (, $(PYTHON3_EXE))
ifeq (,$(findstring Microsoft/WindowsApps/python3,$(subst \,/,$(PYTHON3_EXE))))
PYTHON := python3
endif
endif

ifeq (,$(PYTHON))
PYTHON_EXE := $(shell which python 2>/dev/null)
ifneq (, $(PYTHON_EXE))
PYTHON_VERSION_FULL := $(wordlist 2,4,$(subst ., ,$(shell python --version 2>&1)))
PYTHON_VERSION_MAJOR := $(word 1,${PYTHON_VERSION_FULL})
ifneq (3, ${PYTHON_VERSION_MAJOR})
$(warning Your system does not appear to have Python 3 installed)
else
PYTHON := python
endif
else
$(warning Your system does not appear to have any Python installed)
endif
endif

# RMarkdown files
RMD_SRC = $(wildcard _episodes_rmd/??-*.Rmd)
RMD_DST = $(patsubst _episodes_rmd/%.Rmd,_episodes/%.md,$(RMD_SRC))

# Lesson source files in the order they appear in the navigation menu.
MARKDOWN_SRC = \
index.md \
CODE_OF_CONDUCT.md \
setup.md \
$(sort $(wildcard _episodes/*.md)) \
reference.md \
$(sort $(wildcard _extras/*.md)) \
LICENSE.md

# Generated lesson files in the order they appear in the navigation menu.
HTML_DST = \
${DST}/index.html \
${DST}/conduct/index.html \
${DST}/setup/index.html \
$(patsubst _episodes/%.md,${DST}/%/index.html,$(sort $(wildcard _episodes/*.md))) \
${DST}/reference/index.html \
$(patsubst _extras/%.md,${DST}/%/index.html,$(sort $(wildcard _extras/*.md))) \
${DST}/license/index.html