#+TITLE: Building tools #+STARTUP: indent overview #+PROPERTY: header-args: :comments both This file contains all the tools needed to build the QMCkl library. * Helper functions #+NAME: header #+begin_src sh :tangle no :exports none :output none echo "This file was created by tools/Building.org" #+end_src #+NAME: check-src #+begin_src bash if [[ $(basename ${PWD}) != "src" ]] ; then echo "This script needs to be run in the src directory" exit -1 fi #+end_src #+NAME: url-issues : https://github.com/trex-coe/qmckl/issues #+NAME: url-web : https://trex-coe.github.io/qmckl #+NAME: license #+begin_example BSD 3-Clause License Copyright (c) 2020, TREX Center of Excellence All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #+end_example * Makefiles ** Makefile.in :PROPERTIES: :header-args: :tangle ../src/Makefile.in :noweb yes :comments org :END: This is the main Makefile invoked by the ~make~ command at the root of the package. To compile the sources, it calls the =Makefile= located in the =src= directory. This Makefile creates the source file from the org-mode file, as well as a Makefile, =Makefile.generated=, dedicated to the compilation of the sources. *** Header We want the Makefile to be POSIX-compliant, such that it works not only with GNU Make. #+begin_src makefile # <> .POSIX: #+end_src *** Compiler options Compiler variables are obtained from the configure script (see =configure.ac=) #+begin_src makefile CC = @CC@ FC = @FC@ CFLAGS = @CFLAGS@ FCFLAGS = @FCFLAGS@ LDFLAGS = @LDFLAGS@ DEFS = @DEFS@ #+end_src *** Variables #+begin_src makefile HAS_CPPCHECK = @HAS_CPPCHECK@ # VPATH-related substitution variables srcdir = @srcdir@ VPATH = @srcdir@ top_srcdir=$(srcdir)/.. shared_lib=$(top_srcdir)/lib/libqmckl.so static_lib=$(top_srcdir)/lib/libqmckl.a qmckl_h=$(top_srcdir)/include/qmckl.h qmckl_f=$(top_srcdir)/share/qmckl/fortran/qmckl_f.f90 export CC CFLAGS DEFS FC FCFLAGS LIBS top_srcdir ORG_SOURCE_FILES=$(wildcard $(srcdir)/*.org) C_SOURCE_FILES=$(patsubst %.org,%.c,$(ORG_SOURCE_FILES)) INCLUDE=-I$(top_srcdir)/include/ #+end_src *** Rules The source files are created during the generation of the file ~Makefile.generated~. The Makefile.generated is the one that will be distributed with the library. #+begin_src makefile .PHONY: clean shared static doc all check install uninstall .SECONDARY: # Needed to keep the produced C and Fortran files $(shared_lib) $(static_lib): $(qmckl_h) $(qmckl_f) Makefile.generated $(MAKE) -f Makefile.generated $@ install uninstall: Makefile.generated $(MAKE) -f Makefile.generated $@ $(qmckl_f) $(qmckl_h): Makefile.generated $(top_srcdir)/tools/build_qmckl_h.sh shared: $(shared_lib) static: $(static_lib) all: shared static doc check check: $(static_lib) $(MAKE) -f Makefile.generated check ifeq ($(HAS_CPPCHECK),1) cppcheck: cppcheck \ --addon=cert \ --enable=warning,style,performance,portability,information \ qmckl_*.c endif doc: $(ORG_SOURCE_FILES) $(top_srcdir)/tools/build_doc.sh clean: - $(MAKE) -f Makefile.generated clean - $(RM) test_qmckl_* test_qmckl.c \ $(qmckl_h) $(qmckl_f) \ qmckl_*.f90 qmckl_*.c qmckl_*.h \ Makefile.generated *.html *.txt veryclean: clean FORCE - $(RM) $(top_srcdir)/share/doc/qmckl/html/*.html \ $(top_srcdir)/share/doc/qmckl/text/*.txt Makefile.generated.in: Makefile $(top_srcdir)/tools/create_makefile.sh $(ORG_SOURCE_FILES) $(top_srcdir)/tools/Building.org $(top_srcdir)/tools/create_makefile.sh Makefile.generated: Makefile.generated.in cd .. ; ./config.status .SUFFIXES: .org .c .org.c: $(top_srcdir)/tools/tangle.sh $< #+end_src ** Script to generate auto-generated Makefile :PROPERTIES: :header-args: :tangle create_makefile.sh :noweb yes :shebang #!/bin/bash :comments org :END: This script generates the Makefile that compiles the library. The ~OUTPUT~ variable contains the name of the generated Makefile,typically =Makefile.generated=. #+begin_src bash # <> <> OUTPUT=Makefile.generated.in #+end_src We start by tangling all the org-mode files. #+begin_src bash ${top_srcdir}/tools/tangle.sh *.org ${top_srcdir}/tools/build_qmckl_h.sh #+end_src Then we create the list of ~*.o~ files to be created, for library functions: #+begin_src bash OBJECTS="qmckl_f.o" for i in $(ls qmckl_*.c qmckl_*f.f90) ; do FILE=${i%.*} OBJECTS+=" ${FILE}.o" done >> $OUTPUT #+end_src for tests in C: #+begin_src bash TESTS="" for i in $(ls test_qmckl_*.c) ; do FILE=${i%.c} TESTS+=" ${FILE}.o" done >> $OUTPUT #+end_src and for tests in Fortran: #+begin_src bash TESTS_F="" for i in $(ls test_qmckl_*_f.f90) ; do FILE=${i%.f90} TESTS_F+=" ${FILE}.o" done >> $OUTPUT #+end_src Finally, we append the variables to the Makefile #+begin_src bash :noweb yes cat << EOF > ${OUTPUT} .POSIX: .SUFFIXES: package = @PACKAGE_TARNAME@ version = @PACKAGE_VERSION@ # VPATH-related substitution variables srcdir = @srcdir@ VPATH = @srcdir@ prefix = @prefix@ CC = @CC@ DEFS = @DEFS@ CFLAGS = @CFLAGS@ -I\$(top_srcdir)/munit/ -I\$(top_srcdir)/include -I. CPPFLAGS = @CPPFLAGS@ LIBS = @LIBS@ FC = @FC@ FCFLAGS= @FCFLAGS@ OBJECT_FILES=$OBJECTS TESTS = $TESTS TESTS_F = $TESTS_F LIBS = @LIBS@ FCLIBS = @FCLIBS@ EOF export echo ' <> ' >> ${OUTPUT} #+end_src and the rules: #+NAME: rules #+begin_src makefile :tangle no top_srcdir=$(srcdir)/.. shared_lib=$(top_srcdir)/lib/libqmckl.so static_lib=$(top_srcdir)/lib/libqmckl.a qmckl_h=$(top_srcdir)/include/qmckl.h qmckl_f=$(top_srcdir)/share/qmckl/fortran/qmckl_f.f90 munit=$(top_srcdir)/munit/munit.c datarootdir=$(prefix)/share datadir=$(datarootdir) docdir=$(datarootdir)/doc/$(package) htmldir=$(docdir)/html libdir=$(prefix)/lib includedir=$(prefix)/include fortrandir=$(datarootdir)/$(package)/fortran shared: $(shared_lib) static: $(static_lib) all: shared static $(shared_lib): $(OBJECT_FILES) $(CC) -shared $(OBJECT_FILES) -o $(shared_lib) $(static_lib): $(OBJECT_FILES) $(AR) rcs $(static_lib) $(OBJECT_FILES) # Test qmckl_f.o: $(qmckl_f) $(FC) $(FCFLAGS) -c $(qmckl_f) -o $@ test_qmckl: test_qmckl.c $(qmckl_h) $(static_lib) $(TESTS) $(TESTS_F) $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) $(munit) $(TESTS) $(TESTS_F) \ $(static_lib) $(LIBS) $(FCLIBS) test_qmckl.c -o $@ test_qmckl_shared: test_qmckl.c $(qmckl_h) $(shared_lib) $(TESTS) $(TESTS_F) $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) \ -Wl,-rpath,$(top_srcdir)/lib -L$(top_srcdir)/lib $(munit) $(TESTS) \ $(TESTS_F) -lqmckl $(LIBS) $(FCLIBS) test_qmckl.c -o $@ check: test_qmckl test_qmckl_shared ./test_qmckl clean: $(RM) -- *.o *.mod $(shared_lib) $(static_lib) test_qmckl install: install -d $(DESTDIR)$(prefix)/lib install -d $(DESTDIR)$(prefix)/include install -d $(DESTDIR)$(prefix)/share/qmckl/fortran install -d $(DESTDIR)$(prefix)/share/doc/qmckl/html/ install -d $(DESTDIR)$(prefix)/share/doc/qmckl/text/ install $(shared_lib) $(DESTDIR)$(libdir)/ install $(static_lib) $(DESTDIR)$(libdir)/ install $(qmckl_h) $(DESTDIR)$(includedir) install $(qmckl_f) $(DESTDIR)$(fortrandir) install $(top_srcdir)/share/doc/qmckl/html/*.html $(DESTDIR)$(docdir)/html/ install $(top_srcdir)/share/doc/qmckl/html/*.css $(DESTDIR)$(docdir)/html/ install $(top_srcdir)/share/doc/qmckl/text/*.txt $(DESTDIR)$(docdir)/text/ uninstall: rm $(DESTDIR)$(libdir)/libqmckl.so rm $(DESTDIR)$(libdir)/libqmckl.a rm $(DESTDIR)$(includedir)/qmckl.h rm -rf $(DESTDIR)$(datarootdir)/$(package) rm -rf $(DESTDIR)$(docdir) .SUFFIXES: .c .f90 .o .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) -c $*.c -o $*.o .f90.o: qmckl_f.o $(FC) $(FCFLAGS) -c $*.f90 -o $*.o .PHONY: check cppcheck clean all #+end_src * Script to tangle the org-mode files :PROPERTIES: :header-args: :tangle tangle.sh :noweb yes :shebang #!/bin/bash :comments org :END: #+begin_src bash # <> <> #+end_src This file needs to be run from the QMCKL =src= directory. It tangles all the files in the directory. It uses the =config_tangle.el= file, which contains information required to compute the current file names using for example ~(eval c)~ to get the name of the produced C file. The file is not tangled if the last modification date of the org file is less recent than one of the tangled files. #+begin_src bash function tangle() { local org_file=$1 local c_file=${org_file%.org}.c local f_file=${org_file%.org}.f90 if [[ ${org_file} -ot ${c_file} ]] ; then return elif [[ ${org_file} -ot ${f_file} ]] ; then return fi emacs --batch ${org_file} --load=${top_srcdir}/tools/config_tangle.el -f org-babel-tangle } for i in $@ do echo "--- ${i} ----" tangle ${i} done #+end_src * Script to build the final qmckl.h file :PROPERTIES: :header-args:bash: :tangle build_qmckl_h.sh :noweb yes :shebang #!/bin/bash :comments org :END: #+begin_src bash :noweb yes # <> #+end_src #+NAME: qmckl-header #+begin_src text :noweb yes ------------------------------------------ QMCkl - Quantum Monte Carlo kernel library ------------------------------------------ Documentation : <> Issues : <> <> #+end_src All the produced header files are concatenated in the =qmckl.h= file, located in the include directory. The =*_private.h= files are excluded. Put =.h= files in the correct order: #+begin_src bash HEADERS="" for i in $(cat table_of_contents) do HEADERS+="${i%.org}_type.h " done for i in $(cat table_of_contents) do HEADERS+="${i%.org}_func.h " done #+end_src Generate C header file #+begin_src bash OUTPUT="${top_srcdir}/include/qmckl.h" cat << EOF > ${OUTPUT} /* ,* <> ,*/ #ifndef __QMCKL_H__ #define __QMCKL_H__ #include #include #include EOF for i in ${HEADERS} do if [[ -f $i ]] ; then cat $i >> ${OUTPUT} fi done cat << EOF >> ${OUTPUT} #endif EOF #+end_src Generate Fortran interface file from all =qmckl_*_fh.f90= files #+begin_src bash HEADERS_TYPE="qmckl_*_fh_type.f90" HEADERS="qmckl_*_fh_func.f90" OUTPUT="${top_srcdir}/share/qmckl/fortran/qmckl_f.f90" cat << EOF > ${OUTPUT} ! ! <> ! module qmckl use, intrinsic :: iso_c_binding EOF for i in ${HEADERS_TYPE} do cat $i >> ${OUTPUT} done for i in ${HEADERS} do cat $i >> ${OUTPUT} done cat << EOF >> ${OUTPUT} end module qmckl EOF #+end_src * Script to build the documentation :PROPERTIES: :header-args:bash: :tangle build_doc.sh :noweb yes :shebang #!/bin/bash :comments org :END: First define readonly global variables. #+begin_src bash :noweb yes readonly DOCS=${top_srcdir}/share/doc/qmckl/ readonly SRC=${top_srcdir}/src/ readonly HTMLIZE=${DOCS}/html/htmlize.el readonly CONFIG_DOC=${top_srcdir}/tools/config_doc.el readonly CONFIG_TANGLE=${top_srcdir}/tools/config_tangle.el #+end_src Check that all the defined global variables correspond to files. #+begin_src bash :noweb yes function check_preconditions() { if [[ -z ${top_srcdir} ]] then print "top_srcdir is not defined" exit 1 fi for dir in ${DOCS}/html ${DOCS}/text ${SRC} do if [[ ! -d ${dir} ]] then print "${dir} not found" exit 2 fi done for file in ${CONFIG_DOC} ${CONFIG_TANGLE} do if [[ ! -f ${file} ]] then print "${file} not found" exit 3 fi done } #+end_src ~install_htmlize~ installs the htmlize Emacs plugin if the =htmlize.el= file is not present. #+begin_src bash :noweb yes function install_htmlize() { local url="https://github.com/hniksic/emacs-htmlize" local repo="emacs-htmlize" [[ -f ${HTMLIZE} ]] || ( cd ${DOCS}/html git clone ${url} \ && cp ${repo}/htmlize.el ${HTMLIZE} \ && rm -rf ${repo} cd - ) # Assert htmlize is installed [[ -f ${HTMLIZE} ]] \ || exit 1 } #+end_src Extract documentation from an org-mode file. #+begin_src bash :noweb yes function extract_doc() { local org=$1 local local_html=${SRC}/${org%.org}.html local local_text=${SRC}/${org%.org}.txt local html=${DOCS}/html/${org%.org}.html local text=${DOCS}/text/${org%.org}.txt if [[ -f ${html} && ${org} -ot ${html} ]] then return fi emacs --batch \ --load ${HTMLIZE} \ --load ${CONFIG_DOC} \ ${org} \ --load ${CONFIG_TANGLE} \ -f org-html-export-to-html \ -f org-ascii-export-to-ascii mv ${local_html} ${DOCS}/html mv ${local_text} ${DOCS}/text } #+end_src The main function of the script. #+begin_src bash :noweb yes function main() { check_preconditions || exit 1 # Install htmlize if needed install_htmlize || exit 2 # Create documentation cd ${SRC} \ || exit 3 for i in *.org do echo echo "======= ${i} =======" extract_doc ${i} done if [[ $? -eq 0 ]] then cd ${DOCS}/html rm -f index.html ln README.html index.html exit 0 else exit 3 fi } main #+end_src