14 KiB
Building tools
- Helper functions
- Makefiles
- Script to tangle the org-mode files
- Script to build the final qmckl.h file
- Script to build the documentation
This file contains all the tools needed to build the QMCkl library.
Helper functions
if [[ $(basename ${PWD}) != "src" ]] ; then
echo "This script needs to be run in the src directory"
exit -1
fi
https://github.com/trex-coe/qmckl/issues
https://trex-coe.github.io/qmckl
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.
Makefiles
Makefile.in
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.
# <<header()>>
.POSIX:
Compiler options
Compiler variables are obtained from the configure script (see configure.ac
)
CC = @CC@
FC = @FC@
CFLAGS = @CFLAGS@
FCFLAGS = @FCFLAGS@
LDFLAGS = @LDFLAGS@
DEFS = @DEFS@
Variables
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/
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.
.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 $<
Script to generate auto-generated Makefile
This script generates the Makefile that compiles the library.
The OUTPUT
variable contains the name of the generated Makefile,typically
Makefile.generated
.
# <<header()>>
<<check_src>>
OUTPUT=Makefile.generated.in
We start by tangling all the org-mode files.
${top_srcdir}/tools/tangle.sh *.org
${top_srcdir}/tools/build_qmckl_h.sh
Then we create the list of *.o
files to be created, for library
functions:
OBJECTS="qmckl_f.o"
for i in $(ls qmckl_*.c qmckl_*f.f90) ; do
FILE=${i%.*}
OBJECTS+=" ${FILE}.o"
done >> $OUTPUT
for tests in C:
TESTS=""
for i in $(ls test_qmckl_*.c) ; do
FILE=${i%.c}
TESTS+=" ${FILE}.o"
done >> $OUTPUT
and for tests in Fortran:
TESTS_F=""
for i in $(ls test_qmckl_*_f.f90) ; do
FILE=${i%.f90}
TESTS_F+=" ${FILE}.o"
done >> $OUTPUT
Finally, we append the variables to the Makefile
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 '
<<rules>>
' >> ${OUTPUT}
and the rules:
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
Script to tangle the org-mode files
# <<header()>>
<<check_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.
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
Script to build the final qmckl.h file
# <<header()>>
------------------------------------------
QMCkl - Quantum Monte Carlo kernel library
------------------------------------------
Documentation : <<url-web()>>
Issues : <<url-issues()>>
<<license()>>
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:
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
Generate C header file
OUTPUT="${top_srcdir}/include/qmckl.h"
cat << EOF > ${OUTPUT}
/*
,* <<qmckl-header>>
,*/
#ifndef __QMCKL_H__
#define __QMCKL_H__
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
EOF
for i in ${HEADERS}
do
if [[ -f $i ]] ; then
cat $i >> ${OUTPUT}
fi
done
cat << EOF >> ${OUTPUT}
#endif
EOF
Generate Fortran interface file from all qmckl_*_fh.f90
files
HEADERS_TYPE="qmckl_*_fh_type.f90"
HEADERS="qmckl_*_fh_func.f90"
OUTPUT="${top_srcdir}/share/qmckl/fortran/qmckl_f.f90"
cat << EOF > ${OUTPUT}
!
! <<qmckl-header>>
!
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
Script to build the documentation
First define readonly global variables.
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
Check that all the defined global variables correspond to files.
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
}
install_htmlize
installs the htmlize Emacs plugin if the
htmlize.el
file is not present.
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
}
Extract documentation from an org-mode file.
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
}
The main function of the script.
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