From 4d1bdb47413ce48de6bbf5d10b6f3b593a6cc9a5 Mon Sep 17 00:00:00 2001 From: Axel Kohlmeyer Date: Sat, 23 Apr 2022 13:52:12 -0400 Subject: [PATCH] add python tool to detect and remove (if desired) error docs from headers also document the tools in the manual. --- doc/src/Tools.rst | 35 ++++--- src/Makefile | 8 ++ tools/README | 1 - tools/cmake/cmbuild | 69 -------------- tools/coding_standard/README | 4 +- tools/coding_standard/errordocs.py | 148 +++++++++++++++++++++++++++++ 6 files changed, 179 insertions(+), 86 deletions(-) delete mode 100755 tools/cmake/cmbuild create mode 100644 tools/coding_standard/errordocs.py diff --git a/doc/src/Tools.rst b/doc/src/Tools.rst index f24659b278..558824a81b 100644 --- a/doc/src/Tools.rst +++ b/doc/src/Tools.rst @@ -87,7 +87,7 @@ Miscellaneous tools .. table_from_list:: :columns: 6 - * :ref:`CMake ` + * :ref:`LAMMPS coding standards ` * :ref:`emacs ` * :ref:`i-pi ` * :ref:`kate ` @@ -189,27 +189,32 @@ for the :doc:`chain benchmark `. ---------- -.. _cmake: +.. _coding_standard: -CMake tools ------------ +LAMMPS coding standard +---------------------- -The ``cmbuild`` script is a wrapper around using ``cmake --build ---target`` and allows compiling LAMMPS in a :ref:`CMake build folder -` with a make-like syntax regardless of the actual build -tool and the specific name of the program used (e.g. ``ninja-v1.10`` or -``gmake``) when using ``-D CMAKE_MAKE_PROGRAM=``. +The ``coding_standard`` folder contains multiple python scripts to +check for and apply some LAMMPS coding conventions. The following +scripts are available: .. parsed-literal:: - Usage: cmbuild [-v] [-h] [-C ] [-j ] [] + permissions.py # detects if sources have executable permissions and scripts have not + whitespace.py # detects TAB characters and trailing whitespace + homepage.py # detects outdated LAMMPS homepage URLs (pointing to sandia.gov instead of lammps.org) + errordocs.py # detects deprecated error docs in header files - Options: - -h print this message - -j allow processing of NUM concurrent tasks - -C DIRECTORY execute build in folder DIRECTORY - -v produce verbose output +The tools need to be given the main folder of the LAMMPS distribution +or individual file names as argument and will by default check them +and report any non-compliance. With the optional ``-f`` argument the +corresponding script will try to change the non-compliant file(s) to +match the conventions. +For convenience this scripts can also be invoked by the make file in +the ``src`` folder with, `make check-whitespace` or `make fix-whitespace` +to either detect or edit the files. Correspondingly for the other python +scripts. `make check` will run all checks. ---------- diff --git a/src/Makefile b/src/Makefile index 1b0992ab95..717c0cf772 100644 --- a/src/Makefile +++ b/src/Makefile @@ -472,6 +472,8 @@ tar: @cd STUBS; $(MAKE) @echo "Created $(ROOT)_src.tar.gz" +check: check-whitespace check-permissions check-homepage check-errordocs + check-whitespace: $(PYTHON) ../tools/coding_standard/whitespace.py .. @@ -490,6 +492,12 @@ check-homepage: fix-homepage: $(PYTHON) ../tools/coding_standard/homepage.py .. -f +check-errordocs: + $(PYTHON) ../tools/coding_standard/errordocs.py .. + +fix-errordocs: + $(PYTHON) ../tools/coding_standard/errordocs.py .. -f + format-src: clang-format -i --verbose --style=file *.cpp *.h */*.cpp */*.h diff --git a/tools/README b/tools/README index eb329e27cd..22e50b55fe 100644 --- a/tools/README +++ b/tools/README @@ -16,7 +16,6 @@ amber2lmp python scripts for using AMBER to setup LAMMPS input binary2txt convert a LAMMPS dump file from binary to ASCII text ch2lmp convert CHARMM files to LAMMPS input chain create a data file of bead-spring chains -cmake tools and scripts for use with CMake coding_standard python scripts to detect and fix some LAMMPS conventions colvars post-process output of the fix colvars command createatoms generate lattices of atoms within a geometry diff --git a/tools/cmake/cmbuild b/tools/cmake/cmbuild deleted file mode 100755 index f9e0f87390..0000000000 --- a/tools/cmake/cmbuild +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash -# make like wrapper around "cmake --build" -# (c) 2020 Axel Kohlmeyer -# This file is in the public domain - -WORKDIR="${PWD}" -PARAMS="" -MYARGS="" - -usage() -{ - echo "Usage: cmbuild [-v] [-h] [-C ] [-j ] []" >&2 -} - -help() -{ - usage - cat >&2 < allow processing of NUM concurrent tasks - -C DIRECTORY execute build in folder DIRECTORY - -v produce verbose output -EOF -} - -while (( "$#" )); do - case "$1" in - -C) - WORKDIR="$2" - shift 2 - ;; - -v) - MYARGS="${MYARGS} -v" - shift - ;; - -h) - help - exit 2 - ;; - -j) - MYARGS="${MYARGS} -j $2" - shift 2 - ;; - --) - shift - break - ;; - -*) - echo "Error: Unsupported flag $1" >&2 - echo - usage - exit 1 - ;; - *) - PARAMS="${PARAMS} $1" - shift - ;; - esac -done - -if [ ! -f "${WORKDIR}/CMakeCache.txt" ] ; then - echo "Must execute in a CMake build directory or use -C flag to select one" >&2 - exit 3 -fi - -eval set -- "${PARAMS} $@" -exec cmake --build "${WORKDIR}" ${MYARGS} --target "$@" diff --git a/tools/coding_standard/README b/tools/coding_standard/README index d0cd2872df..b5e483f38e 100644 --- a/tools/coding_standard/README +++ b/tools/coding_standard/README @@ -2,4 +2,6 @@ These Python scripts help to detect whether files in the repository conform to LAMMPS coding conventions or not, and can fix those issues, too. permissions.py detects if sources have executable permissions and scripts have not -whitespace.py (currently) detects trailing whitespace +whitespace.py detects TAB characters and trailing whitespace +homepage.py detects outdated LAMMPS homepage URLs (pointing to sandia.gov instead of lammps.org) +errordocs.py detects deprecated error docs in header files diff --git a/tools/coding_standard/errordocs.py b/tools/coding_standard/errordocs.py new file mode 100644 index 0000000000..07c0064e6c --- /dev/null +++ b/tools/coding_standard/errordocs.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +# Utility for detecting leftover error/warning docs in header files +# +# Written by Axel Kohlmeyer (Temple University) +from __future__ import print_function +import sys + +if sys.version_info.major < 3: + sys.exit('This script must be run with Python 3.5 or later') + +if sys.version_info.minor < 5: + sys.exit('This script must be run with Python 3.5 or later') + +import os +import glob +import re +import yaml +import argparse +import shutil + +DEFAULT_CONFIG = """ +recursive: true +include: + - src/** +patterns: + - "*.h" +""" + +def check_errordocs(f): + pattern = re.compile(r'^ */\* *ERROR/WARNING messag') + lineno = 1 + errors = set() + + for line in f: + if pattern.match(line): + errors.add(lineno) + lineno += 1 + + return errors + +def check_file(path): + if path.find('errordocs.py') >= 0: return { 'errordocs_errors' : '' } + encoding = 'UTF-8' + errordocs_errors = set() + try: + with open(path, 'r') as f: + errordocs_errors = check_errordocs(f) + except UnicodeDecodeError: + encoding = 'ISO-8859-1' + try: + with open(path, 'r', encoding=encoding) as f: + errordocs_errors = check_errordocs(f) + except Exception: + encoding = 'unknown' + + return { + 'errordocs_errors': errordocs_errors, + 'encoding': encoding + } + +def fix_file(path, check_result): + if path.find('errordocs.py') >= 0: return + newfile = path + ".modified" + pattern = re.compile(r'[\n\r ]* */\* *ERROR/WARNING *messag.*\*/', re.DOTALL) + with open(newfile, 'w', encoding='UTF-8') as out: + with open(path, 'r', encoding=check_result['encoding']) as src: + filetxt = re.sub(pattern,'', src.read()); + print(filetxt, end='', file=out) + shutil.copymode(path, newfile) + shutil.move(newfile, path) + +def check_folder(directory, config, fix=False, verbose=False): + success = True + files = [] + + for base_path in config['include']: + for pattern in config['patterns']: + path = os.path.join(directory, base_path, pattern) + files += glob.glob(path, recursive=config['recursive']) + + for f in files: + path = os.path.normpath(f) + + if verbose: + print("Checking file:", path) + + result = check_file(path) + + has_resolvable_errors = False + + for lineno in result['errordocs_errors']: + print("[Error] Found LAMMPS errordocs @ {}:{}".format(path, lineno)) + has_resolvable_errors = True + + if has_resolvable_errors: + if fix: + print("Applying automatic fixes to file:", path) + fix_file(path, result) + else: + success = False + + return success + +def main(): + parser = argparse.ArgumentParser(description='Utility for detecting and fixing whitespace issues in LAMMPS') + parser.add_argument('-c', '--config', metavar='CONFIG_FILE', help='location of a optional configuration file') + parser.add_argument('-f', '--fix', action='store_true', help='automatically fix URLs') + parser.add_argument('-v', '--verbose', action='store_true', help='verbose output') + parser.add_argument('DIRECTORY', help='directory (or file) that should be checked') + args = parser.parse_args() + lammpsdir = os.path.abspath(os.path.expanduser(args.DIRECTORY)) + + if args.config: + with open(args.config, 'r') as cfile: + config = yaml.load(cfile, Loader=yaml.FullLoader) + else: + config = yaml.load(DEFAULT_CONFIG, Loader=yaml.FullLoader) + + if os.path.isdir(lammpsdir): + if not check_folder(lammpsdir, config, args.fix, args.verbose): + sys.exit(1) + else: + success = True + path = os.path.normpath(lammpsdir) + + if args.verbose: + print("Checking file:", path) + + result = check_file(path) + + has_resolvable_errors = False + + for lineno in result['errordocs_errors']: + print("[Error] Found LAMMPS errordocs @ {}:{}".format(path, lineno)) + has_resolvable_errors = True + + if has_resolvable_errors: + if args.fix: + print("Applying automatic fixes to file:", path) + fix_file(path, result) + else: + success = False + + if not success: + sys.exit(1) + +if __name__ == "__main__": + main()