# Makefile for beastie # # Important functional targets: # # all : the program # check : build the program and run tests # docs : build documentation in doc/ # clean : tidy up # # and interesting executable targets: # # beastie : the program # repl : a link to `beastie`; calling ./repl is similar to `beastie -r` # lex-aux,... : programs lex-bib2, lex-bst, lex-authors, ... # (one per .lex file, plus lex-bib2.c) will lex an input file # or a string argument and list the lexemes, for the sake # of debugging or curiosity. # # The _distributed_ Makefile.in is edited slightly, compared to the # repository one, so that it doesn't contain the 'dist' target. # # Some of the source files -- specifically util-extra.c and mycu.c -- # are generated from the corresponding .in files using # scheme-macro-filter.scm and the bare-bones program beastie0. # # SPDX-FileCopyrightText: 2023 Norman Gray # SPDX-License-Identifier: BSD-2-Clause # Copyright years and repo URL are set in configure.ac # The 'install' target installs things here prefix=@prefix@ bindir=$(prefix)/bin mandir=$(prefix)/share/man/man1 # there is a parse-mdblock lexer, but no parser, therefore mdblock is # not listed here, but separately in LIBOBJS below PARSERS=authors aux bst fmtstring markdown mdinline json SRC= beastie.c beastie0.c core.c s7.c util-extra.c.in util.c \ beastie.h core.h version.h s7.h util.h util-extra.h yycommon.h \ unicode.c unicode.h uniprops.h unidefs.h misc/unicode/ucd/ucd_version.h \ unicode-scm.c unicode-scm.h \ authors.scm bibtex.scm bst.scm config.scm.in \ parse-mdblock.lex parse-mdblock.h lex-bib2.c lex-bib2.h \ klipspringer.scm unicode.scm \ parse-aux.scm parse-markdown.scm parse-mdblock.scm \ parse-subtex.scm parse-bib2.scm parse-json.scm \ runtime.scm xexpr.scm \ scheme-macro-filter.scm scheme-utils.scm readermacros.scm readermacros.inc \ $(PARSERS:%=parse-%.lex) \ $(PARSERS:%=parse-%.y) \ $(PARSERS:%=parse-%.h) GEN_C=util-extra.c HAVE_ICU=@HAVE_ICU@ ifeq ($(HAVE_ICU), YES) SRC += icu-shim.c else GEN_C += mycu.c endif DISTFILES= $(SRC) mycu.c.in icu-shim.c depend.mk \ configure.ac configure config.h.in version.sh.in Makefile.in doc/Makefile.in \ bison2.sh release-notes.md LICENCE.txt # distfiles which are edited during distribution DISTFILES_MOD=README.md # This list doesn't include core.o, for no terribly good reason beyond # it being essentially part of beastie.c, rather than library code as such. # # But, hmm: there's now an inconsistent mix of C code in core.o, which # I think of as somehow different from this, and C code in # unicode-scm.o and parse-mdblock.o. LIBOBJS=util.o $(GEN_C:.c=.o) unicode.o unicode-scm.o \ parse-mdblock.o s7.o lex-bib2.o \ $(PARSERS:%=parse-%.o) $(PARSERS:%=parse-%.tab.o) ifeq ($(HAVE_ICU), YES) LIBOBJS += icu-shim.o else LIBOBJS += mycu.o endif CC=@CC@ LIBS=@LIBS@ # clang (possibly v14+) warns about variables set but never used, # which happens in .tab.c files. # (the -Wno-unused-but-set-variable option turns off a warning about # a variable being set but not used, in generated Bison code; # this option seems to be acceptable to both clang and gcc, # but not older version of clang CPPFLAGS=@CPPFLAGS@ -MMD CFLAGS=@CFLAGS@ -Wall -std=c99 LDFLAGS=@LDFLAGS@ NOUNUSEDCFLAG=@NOUNUSEDCFLAG@ FLEX=@FLEX@ BISON_ARGS= #BISON_ARGS=--report=state #BISON_ARGS=--report=state -Wcounterexamples BISON = @BISON@ -t -d $(BISON_ARGS) REDTEXT=printf " %s \n" all: beastie beastie: beastie.o core.o libbeastie.a $(CC) -o $@ -g $(LDFLAGS) beastie.o core.o -L. -lbeastie $(LIBS) # If beastie is started with the program name 'repl', then it starts # in REPL mode by default. This is a basic REPL. If BEASTIE_LOAD_PATH # is pointing to a colon-separated list of directories, in one of # which is libc_s7.so (see below), then this library is loaded and we # get a better repl. But this one is good enough for Emacs. repl: beastie rm -f repl ln -s beastie repl libbeastie.a: $(LIBOBJS) ar rv $@ $? ranlib $@ parse-%.tab.c parse-%.tab.h: parse-%.y $(BISON) --name-prefix=$* $< parse-%.tab.o: parse-%.tab.c $(CC) $(CPPFLAGS) $(CFLAGS) $(NOUNUSEDCFLAG) -c -o $@ $< parse-%.c: parse-%.lex $(FLEX) -o $@ $< # The programs lex-bib2, lex-bst, lex-authors, ... (one per .lex file, # plus lex-bib2.c) will lex an input file or a string argument and # list the lexemes. # # Most of these are built from a flex-based .lex, and a yacc/bison .y source. build/lex-%: build/lex-%.o libbeastie.a $(CC) -o $@ -g $< -L. -lbeastie $(LDFLAGS) $(LIBS) build/lex-%.o: parse-%.c parse-%.tab.c test -d build || mkdir build $(CC) $(CPPFLAGS) $(CFLAGS) -c -g -o $@ -DWITH_MAIN=1 $< # parse-mdblock doesn't have a corresponding .y grammar, so no .tab.c build/lex-mdblock.o: parse-mdblock.c test -d build || mkdir build $(CC) $(CPPFLAGS) $(CFLAGS) -c -g -o $@ -DWITH_MAIN=1 $< # bib2 has neither a .lex nor a .y source # (there was once a parse-bib.{lex,y}, hence the '2', here) build/lex-bib2: build/lex-bib2-main.o libbeastie.a $(CC) -o $@ -g $< -L. $(LDFLAGS) -lbeastie $(LIBS) build/lex-bib2-main.o: lex-bib2.c test -d build || mkdir build $(CC) $(CPPFLAGS) $(CFLAGS) -c -g -o $@ -DWITH_MAIN=1 $< # The following is how to build a bison parser which reports state in .output # (this is retained here only for reference). # See also -Wcounterexamples in recent versions of Bison. # parse-aux.tab.c parse-aux.tab.h: parse-aux.y # bison -d --name-prefix=aux --report=state -t parse-aux.y # parse-aux: parse-aux.tab.c parse-aux.o util.o s7.o # $(CC) -o $@ -DWITH_MAIN=1 parse-aux.tab.c parse-aux.o util.o s7.o # There is code in scheme-macro-filter.scm which generates a dependency file # util-extra.c.d. util-extra.c: util-extra.c.in beastie0 scheme-macro-filter.scm ./beastie0 scheme-macro-filter.scm $< $@ # To turn off debugging support in s7.o (faster), # simply comment this out and use the default rule. @S7DEBUGGING@s7.o: s7.c @S7DEBUGGING@ $(CC) $(CFLAGS) -DS7_DEBUGGING -c -o $@ $< # Coverage support: GCOV1 is enabled when ./configure is run without # arguments, and GCOV2 when it's run with --enable-altgcov # # The following may be mildly specific to llvm gcov, but is at least # close to the usage of the GCC gcov tool. # # Before building beastie-gcov, do `make; rm *.o *.gcovo`. # # The usual way to do this is as below. Then # # % (cd test; make BEASTIE=../heastie-gcov test)`, # % gcov foo.c # # That produces `foo.c.gcov`, which is supposed to add up the number # of times each line of foo.c was called, but... doesn't seem to in # fact. Bah: this is wrong, but don't sweat it now. At least I have # finally worked out how to build these. # @GCOV1@%.gcno: %.c @GCOV1@ $(CC) --coverage -c -O0 -o $*.o $< @GCOV1@beastie-gcov: beastie.gcno core.gcno $(LIBOBJS:.o=.gcno) @GCOV1@ $(CC) --coverage -o $@ beastie.o core.o $(LIBOBJS) # # The above doesn't work when s7 terminates in an # abrupt way which means that the gcda data, done the usual way, isn't # written. This is (I think) because s7.c chooses to terminate using # _exit(2) rather than exit(3). # # So we do it the alternative way, using llvm-profdata: the # %c in the LLVM_PROFILE_FILE is important, since it triggers a mode # where this is streamed directly to beastie.profraw. # # See https://clang.llvm.org/docs/SourceBasedCodeCoverage.html # # Build beastie.profraw as below, then # # PATH="$XPATH:$PATH" llvm-profdata merge -sparse beastie.profraw -o beastie.profdata # PATH="$XPATH:$PATH" llvm-cov show ./beastie-gcov -instr-profile=beastie.profdata >beastie.gcov # # Alternatively, use `llvm-cov report`. It may be necessary to # rebuild beastie0 with `cc -fprofile-instr-generate -o beastie0 # beastie0.o s7.o`, for some dependency-related reason I haven't got # to the bottom of. # # macOS note: on macOS it's necessary to explicitly place llvm-cov in # the path, as here (XPATH is the path to the Xcode tools, such as # /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin; # it slightly unexpectedly isn't in the path by default). I'd expect # it to be OK to add this directory to the PATH before building, but # it seems not, and the cc then in the path needs some extra # environment variables, which... I'll work out some other time. # # The .gcovo file extension is a dummy one -- that is, the useful # product of this rule is the .o file, and the .gcovo is just for # dependency tracking. @GCOV2@%.gcovo: %.c @GCOV2@ $(CC) -fprofile-instr-generate -fcoverage-mapping -c -O0 -o $*.o $< @GCOV2@ touch $@ @GCOV2@beastie-gcov: beastie.gcovo core.gcovo $(LIBOBJS:.o=.gcovo) @GCOV2@ $(CC) -fprofile-instr-generate -o $@ beastie.o core.o $(LIBOBJS) @GCOV2@beastie.profraw: beastie-gcov @GCOV2@ LLVM_PROFILE_FILE='%cbeastie.profraw' BEASTIE_LOAD_PATH=$$PWD/test ./beastie-gcov test/test-misc.scm # The targets for version.h and version.sed are OMITTED from the # Makefile.in included in the distribution tarball. # This is in order to freeze the version number in the distributed version.h. # See the source repository. include depend.mk # when (occasionally) rebuilding depend.mk, do so in the nonicu case depend.mk: @echo "# GENERATED FILE" >$@ @echo "# This file is part of Beastie " >>$@ @echo "# SPDX-FileCopyrightText: 2024 Norman Gray " >>$@ @echo "# SPDX-License-Identifier: BSD-2-Clause" >>$@ cat *.d >>$@ # the following line will remove all of the absolute paths in the depend files, # but it isn't necessary, if this is rebuilt in the nonicu case # sed -e 's,/[^ ]*,,' -e '/^ *\\/d' *.d >>$@ ### REUSE-IgnoreEnd # See https://tkurtbond.github.io/posts/2020/08/03/compiling-s7-scheme-on-macos/ # then ./s7_repl libc.scm to build libc_s7.so # ...but this has to be done in the s7 distribution directory. # This target exists for maintenance and debugging: # the s7 source is _not_ routinely downloaded during building S7DIR=s7 s7repl: $(S7DIR)/s7repl $(S7DIR)/libc_s7.so $(S7DIR)/s7repl: $(S7DIR)/s7.c cd $(S7DIR); $(CC) s7.c -o s7repl -DWITH_MAIN -I. -O2 -g -ldl -lm -Wl,-dynamic $(S7DIR)/s7.o: $(S7DIR)/s7.c cd $(S7DIR); $(CC) -c -g -o s7.o s7.c $(S7DIR)/libc_s7.so: $(S7DIR)/s7repl $(S7DIR)/s7.o cd $(S7DIR); ./s7repl libc.scm # s7.tar.gz expands into s7/ s7/s7.c: s7.tar.gz tar xmzf s7.tar.gz s7.tar.gz: curl -O https://ccrma.stanford.edu/software/s7/s7.tar.gz mycu.c: mycu.c.in beastie0 misc/unicode/ucd/UnicodeData.txt ./beastie0 scheme-macro-filter.scm mycu.c.in >$@ core0.o: core.c $(CC) -c $(CPPFLAGS) $(CFLAGS) -MF ${@:.o=.d} -DALL_FUNCTIONS=0 -o $@ $< unicode-scm0.o: unicode-scm.c $(CC) -c $(CPPFLAGS) $(CFLAGS) -MF ${@:.o=.d} -DALL_FUNCTIONS=0 -o $@ $< beastie0.o: readermacros.inc version.h # beastie0 is a version of beastie which uses no utility functions, # and only a cut-down runtime. It's used for bootstrapping # some parts of the compiled source. beastie0: beastie0.o core0.o util.o s7.o unicode-scm0.o unicode.o $(CC) -o $@ $(LDFLAGS) beastie0.o core0.o unicode-scm0.o util.o unicode.o s7.o $(LIBS) ### Non-ICU testing # # We also want to build a non-ICU version of the code, # when the configured version is the ICU version, # to confirm that this also still works. nonicu/%: % nonicu/Makefile.in cp $< $@ # nonicu/Makefile.in is also the target which creates the nonicu directory nonicu/Makefile.in: Makefile.in test -d nonicu || mkdir -p nonicu mkdir nonicu/doc nonicu/test nonicu/examples sed -e '/^version.sed:/,/^$$/d' -e '/^version.h:/,/^$$/d' Makefile.in >$@ nonicu/examples/Makefile: nonicu/Makefile.in cp -R examples nonicu nonicu/misc/unicode/ucd/ucd_version.h: misc/unicode/ucd/ucd_version.h nonicu/Makefile.in cp -R misc nonicu nonicu/test: test/build/dist-test.tar nonicu/Makefile.in test -d nonicu/test || mkdir nonicu/test cd nonicu/test; tar xf ../../test/build/dist-test.tar nonicu/config.h: $(DISTFILES:%=nonicu/%) nonicu/test cd nonicu; ./configure --without-icu nonicu/beastie: nonicu/config.h nonicu/examples/Makefile @echo "\n Building a non-ICU version... " cd nonicu; make -j ### Assemble the distribution(s) docs: doc/dist/beastie.xhtml doc/dist/beastie.xhtml: beastie doc/beastie.md cd doc; $(MAKE) dist/beastie.xhtml test/build/dist-test.tar: test cd test; $(MAKE) build/dist-test.tar examples/build/dist-examples.tar: examples cd examples; $(MAKE) build/dist-examples.tar doc/build/dist-doc.tar: doc cd doc; $(MAKE) build/dist-doc.tar # Distribution: the targets here produce a 'distribution kit' in build/, # with pre-built documentation, and a slightly cut-down Makefile.in which # doesn't include the targets version.{h,sed}. # # The dist target depends on version.sed, which isn't included in the # version of this file included in the distribution tarball. # This is semi-deliberate, since I want to discourage any # distributions happening from such a tarball. DISTLABEL=use-make-dist-XXX dist: version.sed d=`echo beastie-@BEASTIE_VERSION@ | sed -f version.sed`; \ $(MAKE) DISTLABEL=$$d build/dist-manifest # CTAN wants the unpacked directory not to contain a version number, # but to be just beastie/ dist-ctan: version.sed $(MAKE) DISTLABEL=beastie build/dist-manifest # the CI in .gitlab-ci.yml uses this file build/dist-manifest: build/$(DISTLABEL).tar.gz build/$(DISTLABEL).zip for f in $(^:build/%=%); do echo $$f; done >$@ # The macOS tar has a --no-xattrs option, which stops it inserting # extended attributes, it finds in files it's bundling, and the same # behaviour is triggered by the (famously undocumented) # COPYFILE_DISABLE=1 environment variable. # I expect in fact to build distribution tarballs during CI, so there # may be no need for this, but include it anyway, just in case. build/$(DISTLABEL).tar.gz: build/$(DISTLABEL)/Makefile.in cd build; \ COPYFILE_DISABLE=1 tar -cf $(DISTLABEL).tar $(DISTLABEL); \ gzip --best $(DISTLABEL).tar build/$(DISTLABEL).zip: build/$(DISTLABEL)/Makefile.in cd build; zip -q -r $(DISTLABEL).zip $(DISTLABEL) # Assemble the distribution. # The contents of test, examples, and doc are assembled into tarballs # by Makefiles in the corresponding directories -- this avoids us # inadvertently including built files from those directories. # # We include doc/dist, so that the distributed tarball has readable # docs immediately, without the recipient having to build them. build/$(DISTLABEL)/Makefile.in: $(DISTFILES) $(DISTFILES_MOD) \ doc/dist/beastie.xhtml version.sed \ test/build/dist-test.tar examples/build/dist-examples.tar doc/build/dist-doc.tar mkdir -p build/$(DISTLABEL) tar cf - $(DISTFILES) | (cd build/$(DISTLABEL); tar xBpf -) for f in $(DISTFILES_MOD); do sed -f version.sed $$f >build/$(DISTLABEL)/$$f; done top=`pwd`; for d in test examples doc; do \ mkdir -p build/$(DISTLABEL)/$$d; \ (cd build/$(DISTLABEL)/$$d; tar xf $$top/$$d/build/dist-$$d.tar); \ done cp -R misc LICENSES build/$(DISTLABEL) cp -R doc/dist build/$(DISTLABEL)/doc echo @PACKAGE_VERSION@ >build/$(DISTLABEL)/VERSION sed '/^#NODIST/,/^#DIST/d' Makefile.in >build/$(DISTLABEL)/Makefile.in # Attempt to build and test the distribution tarball dist-check: dist cd build; \ d=$$PWD; tar=`grep tar.gz dist-manifest`; name=`basename $$tar .tar.gz`\ mkdir install-test-build; cd install-test-build \ && tar xzf $$d/$$tar && cd * \ && ./configure --prefix=$$d/install-test-local \ && make -j check # The install target is very crude! install: beastie test -d "$(bindir)" || { echo "Create $(bindir) first" >&2; false; } test -d "$(mandir)" || { echo "Create $(mandir) first" >&2; false; } install -m 755 beastie "$(bindir)" install -m 644 doc/beastie.1 "$(mandir)" # If we have ICU, then check the non-ICU case as well ifeq ($(HAVE_ICU), YES) check: check0 check-nonicu else check: check0 endif check-nonicu: nonicu/beastie @echo " Repeating the tests for the non-ICU case... " cd nonicu; $(MAKE) check @echo " ...non-ICU check OK" check0: beastie cd test; $(MAKE) check cd examples; $(MAKE) check Makefile: ./config.status Makefile.in ./config.status Makefile config.h config.scm: ./config.status ./config.status touch config.h config.scm version.sh: ./config.status version.sh.in ./config.status version.sh clean: rm -f beastie beastie0 repl \ *.o *.d \ $(PARSERS:%=parse-%.c) \ $(PARSERS:%=parse-%.tab.c) \ $(PARSERS:%=parse-%.tab.h) \ $(PARSERS:%=lex-%) \ $(GEN_C) \ libbeastie.a libc_s7.* rm -Rf build # these directory tests are redundant _except_ when running this in the nonicu case if test -d doc; then cd doc; $(MAKE) clean; fi if test -d test; then cd test; $(MAKE) clean; fi if test -d examples; then cd examples; $(MAKE) clean; fi if test -d nonicu; then rm -Rf nonicu; fi