From ba4f717368267258660b2a06c4452e83dc25d6c0 Mon Sep 17 00:00:00 2001 From: Maxim Belkin Date: Fri, 20 Dec 2019 18:15:51 -0600 Subject: [PATCH] Reworked makefile --- Makefile | 122 ++++++++++++++---------------------- bin/boilerplate/_config.yml | 1 + commands.mk | 69 ++++++++++++++++++++ tools.mk | 69 ++++++++++++++++++++ 4 files changed, 186 insertions(+), 75 deletions(-) create mode 100644 commands.mk create mode 100644 tools.mk diff --git a/Makefile b/Makefile index c2b18534..247622c7 100644 --- a/Makefile +++ b/Makefile @@ -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 {} \; @@ -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} @@ -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 : @@ -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 + diff --git a/bin/boilerplate/_config.yml b/bin/boilerplate/_config.yml index f5067e55..4b40f8ff 100644 --- a/bin/boilerplate/_config.yml +++ b/bin/boilerplate/_config.yml @@ -89,6 +89,7 @@ defaults: # Files and directories that are not to be copied. exclude: - Makefile + - *.mk - bin/ - .Rproj.user/ - .vendor/ diff --git a/commands.mk b/commands.mk new file mode 100644 index 00000000..7c80a943 --- /dev/null +++ b/commands.mk @@ -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 + + diff --git a/tools.mk b/tools.mk new file mode 100644 index 00000000..c330e636 --- /dev/null +++ b/tools.mk @@ -0,0 +1,69 @@ +# 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) + + +# Determine Jekyll version that we need +ifneq (, $(JEKYLL_VERSION)) + # Get Jekyll version from the Gemfile.lock + ifneq (, $(wildcard ./Gemfile.lock)) + JEKYLL_VERSION := $(shell sed -n "/jekyll\ (=.*)/s|.*(= \(.*\))|\1|p" Gemfile.lock) + endif + + ifeq (, $(JEKYLL_VERSION)) + # Sync with https://pages.github.com/versions + JEKYLL_VERSION := 3.8.5 + 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