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