Skip to content
Closed
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
36 changes: 31 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ DEPENDS := $(shell $(SED) -n -e 's/^[Dd]epends[^,]*, *\(.*\)/\1/p' $(DESCRIPTION
TARGET_DIR := target
RELEASE_DIR := $(TARGET_DIR)/$(PACKAGE)-$(VERSION)
RELEASE_TARBALL := $(TARGET_DIR)/$(PACKAGE)-$(VERSION).tar.gz
RELEASE_DIR_CI := $(TARGET_DIR)/$(PACKAGE)-$(VERSION)-ci
RELEASE_TARBALL_CI := $(RELEASE_DIR_CI).tar.gz
DOCS_HTML_DIR := docs
DOCS_DIR := doc
DOCS_DEV_DIR := devel/doc
Expand All @@ -69,7 +71,7 @@ DOCS_PDF := $(DOCS_DIR)/$(PACKAGE).pdf
DOCS_QCH := $(DOCS_DIR)/$(PACKAGE).qch
DOCS_LOGO := $(DOCS_DIR)/$(PACKAGE).svg

.PHONY: help dist docs-html docs release install all check check-ci check-local run clean
.PHONY: help dist docs-html docs release install all check check-ci check-local tarball-nodocs run clean

help:
@echo " "
Expand All @@ -88,6 +90,9 @@ help:
@echo " install - Install the package in GNU Octave"
@echo " all - Build all oct files"
@echo " check - Execute package tests (with install)"
@echo " check-ci - Build a doc-free tarball, install it, and run tests"
@echo " check-local - Run check-ci inside Docker via compose"
@echo " tarball-nodocs - Build a doc-free tarball without running tests"
@echo
@echo " clean - Remove releases, doc and oct files"
@echo " distclean - Remove releases, oct files and compiled libraries"
Expand Down Expand Up @@ -135,6 +140,26 @@ $(RELEASE_DIR): .git/index
@cd $@/$(SRC) && ./bootstrap && $(RM) -r "autom4te.cache"
@chmod -R a+rX,u+w,go-w "$@"

$(RELEASE_DIR_CI): .git/index
@echo "Creating CI package dist directory $@ ..."
@-$(RM) -r $@
@mkdir -p $@
@echo " git archive ..."
@GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0="$(abspath .)" \
git archive -o $@/tmp.tar HEAD
@cd $@ && tar -xf tmp.tar && $(RM) tmp.tar
@echo " copy slicot files ..."
@mkdir -p $@/$(SC)/$(SC_LAPACK)
@cp -t $@/$(SC)/$(SC_SRC) $(SC_SUBMOD)/$(SC_SRC)/*.f
@cp -t $@/$(SC)/$(SC_LAPACK) $(SC_SUBMOD)/$(SC_LAPACK)/*.f
@cp $(SC_SUBMOD)/LICENSE $@/$(SC_SRC)/../
@cp $(SC_SUBMOD)/README.md $@/$(SC_SRC)/../README-SLICOT.md
@cp $(SC_SUBMOD)/LICENSE $@/$(SC_DOC)/
@cp $(SC_SUBMOD)/README.md $@/$(SC_DOC)/README-SLICOT.md
@echo " bootstrap ..."
@cd $@/$(SRC) && ./bootstrap && $(RM) -r "autom4te.cache"
@chmod -R a+rX,u+w,go-w "$@"

docs-html:
@echo "Updating HTML documentation ... "
@cd $(DOCS_HTML_DIR) && $(OCTAVE) \
Expand Down Expand Up @@ -208,15 +233,16 @@ check: install
$(OCTAVE) --path "inst/" --path "src/" \
--eval 'pkg test control'

check-ci:
test -f $(SRC)/Makefile.conf || (cd $(SRC) && ./bootstrap && ./configure)
$(MAKE) -C $(SRC) all
check-ci: tarball-nodocs
$(OCTAVE) --no-gui --version
$(OCTAVE) --no-gui --path "inst/" --path "src/" devel/run_tests.m
$(OCTAVE) --no-gui --eval 'pkg install "$(RELEASE_TARBALL_CI)"'
$(OCTAVE) --no-gui devel/run_tests.m

check-local:
docker compose --file devel/compose.yaml --project-directory . run --rm octave

tarball-nodocs: $(RELEASE_TARBALL_CI)

clean:
$(RM) -r $(TARGET_DIR)
$(MAKE) -C $(SRC) clean
Expand Down
26 changes: 26 additions & 0 deletions ci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# CI Notes

## Why check-ci builds its own tarball

The canonical way to test an Octave package is `pkg install` followed by
`pkg test`. Both require an installable tarball, which `make dist` normally
produces.

`make dist` cannot run in the CI Docker image (`gnuoctave/octave`) because
the PDF documentation step fails. The `makeinfo --pdf` step invokes `pdfetex`,
which cannot include SVG images, and the manual references the project logo as
an SVG. No conversion toolchain (inkscape, rsvg-convert, etc.) is present in
the image.

The `check-ci` target works around this by building a doc-free tarball via the
`RELEASE_DIR_CI` make target. That target mirrors `RELEASE_DIR` (used by
`make dist`) but omits the three lines that copy the PDF, QCH, and logo into
the package directory.

## Git safe.directory in Docker

The CI container runs as root while the bind-mounted source tree is owned by
the host user. Git 2.35.2 and later refuse to operate on repositories owned
by a different user. The `RELEASE_DIR_CI` recipe passes the trust exception
via environment variables (`GIT_CONFIG_COUNT`, `GIT_CONFIG_KEY_0`,
`GIT_CONFIG_VALUE_0`) so no global git config file is modified.
60 changes: 6 additions & 54 deletions devel/run_tests.m
Original file line number Diff line number Diff line change
@@ -1,57 +1,9 @@
## Run embedded %!test blocks from source without installing the package.
##
## The canonical way (according to the docs I've found) to to actually test is
## to call 'pkg test control' but that is coupled to docs today because it
## requires you to install first, but to install you have to build a release
## tarball with dist which depends on docs. This is a "hack" to get around that
## and just run the tests directly. This should probably change in the future
## b/c searching and grepping through a bunch of src files to test is very
## awkward.
## Run embedded %!test blocks from the installed control package.
##
## Called by 'make check-ci'. Exit code is nonzero if any tests fail.

## Register autoloads from PKG_ADD directives (normally done by pkg install)

src_path = fullfile (fileparts (mfilename ("fullpath")), "..", "src");

## Find all .cc files in src/ b/c those are compiled to .oct files. We literally
## just want this out of the cpp files so that we know what .oct file we should
## target
## // PKG_ADD: autoload ("__sl_sb03md__", "__control_slicot_functions__.oct");
cc_files = glob (fullfile (src_path, "*.cc"));

## Read every file and grep of PKG_ADD, call autoload() on it with the absolute
## path to the .oct file. Autoload registers a deffered load which tells octave
## "when someone calls funcname, load filepath to find it, the .oct file isn't
## actually loaded until that first call"
for i = 1:numel (cc_files)
txt = fileread (cc_files{i});
## Use a regex to capture the 2 arguments (function name and .oct filename)
toks = regexp (txt, 'PKG_ADD: autoload \("(\w+)", "(\w+\.oct)"\)', "tokens");
for j = 1:numel (toks)
autoload (toks{j}{1}, fullfile (src_path, toks{j}{2}));
endfor
endfor

## Find all the class dirs to test ("inst", "inst/@tf", etc...)
dirs = [{"inst"}, cellstr(glob ("inst/@*"))'];

n_pass = 0;
n_total = 0;

## For every dir we collected, go through every mfile and call test
for i = 1:numel (dirs)
mfiles = dir (fullfile (dirs{i}, "*.m"));
for j = 1:numel (mfiles)
f = fullfile (mfiles(j).folder, mfiles(j).name);
## collect output args from test call
## https://docs.octave.org/v11.1.0/Test-Functions.html
[p, total, xfail, xbug, skip, rtskip] = test (f, "quiet", stdout);
n_pass += p;
n_total += total - xfail - xbug - skip - rtskip;
endfor
endfor

n_fail = n_total - n_pass;
fprintf ("\nTotal: %d passed, %d failed\n", n_pass, n_fail);
exit (n_fail > 0);
pkg load control;
info = pkg ("list", "control"){1};
dirs = [{info.dir}, cellstr(glob (fullfile (info.dir, "@*")))'];
[~, nfail] = __run_test_suite__ (dirs, {});
exit (nfail > 0);