From ac47d3137a6a3dc618afe9bf1a6ca860e117942a Mon Sep 17 00:00:00 2001 From: Joe Wreschnig Date: Sat, 18 Apr 2015 19:01:09 +0200 Subject: [PATCH] Makefile refactoring and cleanup. Add new convenience functions/macros, notably include-once, quiet, command-v, and MKDIR. Remove support for old-format (node-webkit 0.8 era) Win32 nwjs zips. Move jshint configuration into standard locations and remove special handling of it from the jshint macros. Change file organization to separate the server, merge default settings, simplify some other variable defaults, etc. --- test/jshint.config => .jshintrc | 0 Makefile | 93 ++++++++++++++------------------- rules/default.mk | 59 +++++++++++++++++++++ rules/git.mk | 2 +- rules/icons.mk | 39 -------------- rules/images.mk | 63 ++++++++++++++++++++++ rules/javascript.mk | 37 ++++++------- rules/nwjs.mk | 33 ++++++------ rules/pngcrush.mk | 20 ------- rules/programs.mk | 21 +++++++- rules/server.mk | 12 +++++ 11 files changed, 226 insertions(+), 153 deletions(-) rename test/jshint.config => .jshintrc (100%) create mode 100644 rules/default.mk delete mode 100644 rules/icons.mk create mode 100644 rules/images.mk delete mode 100644 rules/pngcrush.mk create mode 100644 rules/server.mk diff --git a/test/jshint.config b/.jshintrc similarity index 100% rename from test/jshint.config rename to .jshintrc diff --git a/Makefile b/Makefile index f2d4697..b3207b0 100644 --- a/Makefile +++ b/Makefile @@ -1,89 +1,72 @@ #!/usr/bin/make -f +# +# This is free and unencumbered software released into the public +# domain. To the extent possible under law, the author of this file +# waives all copyright and related or neighboring rights to it. nwjs-version := 0.12.0 -all: -print-%: ; @echo $*=$($*) +stampprefix := build/stamp/ -.DELETE_ON_ERROR: -include rules/programs.mk -include rules/javascript.mk -include rules/icons.mk -include rules/git.mk -include rules/nwjs.mk -include rules/pngcrush.mk +include rules/default.mk +$(call include-once,rules/*.mk) -.PHONY: all check distclean lint test dist clean serve +.PHONY: all check clean distclean lint test dist APPLICATION := pwl6 -DISTDIR := build/dist +DISTDIR := build/dist/ VERSION := $(call git-describe) -DISTROOT := $(DISTDIR)/$(APPLICATION)_$(VERSION) +DISTROOT := $(DISTDIR)$(APPLICATION)_$(VERSION) -FIND_FILES := -type f ! -name '.*' -FIND_JS := -type f -name '*.js' +MY_SOURCES := $(call find-js,src -path 'src/ext' -prune -o) +TEST_SOURCES := $(call find-js,test/spec) -SOURCES := $(shell cd src && find . $(FIND_FILES)) -MY_SOURCES := $(shell cd src && find . $(FIND_JS) -not -path './ext/*') - -TEST_SOURCES := $(shell find test/spec $(FIND_JS)) - -LINT_TARGETS := $(call jshint-stampify,$(MY_SOURCES)) -TEST_TARGETS := $(call jstest-stampify,$(TEST_SOURCES)) - -JSHINTCONFIG := test/jshint.config -JSTEST_NODE_PATH := src VPATH := src -ICONSETS := $(shell find src -type d -name '*.iconset') -ICONS := $(ICONSETS:.iconset=.icns) $(ICONSETS:.iconset=.ico) - -IMAGEGZSRC := $(shell find src -type f -name '*.xcf.gz') -IMAGESRC := $(shell find src -type f -name '*.xcf') -IMAGES := $(IMAGEGZSRC:.xcf.gz=.png) $(IMAGESRC:.xcf=.png) +ICONS := $(call iconset>icons,$(call find-iconsets,src)) +XCFS := $(call find-xcfs,src) +OBJPNGS := $(call xcf>png,$(XCFS)) +PNGS := $(call find-files,src,-name '*.png') $(OBJPNGS) -BUILT := $(ICONS) $(IMAGES) +BUILT := $(ICONS) $(OBJPNGS) +CRUSHABLE := $(call pngcrush-stampify,$(PNGS)) +LINTABLE := $(call jshint-stampify,$(MY_SOURCES)) +TESTABLE := $(call jstest-stampify,$(TEST_SOURCES)) +DISTFILES := $(addprefix $(DISTROOT)-,$(nwjs-platforms)) -HTTP_SERVER_PORT ?= 8000 +all:: check $(BUILT) $(CRUSHABLE) -all: check $(BUILT) $(call pngcrush-stampify,$(IMAGES)) +dist: $(DISTFILES) -$(DISTDIR): - mkdir -p $@ - -dist: $(addprefix $(DISTROOT),-src.zip -src.tar.gz .appcache .nw -osx-ia32.zip -osx-x64.zip -linux-ia32.tar.gz -linux-x64.tar.gz -win-ia32.zip) - -test/spec/%.js: %.js +test/spec/%.js:: %.js touch $@ -lint: $(LINT_TARGETS) - -test: $(TEST_TARGETS) - -check: lint test +lint:: $(LINTABLE) +test:: $(TESTABLE) -serve: | $(npmbindir)/http-server - $(npmbindir)/http-server $(@D) -p $(HTTP_SERVER_PORT) -c-1 +check:: lint test -clean: - $(RM) $(IMAGES) - $(RM) $(ICONS) - $(RM) -r build +clean:: + $(RM) $(DISTFILES) + $(RM) $(BUILT) + $(RM) $(LINTABLE) + $(RM) $(TESTABLE) + $(RM) $(CRUSHABLE) + $(call rmdir-r,build) -distclean: clean +distclean:: clean $(RM) -r node_modules - $(RM) $(nwjs-archives) $(DISTROOT)-src.zip $(DISTROOT)-src.tar.gz: | .git - mkdir -p $(@D) + $(MKDIR) $(@D) $(call git-archive,$@,$(notdir $(DISTROOT))/) $(DISTROOT).bare.zip: | .git $(RM) $@ $(RM) -r $@.tmp - mkdir -p $@.tmp + $(MKDIR) $@.tmp cd src && $(GIT) archive $(call git-describe) . | tar -x -C ../$@.tmp $(MAKE) $(BUILT:src/%=$@.tmp/%) - $(RM) $(IMAGESRC:src/%=$@.tmp/%) $(IMAGEGZSRC:src/%=$@.tmp/%) + $(RM) $(XCFS:src/%=$@.tmp/%) cd $@.tmp && $(ZIP) ../$(@F) -r . $(RM) -r $@.tmp diff --git a/rules/default.mk b/rules/default.mk new file mode 100644 index 0000000..2bfa76d --- /dev/null +++ b/rules/default.mk @@ -0,0 +1,59 @@ +# This is free and unencumbered software released into the public +# domain. To the extent possible under law, the author of this file +# waives all copyright and related or neighboring rights to it. + +# Should be defaults. +.DELETE_ON_ERROR: +.DEFAULT_GOAL: all + +# Force these to be double-colon targets, so individual makefiles can +# extend them as necessary (particularly for clean). +all:: +clean:: +distclean:: + +# http://blog.jgc.org/2015/04/the-one-line-you-should-add-to-every.html +# Depends on FORCE because "implicit [pattern] rule search is skipped +# for .PHONY targets." +print-%: FORCE ; @echo $*=$($*) + +.PHONY: FORCE all clean distclean + +# Use with include to ensure files are included only once. e.g.: +# $(call include-once,example.mk more-rules/*.mk) +include-once = $(if $(wildcard $1),\ + $(foreach f,$(sort $(wildcard $1)),\ + $(eval include $(filter-out $(MAKEFILE_LIST),$f))),\ + $(error $1: No such file or directory)) + +# Call before including anything for the current makefile's directory. +where-am-i = $(dir $(lastword $(MAKEFILE_LIST))) + +# Like include-once, but the files are searched for relative to the most +# recently included file (usually the current one) not the current +# working directory. +include-once. = $(call include-once,$(addprefix $(call where-am-i),$1)) + + +# Quiet the output of a command, unless an error occurs. This is +# similar to the shell 'chronic' / 'cronic' programs. +# +# To hide output unless an error occurs, pass a single argument: +# $(call quiet,munge somefile) +# +# To store output to a file unless an error occurs, pass a second +# argument of the filename. If an error occurs, this file will not +# exist. Such a file will often be the rule's target. +# $(call quiet,munge somefile,$@) + +define quiet +@echo "$1" $(if $2,"> $2") +$(eval QUIETTMP := $(firstword $2 $(shell mktemp -t make-quiet.XXXXX))) +@($1) > $(QUIETTMP) \ + || (STATUS=$$? \ + && cat "$(QUIETTMP)" \ + && $(RM) "$(QUIETTMP)" \ + && exit $$STATUS) +$(if $2,,$(RM) "$(QUIETTMP)") +endef + diff --git a/rules/git.mk b/rules/git.mk index e8ceb78..6c8397d 100644 --- a/rules/git.mk +++ b/rules/git.mk @@ -10,4 +10,4 @@ $(GIT) archive --output '$1' $(if $2,--prefix '$2') '$(call git-describe,$3)' endef .git: - $(error "This target must be run inside a git repository.") + $(error This target must be run inside a git repository.) diff --git a/rules/icons.mk b/rules/icons.mk deleted file mode 100644 index eba5e89..0000000 --- a/rules/icons.mk +++ /dev/null @@ -1,39 +0,0 @@ -# This is free and unencumbered software released into the public -# domain. To the extent possible under law, the author of this file -# waives all copyright and related or neighboring rights to it. - -.DELETE_ON_ERROR: - -XCF2PNG ?= xcf2png - -%.png: %.xcf - $(XCF2PNG) $< > $@ - -# First: xcf2png by default calls zcat rather than gzcat. This is -# totally always broken; zcat forces a .Z extension on its input -# filename. So we can't rely on xcf2png's default behavior. But it -# offers -Z for a custom decompression program. -# -# BUT: Gimp produces gz files with some padding zeros because, I don't -# know, someone might want to save their compressed xcfs to DECtape. -# gzip has a -q option to not *print* the warning associated with this -# harmless thing, but then goes ahead and exits non-zero anyway, which -# makes xcf2png barf and die before writing anything even though it -# got perfectly good data. -# -# So: 1) use gunzip, 2) manually feed it to xcf2png, 3) hope nothing is -# set to die based on pipe status, 4) hope that if the xcf data is -# actually busted xcf2png will do something helpful. -%.png: %.xcf.gz - gunzip -c $< | $(XCF2PNG) - > $@ - -.SECONDEXPANSION: - -ICONUTIL ?= $(firstword $(shell command -v iconutil icnsutil) iconutil) - -%.icns: %.iconset $$(wildcard $$(@D)/$$*.iconset/icon_*.png) - $(ICONUTIL) -c icns -o $@ $< - -%.ico: %.iconset $$(wildcard $$(@D)/$$*.iconset/icon_*[0-9].png) - convert -background transparent -colors 256 $(filter-out $<,$^) $@ - diff --git a/rules/images.mk b/rules/images.mk new file mode 100644 index 0000000..4ef2d69 --- /dev/null +++ b/rules/images.mk @@ -0,0 +1,63 @@ +# This is free and unencumbered software released into the public +# domain. To the extent possible under law, the author of this file +# waives all copyright and related or neighboring rights to it. + +$(call include-once.,programs.mk) + +XCF2PNG ?= xcf2png +ICONUTIL := $(call command-v,iconutil icnsutil) + +PNGCRUSH ?= pngcrush +PNGCRUSHFLAGS ?= -brute -blacken -reduce -q +CRUSH.png ?= $(PNGCRUSH) $(PNGCRUSHFLAGS) -ow + +%.png: %.xcf + $(XCF2PNG) $< > $@ + +# First: xcf2png by default calls zcat rather than gzcat. This is +# totally always broken; non-GNU zcat forces a .Z extension on its +# input filename. So we can't rely on xcf2png's default behavior. +# Luckily it offers -Z for a custom decompression program. +# +# BUT: When Gimp saves a .xcf.gz (and related formats), it doesn't +# truncate the file, it just writes until it's done and closes, and +# whatever data was after that, is still after that. This is terrible +# because it means the file never shrinks, but it's still nominally a +# valid gzip file, according to http://www.gzip.org/#faq8. +# +# BUT: The suggestion to use gzip -q in the FAQ doesn't work for +# non-GNU gzip. It shuts up the 'warning,' yes, but it still exits +# with status code 1 (not even 2, like GNU gzip does for all +# warnings). It does write out the data perfectly well, at least. +# +# BUT (of course): The non-zero exit (somewhat reasonably) makes +# xcf2png barf and die before writing anything even though it got all +# the data it needs. +# +# So: 1) use gunzip, 2) manually feed it to xcf2png, 3) hope nothing +# is set to die based on pipe status, 4) hope that if the xcf data is +# truly busted xcf2png will do something helpful. +%.png: %.xcf.gz + gunzip -c $< | $(XCF2PNG) - > $@ + +# https://korewanetadesu.com/make-icons.html +.SECONDEXPANSION: +%.icns: %.iconset $$(wildcard $$(@D)/$$*.iconset/icon_*.png) + $(ICONUTIL) -c icns -o $@ $< + +%.ico: %.iconset $$(wildcard $$(@D)/$$*.iconset/icon_*[0-9].png) + convert -background transparent -colors 256 $(filter-out $<,$^) $@ + +find-iconsets = $(shell find $1 -type d -name '*.iconset') +find-xcfs = $(shell find $1 -type f -name '*.xcf.gz' -or -name '*.xcf') + +iconset>icons = $(1:.iconset=.icns) $(1:.iconset=.ico) +xcf>png = $(patsubst %.xcf,%.png,$(patsubst %.xcf.gz,%.xcf,$1)) + +pngcrush-stampprefix ?= $(stampprefix) +pngcrush-stampify = $(1:%.png=$(pngcrush-stampprefix)%.png.crushed) + +$(pngcrush-stampprefix)%.png.crushed: %.png + $(MKDIR) $(@D) + $(CRUSH.png) $< + touch $@ diff --git a/rules/javascript.mk b/rules/javascript.mk index 15fb8a5..a7bac87 100644 --- a/rules/javascript.mk +++ b/rules/javascript.mk @@ -2,9 +2,7 @@ # domain. To the extent possible under law, the author of this file # waives all copyright and related or neighboring rights to it. -.DELETE_ON_ERROR: - -javascript>fallback = $(firstword $(shell command -v $1) $2 $1) +$(call include-once.,programs.mk) NPM ?= npm NPMROOT ?= $(CURDIR) @@ -12,7 +10,7 @@ NPMROOT ?= $(CURDIR) npmbindir = $(NPMROOT)/node_modules/.bin .PRECIOUS: $(npmbindir)/% -npmbin = $(call javascript>fallback,$1,$(npmbindir)/$1) +npmbin = $(call command-v,$1 $(npmbindir)/$1) JSTEST ?= $(npmbindir)/jstest JSHINT ?= $(call npmbin,jshint) @@ -24,39 +22,36 @@ UGLIFYFLAGS ?= --comments \ --compress $(UGLIFYCOMPRESSFLAGS) \ --mangle $(UGLIFYMANGLEFLAGS) -BUILDDIR ?= build/ -JSSTAMPDIR ?= $(BUILDDIR)/stamp -JSHINTDIR ?= $(JSSTAMPDIR) -JSTESTDIR ?= $(JSSTAMPDIR) -JSUGLYDIR ?= $(CURDIR) +jsstampprefix ?= $(stampprefix) +jsuglyprefix ?= -JSHINTFLAGS += $(if $(JSHINTCONFIG),--config $(JSHINTCONFIG)) JSTESTFLAGS += $(if $(JSTESTFORMAT),--format $(JSTESTFORMAT)) +JSTEST_NODE_PATH ?= $(VPATH) JSTESTENV += $(if $(JSTEST_NODE_PATH),NODE_PATH=$(JSTEST_NODE_PATH)) JSTESTFORMAT ?= spec -jshint-stampify = $(patsubst %.js,$(JSHINTDIR)/%.js.lint,$1) -jstest-stampify = $(patsubst %.js,$(JSTESTDIR)/%.js.test,$1) -uglify-stampify = $(patsubst %.js,$(JSUGLYDIR)/%.min.js,$1) +jshint-stampify = $(patsubst %.js,$(jsstampprefix)%.js.lint,$1) +jstest-stampify = $(patsubst %.js,$(jsstampprefix)%.js.test,$1) +uglify-stampify = $(patsubst %.js,$(jsuglyprefix)%.min.js,$1) -javascript>capture-to-target = @echo "$1" && $1 > $@ || (cat $@ && exit 1) +find-js = $(shell find $1 $(if $2,$2 -o) -type f -name '*.js' -print) UGLIFY.js = $(UGLIFY) $(UGLIFYFLAGS) LINT.js = $(JSHINT) $(JSHINTFLAGS) TEST.js = $(JSTESTENV) $(JSTEST) $(JSTESTFLAGS) -$(JSUGLYDIR)/%.min.js: %.js | $(UGLIFY) - mkdir -p $(@D) +$(jsuglyprefix)/%.min.js: %.js | $(UGLIFY) + $(MKDIR) $(@D) $(UGLIFY.js) < $< > $@ -$(JSHINTDIR)/%.js.lint: %.js | $(JSHINT) - mkdir -p $(@D) +$(jsstampprefix)%.js.lint: %.js | $(JSHINT) + $(MKDIR) $(@D) $(LINT.js) $< touch $@ -$(JSTESTDIR)/%.js.test: %.js | $(JSTEST) - mkdir -p $(@D) - $(call javascript>capture-to-target,$(TEST.js) $<) +$(jsstampprefix)%.js.test: %.js | $(JSTEST) + $(MKDIR) $(@D) + $(call quiet,$(TEST.js) $<,$@) $(npmbindir)/%: $(NPM) install $(firstword $(value $(@F)_npm_package) $(@F)) diff --git a/rules/nwjs.mk b/rules/nwjs.mk index 42bbf79..bd5f37d 100644 --- a/rules/nwjs.mk +++ b/rules/nwjs.mk @@ -2,30 +2,28 @@ # domain. To the extent possible under law, the author of this file # waives all copyright and related or neighboring rights to it. -.DELETE_ON_ERROR: - -include $(dir $(realpath $(lastword $(MAKEFILE_LIST))))programs.mk +$(call include-once.,programs.mk) nwjs-platforms := \ osx-x64.zip osx-ia32.zip \ linux-x64.tar.gz linux-ia32.tar.gz \ win-ia32.zip -nwjs-pattern := \ - $(addprefix nwjs-v%-,$(nwjs-platforms)) +nwjs-pattern := $(addprefix nwjs-v%-,$(nwjs-platforms)) # These are like 30MB, don't download them every time. .PRECIOUS: $(nwjs-pattern) $(nwjs-pattern): - mkdir -p $(@D) - wget -O $@ http://dl.nwjs.io/v$(*F)/$(@F) || ($(RM) $@ && exit 1) + $(MKDIR) '$(@D)' + wget -O '$@'.inprogress 'http://dl.nwjs.io/v$(*F)/$(@F)' + mv '$@'.inprogress "$@" -nwjs-version ?= 0.10.2 -nwjs-prefix ?= nwjs/ -nwjs-directx = $(addprefix $(nwjs-prefix),d3dcompiler_43.dll d3dcompiler_46.dll) +nwjs-version ?= 0.12.0 +nwjsdir ?= nwjs/ +nwjs-directx = $(addprefix $(nwjsdir),d3dcompiler_43.dll d3dcompiler_46.dll) -nwjs = $(nwjs-prefix)nwjs-v$(firstword $(value nwjs-version-$1) $(nwjs-version))-$1 +nwjs = $(nwjsdir)nwjs-v$(firstword $(value nwjs-version-$1) $(nwjs-version))-$1 nwjs-archives = $(foreach p,$(nwjs-platforms),$(call nwjs,$(p))) @@ -51,7 +49,7 @@ define nwjs-package-linux $(RM) $@ $(RM) -r $(@:.tar.gz=) tar -C $(@D) -xzf $2 - mkdir -p $(@:.tar.gz=) + $(MKDIR) $(@:.tar.gz=) mv $(@D)/$(notdir $(2:.tar.gz=)) $(@:.tar.gz=)/nw cp -a $1 $(@:.tar.gz=)/nw/package.nw cp -a tools/nw-linux-wrapper $(@:.tar.gz=)/`echo $(notdir $1) | sed -E 's/_.+//'` @@ -67,14 +65,12 @@ endef %-linux-x64.tar.gz: %.nw $(call nwjs,linux-x64.tar.gz) $(call nwjs-package-linux,$<,$(word 2,$^)) -WINE ?= wine - nwjs-icon = $(shell $(UNZIP) -p $1 package.json | grep -Eo '"[^"]+.ico"' -m 1) define nwjs-package-win $(RM) $@ $(RM) -r $(@:.zip=) - if $(UNZIP) -l $2 credits.html > /dev/null; then $(UNZIP) -d $(@D)/$(notdir $(2:.zip=)) $2; else $(UNZIP) -d $(@D) $2; fi + $(UNZIP) -d $(@D) $2 mv $(@D)/$(notdir $(2:.zip=)) $(@:.zip=) $(RM) $(@:.zip=)/nwjc.exe $(UNZIP) -p $< $(call nwjs-icon,$<) > $(@D)/icon.ico @@ -94,5 +90,10 @@ endef $(nwjs-directx): wget -O $@ 'https://github.com/cefsharp/cef-binary/raw/1e51255cf77d267899bf7834768b8774affaad2d/cef_binary_3.y.z_windows32/Release/'$(notdir $@) -$(nwjs-prefix)dxwebsetup.exe: +$(nwjsdir)dxwebsetup.exe: wget -O $@ http://download.microsoft.com/download/1/7/1/1718CCC4-6315-4D8E-9543-8E28A4E18C4C/dxwebsetup.exe + +distclean:: + $(RM) $(nwjs-archives) + $(RM) $(nwjs-directx) + $(RM) $(nwjsdir)dxwebsetup.exe diff --git a/rules/pngcrush.mk b/rules/pngcrush.mk deleted file mode 100644 index 39630df..0000000 --- a/rules/pngcrush.mk +++ /dev/null @@ -1,20 +0,0 @@ -# This is free and unencumbered software released into the public -# domain. To the extent possible under law, the author of this file -# waives all copyright and related or neighboring rights to it. - -.DELETE_ON_ERROR: - -BUILDDIR ?= build/ -PNGCRUSHSTAMPDIR ?= $(BUILDDIR)/stamp - -pngcrush-stampify = $(patsubst %.png,$(PNGCRUSHSTAMPDIR)/%.png.crushed,$1) - -PNGCRUSH ?= pngcrush -PNGCRUSHFLAGS ?= -brute -blacken -reduce -q - -CRUSH.png ?= $(PNGCRUSH) $(PNGCRUSHFLAGS) - -$(PNGCRUSHSTAMPDIR)/%.png.crushed: %.png - $(CRUSH.png) -ow $< - mkdir -p $(@D) - touch $@ diff --git a/rules/programs.mk b/rules/programs.mk index d706d3f..bd5b850 100644 --- a/rules/programs.mk +++ b/rules/programs.mk @@ -9,5 +9,24 @@ UNZIP = unzip $(UNZIPFLAGS) ZIP = zip $(ZIPFLAGS) ifneq ($(OS),Windows_NT) - WINE ?= wine +WINE ?= wine endif + +find-files = $(shell find $1 -type f $2 -print $3) + +rmdir-r = [ ! -e $1 ] || find -d $1 -type d -delete + +# Use to find the best command to execute out of a series of possible +# equivalent ones, or provide a fallback. If a second argument is +# supplied it is used as the fallback, otherwise the first element of +# the checked executables is used. e.g. +# PYLINTER := $(call command-v,flake8 pyflakes,python -mpy_compile) +# will pick flake8 if it's installed, or pyflakes if it isn't, +# or python -mpy_compile (a syntax check) if neither is +# installed. +command-v = $(firstword $(shell command -v $1) $2 $1) + +# Use in place of 'mkdir -p' to avoid littering every other line of +# output with a redundant mkdir call; the downside is that it can't be +# silenced itself. +MKDIR = @$(SHELL) -c '[ -d "$$0" ] || echo mkdir -p "$$0" && mkdir -p "$$0"' diff --git a/rules/server.mk b/rules/server.mk new file mode 100644 index 0000000..87964e7 --- /dev/null +++ b/rules/server.mk @@ -0,0 +1,12 @@ +# This is free and unencumbered software released into the public +# domain. To the extent possible under law, the author of this file +# waives all copyright and related or neighboring rights to it. + +$(call include-once.,javascript.mk) + +HTTP_SERVER_PORT ?= 8000 + +serve: | $(npmbindir)/http-server + $(npmbindir)/http-server $(@D) -p $(HTTP_SERVER_PORT) -c-1 + +.PHONY: serve -- 2.30.2