diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9b36c716..0e846ea1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -125,6 +125,12 @@ make check-local This pulls the latest `gnuoctave/octave` image and runs the full test suite inside it, matching the CI environment. Docker must be installed and running. +`check-ci` (and therefore `check-local`) packages your working tree with `git +stash create`, so uncommitted edits to already tracked files are picked up and +tested automatically. New files must be staged with `git add` before running the +tests, or they will be left out of the package. Neither case requires an actual +commit. + ### Pull request When your changes are finished, commit and push the change to your forked repository on Github (make sure your fork is up to date) and create a pull request. diff --git a/Makefile b/Makefile index b82e8029..5d67e953 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -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 " " @@ -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" @@ -106,6 +111,20 @@ help: @echo "Create $@ ..." @tar -c -f - --posix -C "$(TARGET_DIR)/" "$(notdir $<)" | gzip -9n > "$@" +define COPY_SLICOT_AND_BOOTSTRAP + @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 "$@" +endef + # Create the directory structure of the destributed archive file. # For this, archive the current git repo with some exceptions given # in .gitattributes with export-ignore and untar it. Then, copy the @@ -123,17 +142,19 @@ $(RELEASE_DIR): .git/index @cp $(DOCS_PDF) $@/$(DOCS_DIR)/ @cp $(DOCS_QCH) $@/$(DOCS_DIR)/ @cp $(DOCS_LOGO) $@/$(DOCS_DIR)/ - @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 "$@" + $(COPY_SLICOT_AND_BOOTSTRAP) + +$(RELEASE_DIR_CI): .git/index + @echo "Creating CI package dist directory $@ ..." + @-$(RM) -r $@ + @mkdir -p $@ + @echo " git archive ..." + @REV=$$(GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0="$(abspath .)" \ + git stash create); \ + GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0="$(abspath .)" \ + git archive -o $@/tmp.tar $${REV:-HEAD} + @cd $@ && tar -xf tmp.tar && $(RM) tmp.tar + $(COPY_SLICOT_AND_BOOTSTRAP) docs-html: @echo "Updating HTML documentation ... " @@ -208,15 +229,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 diff --git a/devel/run_tests.m b/devel/run_tests.m index 79fae7c9..e2de2fa1 100644 --- a/devel/run_tests.m +++ b/devel/run_tests.m @@ -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}; +[~, nfail] = __run_test_suite__ (dirs, {}); +exit (nfail > 0);