1## Copyright 2015-2016 Carnë Draug 2## Copyright 2015-2016 Oliver Heimlich 3## Copyright 2017 Julien Bect <jbect@users.sf.net> 4## Copyright 2017 Olaf Till <i7tiol@t-online.de> 5## Copyright 2019 John Donoghue <john.donoghue@ieee.org> 6## 7## Copying and distribution of this file, with or without modification, 8## are permitted in any medium without royalty provided the copyright 9## notice and this notice are preserved. This file is offered as-is, 10## without any warranty. 11 12TOPDIR := $(shell pwd) 13 14## Some basic tools (can be overriden using environment variables) 15SED ?= sed 16TAR ?= tar 17GREP ?= grep 18CUT ?= cut 19TR ?= tr 20TEXI2PDF ?= texi2pdf -q 21 22## Note the use of ':=' (immediate set) and not just '=' (lazy set). 23## http://stackoverflow.com/a/448939/1609556 24package := $(shell $(GREP) "^Name: " DESCRIPTION | $(CUT) -f2 -d" " | \ 25$(TR) '[:upper:]' '[:lower:]') 26version := $(shell $(GREP) "^Version: " DESCRIPTION | $(CUT) -f2 -d" ") 27 28## These are the paths that will be created for the releases. 29target_dir := target 30release_dir := $(target_dir)/$(package)-$(version) 31release_tarball := $(target_dir)/$(package)-$(version).tar.gz 32html_dir := $(target_dir)/$(package)-html 33html_tarball := $(target_dir)/$(package)-html.tar.gz 34## Using $(realpath ...) avoids problems with symlinks due to bug 35## #50994 in Octaves scripts/pkg/private/install.m. But at least the 36## release directory above is needed in the relative form, for 'git 37## archive --format=tar --prefix=$(release_dir). 38real_target_dir := $(realpath .)/$(target_dir) 39installation_dir := $(real_target_dir)/.installation 40package_list := $(installation_dir)/.octave_packages 41install_stamp := $(installation_dir)/.install_stamp 42 43## These can be set by environment variables which allow to easily 44## test with different Octave versions. 45ifndef OCTAVE 46OCTAVE := octave 47endif 48OCTAVE := $(OCTAVE) --no-gui --silent --norc 49MKOCTFILE ?= mkoctfile 50 51## Command used to set permissions before creating tarballs 52FIX_PERMISSIONS ?= chmod -R a+rX,u+w,go-w,ug-s 53 54HG := hg 55HG_CMD = $(HG) --config alias.$(1)=$(1) --config defaults.$(1)= $(1) 56HG_ID := $(shell $(call HG_CMD,identify) --id | sed -e 's/+//' ) 57HG_TIMESTAMP := $(firstword $(shell $(call HG_CMD,log) --rev $(HG_ID) --template '{date|hgdate}')) 58 59 60## Detect which VCS is used 61vcs := $(if $(wildcard .hg),hg,$(if $(wildcard .git),git,unknown)) 62ifeq ($(vcs),hg) 63release_dir_dep := .hg/dirstate 64endif 65ifeq ($(vcs),git) 66release_dir_dep := .git/index 67endif 68 69TAR_REPRODUCIBLE_OPTIONS := --sort=name --mtime="@$(HG_TIMESTAMP)" --owner=0 --group=0 --numeric-owner 70TAR_OPTIONS := --format=ustar $(TAR_REPRODUCIBLE_OPTIONS) 71 72## .PHONY indicates targets that are not filenames 73## (https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html) 74.PHONY: help 75 76## make will display the command before runnning them. Use @command 77## to not display it (makes specially sense for echo). 78help: 79 @echo "Targets:" 80 @echo " dist - Create $(release_tarball) for release." 81 @echo " html - Create $(html_tarball) for release." 82 @echo " release - Create both of the above and show md5sums." 83 @echo " install - Install the package in $(installation_dir), where it is not visible in a normal Octave session." 84 @echo " check - Execute package tests." 85 @echo " doctest - Test the help texts with the doctest package." 86 @echo " run - Run Octave with the package installed in $(installation_dir) in the path." 87 @echo " clean - Remove everything made with this Makefile." 88 89 90## 91## Recipes for release tarballs (package + html) 92## 93 94.PHONY: release dist html clean-tarballs clean-unpacked-release 95 96## To make a release, build the distribution and html tarballs. 97release: dist html 98 md5sum $(release_tarball) $(html_tarball) 99 @echo "Upload @ https://sourceforge.net/p/octave/package-releases/new/" 100 @echo " and note the changeset the release corresponds to" 101 102## dist and html targets are only PHONY/alias targets to the release 103## and html tarballs. 104dist: $(release_tarball) 105html: $(html_tarball) 106 107## An implicit rule with a recipe to build the tarballs correctly. 108%.tar.gz: % 109 $(TAR) -cf - $(TAR_OPTIONS) -C "$(target_dir)/" "$(notdir $<)" | gzip -9n > "$@" 110 111clean-tarballs: 112 @echo "## Cleaning release tarballs (package + html)..." 113 -$(RM) $(release_tarball) $(html_tarball) 114 @echo 115 116## Create the unpacked package. 117## 118## Notes: 119## * having ".hg/dirstate" (or ".git/index") as a prerequesite means it is 120## only rebuilt if we are at a different commit. 121## * the variable RM usually defaults to "rm -f" 122## * having this recipe separate from the one that makes the tarball 123## makes it easy to have packages in alternative formats (such as zip) 124## * note that if a commands needs to be run in a specific directory, 125## the command to "cd" needs to be on the same line. Each line restores 126## the original working directory. 127$(release_dir): $(release_dir_dep) 128 -$(RM) -r "$@" 129ifeq (${vcs},hg) 130 hg archive --exclude ".hg*" --type files "$@" 131endif 132ifeq (${vcs},git) 133 git archive --format=tar --prefix="$@/" HEAD | $(TAR) -x 134 $(RM) "$@/.gitignore" 135endif 136## Don't fall back to run the supposed necessary contents of 137## 'bootstrap' here. Users are better off if they provide 138## 'bootstrap'. Administrators, checking build reproducibility, can 139## put in the missing 'bootstrap' file if they feel they know its 140## necessary contents. 141ifneq (,$(wildcard src/bootstrap)) 142 cd "$@/src" && ./bootstrap && $(RM) -r "autom4te.cache" 143endif 144## Uncomment this if your src/Makefile.in has these targets for 145## pre-building something for the release (e.g. documentation). 146# cd "$@/src" && ./configure && $(MAKE) prebuild && \ 147# $(MAKE) clean && $(RM) Makefile 148## 149 $(MAKE) -C "$@" docs 150# cd "$@" && $(MAKE) tests 151 # remove dev stuff 152 cd "$@" && $(RM) -rf "devel" && $(RM) -f doc/mkfuncdocs.py 153 ${FIX_PERMISSIONS} "$@" 154 155.PHONY: docs 156docs: doc/$(package).pdf 157 158clean-docs: 159 $(RM) -f doc/$(package).info 160 $(RM) -f doc/$(package).pdf 161 $(RM) -f doc/functions.texi 162 163doc/$(package).pdf: doc/$(package).texi doc/functions.texi 164 cd doc && SOURCE_DATE_EPOCH=$(HG_TIMESTAMP) $(TEXI2PDF) $(package).texi 165 # remove temp files 166 cd doc && $(RM) -f $(package).aux $(package).cp $(package).cps $(package).fn $(package).fns $(package).log $(package).toc 167 168doc/functions.texi: 169 cd doc && ./mkfuncdocs.py --src-dir=../inst/ ../INDEX | $(SED) 's/@seealso/@xseealso/g' > functions.texi 170 171 172run_in_place = $(OCTAVE) --eval ' pkg ("local_list", "$(package_list)"); ' \ 173 --eval ' pkg ("load", "$(package)"); ' 174 175#html_options = --eval 'options = get_html_options ("octave-forge");' 176## Uncomment this for package documentation. 177html_options = --eval 'options = get_html_options ("octave-forge");' \ 178 --eval 'options.package_doc = "$(package).texi";' 179$(html_dir): $(install_stamp) 180 $(RM) -r "$@"; 181 $(run_in_place) \ 182 --eval ' pkg load generate_html; ' \ 183 $(html_options) \ 184 --eval ' generate_package_html ("$(package)", "$@", options); '; 185 $(FIX_PERMISSIONS) "$@"; 186 187clean-unpacked-release: 188 @echo "## Cleaning unpacked release tarballs (package + html)..." 189 -$(RM) -r $(release_dir) $(html_dir) 190 @echo 191 192## 193## Recipes for installing the package. 194## 195 196.PHONY: install clean-install 197 198octave_install_commands = \ 199' llist_path = pkg ("local_list"); \ 200 mkdir ("$(installation_dir)"); \ 201 load (llist_path); \ 202 local_packages(cellfun (@ (x) strcmp ("$(package)", x.name), local_packages)) = []; \ 203 save ("$(package_list)", "local_packages"); \ 204 pkg ("local_list", "$(package_list)"); \ 205 pkg ("prefix", "$(installation_dir)", "$(installation_dir)"); \ 206 pkg ("install", "-local", "-verbose", "$(release_tarball)"); ' 207 208## Install unconditionally. Maybe useful for testing installation with 209## different versions of Octave. 210install: $(release_tarball) 211 @echo "Installing package under $(installation_dir) ..." 212 $(OCTAVE) --eval $(octave_install_commands) 213 touch $(install_stamp) 214 215## Install only if installation (under target/...) is not current. 216$(install_stamp): $(release_tarball) 217 @echo "Installing package under $(installation_dir) ..." 218 $(OCTAVE) --eval $(octave_install_commands) 219 touch $(install_stamp) 220 221clean-install: 222 @echo "## Cleaning installation under $(installation_dir) ..." 223 -$(RM) -r $(installation_dir) 224 @echo 225 226 227## 228## Recipes for testing purposes 229## 230 231.PHONY: run doctest check 232 233## Start an Octave session with the package directories on the path for 234## interactice test of development sources. 235run: $(install_stamp) 236 $(run_in_place) --persist 237 238## Test example blocks in the documentation. Needs doctest package 239## https://octave.sourceforge.io/doctest/index.html 240doctest: $(install_stamp) 241 $(run_in_place) --eval 'pkg load doctest;' \ 242 --eval "targets = pkg('list', '$(package)'){1}.dir;" \ 243 --eval "doctest (targets);" 244 245## Test package. 246octave_test_commands = \ 247' pkgs = pkg("list", "$(package)"); \ 248 dirs = {pkgs{1}.dir}; \ 249 __run_test_suite__ (dirs, {}); ' 250## the following works, too, but provides no overall summary output as 251## __run_test_suite__ does: 252## 253## else cellfun (@runtests, horzcat (cellfun (@ (dir) ostrsplit (([~, dirs] = system (sprintf ("find %s -type d", dir))), "\n\r", true), dirs, "UniformOutput", false){:})); endif ' 254check: $(install_stamp) 255 $(run_in_place) --eval $(octave_test_commands) 256 257 258## 259## CLEAN 260## 261 262.PHONY: clean 263 264clean: clean-tarballs clean-unpacked-release clean-install clean-docs 265 test -e inst/test && rmdir inst/test || true 266 test -e fntests.log && rm -f fntests.log || true 267 @echo "## Removing target directory (if empty)..." 268 test -e $(target_dir) && rmdir $(target_dir) || true 269 @echo 270 @echo "## Cleaning done" 271 @echo 272 273.PHONY: tests 274 275CC_TST_SOURCES := $(shell $(GREP) --files-with-matches '^%!' src/*.cc) 276TST_SOURCES := $(patsubst src/%.cc,inst/test/%.cc-tst,$(CC_TST_SOURCES)) 277 278inst/test: 279 @mkdir -p "$@" 280 281$(TST_SOURCES): inst/test/%.cc-tst: src/%.cc | inst/test 282 @echo "Extracting tests from $< ..." 283 @$(RM) -f "$@" "$@-t" 284 @( echo "## Generated from $<"; \ 285 $(GREP) '^%!' "$<") > "$@" 286 287tests: $(TST_SOURCES) 288