Skip to content

Commit

Permalink
Add examples infrastructure
Browse files Browse the repository at this point in the history
 * Uses the `minted` package to include formatted source code segments
 * Uses a preprocessor script to extract snippets from code examples

Signed-off-by: Joshua Hursey <[email protected]>
  • Loading branch information
jjhursey committed Nov 17, 2020
1 parent ae76313 commit ec629f8
Show file tree
Hide file tree
Showing 7 changed files with 566 additions and 15 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
*.synctex.gz
*.DS_Store
*.xwm
*.mw
datetime-defaults.sty
datetime.sty
check-openpmix
figs/~$drawings.pptx
*.loc
*.soc
_minted*
sources/_autogen_/
36 changes: 21 additions & 15 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Makefile for the PMIx Standard document in LaTex format.
# For more information, see the master document, pmix-standard.tex.

LATEX_C=pdflatex -shell-escape -file-line-error

version=4.0
default: pmix-standard.pdf

Expand All @@ -27,11 +29,8 @@ CHAPTERS= \
App_Python.tex \
Acknowledgements.tex

SOURCES=
# SOURCES=sources/*.c \
# sources/*.cpp \
# sources/*.f90 \
# sources/*.f
SOURCES=sources/*.c \
sources/*.py \

INTERMEDIATE_FILES=pmix-standard.pdf \
pmix-standard.toc \
Expand All @@ -45,27 +44,34 @@ INTERMEDIATE_FILES=pmix-standard.pdf \
pmix-standard.blg \
pmix-standard.synctex.gz \
pmix-standard.xwm \
*.idx *.ilg *.ind
pmix-standard.mw \
pmix-standard.loc \
pmix-standard.soc \
*.idx *.ilg *.ind \
_minted-* \
sources/_autogen_

all: pmix-standard.pdf

pmix-standard.pdf: $(CHAPTERS) $(SOURCES) pmix.sty pmix-standard.tex figs/pmix-logo.png
rm -f $(INTERMEDIATE_FILES)
rm -rf $(INTERMEDIATE_FILES)
@echo "-------------------------------------------------------------"
@echo "If error occurs check pmix-standard.log and pmix-standard.ind"
@echo "-------------------------------------------------------------"
@echo "====> Preprocess Examples"
@./bin/process-example.py $(SOURCES)
@echo "====> Building 1/4"
pdflatex -interaction=batchmode -file-line-error pmix-standard.tex || \
pdflatex -interaction=errorstopmode -file-line-error pmix-standard.tex < /dev/null
$(LATEX_C) -interaction=batchmode pmix-standard.tex || \
$(LATEX_C) -interaction=errorstopmode pmix-standard.tex < /dev/null
@echo "====> Building 2/4 (bibtex)"
bibtex pmix-standard < /dev/null
@echo "====> Building 3/4"
pdflatex -interaction=batchmode -file-line-error pmix-standard.tex || \
pdflatex -interaction=errorstopmode -file-line-error pmix-standard.tex < /dev/null
$(LATEX_C) -interaction=batchmode pmix-standard.tex || \
$(LATEX_C) -interaction=errorstopmode pmix-standard.tex < /dev/null
@echo "====> Building 4/4"
pdflatex -interaction=batchmode -file-line-error pmix-standard.tex || \
pdflatex -interaction=errorstopmode -file-line-error pmix-standard.tex < /dev/null
pdflatex -interaction=batchmode -file-line-error pmix-standard.tex
$(LATEX_C) -interaction=batchmode pmix-standard.tex || \
$(LATEX_C) -interaction=errorstopmode pmix-standard.tex < /dev/null
$(LATEX_C) -interaction=batchmode pmix-standard.tex
@./bin/check-doc.sh
@echo "====> Success"
@cp pmix-standard.pdf pmix-standard-${version}.pdf
Expand Down Expand Up @@ -93,4 +99,4 @@ check-openpmix: pmix-standard.pdf FORCECHECK
@./bin/check-openpmix.py

clean:
rm -f $(INTERMEDIATE_FILES) pmix-standard-*.pdf
rm -rf $(INTERMEDIATE_FILES) pmix-standard-*.pdf
188 changes: 188 additions & 0 deletions bin/process-example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#!/usr/bin/python -u

#
# Recognized tags
# <EG BEGIN ID="string"> : Start a block
# <EG END ID="string"> : End a block
# Rules:
# - The tag strings, regardless of ID, are removed from the final output
# - If multiple blocks with the same ID exist then they are concatinated together
# - ID can contain alphabetic and numberic characters and the following symbols: . _
# - Quote marks are stripped out
#
import sys
import os
import re
import argparse
import subprocess
import shutil

EG_BEGIN_STR="EG BEGIN ID="
EG_END_STR="EG END ID="
EG_ID_PATTERN="[A-Za-z0-9_\.\"]"


class Example:
"""An example from the source code"""
eid = None
active = False
filename = None
code_block = ""

def __init__(self):
self.eid = ""
self.active = False
self.filename = None
self.code_block = ""

def __str__(self):
return "in_fname=[%s] id=[%s]" % (self.filename, self.eid)

def get_out_fname(self):
f_id = self.eid.replace('"', '')
return os.path.basename(self.filename) + "_" + f_id

def append_line(self, line):
self.code_block = self.code_block + line
if line.endswith("\n") is False:
self.code_block = self.code_block + "\n"

def get_code_block(self):
final_block = ""
min_lead_spaces = 10000
lines = 0
total_lines = 0
skip_last_lines = 0

# First pass to find min spacing
for line in self.code_block.splitlines(True):
if re.search(r"\s*"+EG_BEGIN_STR, line) is not None:
continue
if re.search(r"\s*"+EG_END_STR, line) is not None:
continue

total_lines = total_lines + 1
# Skip empty lines
if len(line) <= 1:
skip_last_lines = skip_last_lines + 1
continue
else:
skip_last_lines = 0

m = re.match(r"^([ \t]+)", line)
if m is not None:
c_len = len(m.group(1))
if c_len > 1:
min_lead_spaces = min(c_len, min_lead_spaces)
else:
# Indicates that there is a line that does not have leading spaces, but is not empty
min_lead_spaces = 0

# Next pass to build the string
lines = 0
for line in self.code_block.splitlines(True):
if re.search(r"\s*"+EG_BEGIN_STR, line) is not None:
continue
if re.search(r"\s*"+EG_END_STR, line) is not None:
continue

# Clear off trailing empty lines
if total_lines - skip_last_lines == lines:
break
lines = lines + 1

m = re.match(r"^([ \t]+)", line)
if m is not None:
line = line[min_lead_spaces:]
final_block = final_block + line

return final_block


def process_file(filename):
"""Process all of the key/value splitting in the file"""
all_examples = {}
eg_id = None

with open(filename, 'r') as fd:
for line in fd:
line = line.rstrip()
m = re.search(r"\s*"+EG_BEGIN_STR+"("+EG_ID_PATTERN+"*)", line)
if m is not None:
eg_id = m.group(1)
# Find this object and update it
found = False
for example_id in all_examples:
if example_id == eg_id:
example = all_examples[example_id]
example.active = True
example.append_line(line)
found = True
# Insert it if not found
if found is False:
x = Example()
x.eid = eg_id
x.active = True
x.append_line(line)
x.filename = filename
all_examples[eg_id] = x
continue

m = re.search(r"\s*"+EG_END_STR+"("+EG_ID_PATTERN+"*)", line)
if m is not None:
eg_id = m.group(1)
# Find this object and update it
for example_id in all_examples:
if example_id == eg_id:
example = all_examples[example_id]
example.active = False
example.append_line("") # Add an empty line
continue

for example_id in all_examples:
example = all_examples[example_id]
if example.active is True:
example.append_line(line)

return all_examples


if __name__ == "__main__":
#
# Command line parsing
#
parser = argparse.ArgumentParser(description="PMIx Standard Example Preprocessor")
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
parser.add_argument("files", nargs='+', help="List of files to process")
parser.parse_args()
args = parser.parse_args()

#
# Create a temporary directory to store the snippets
#
gen_dir = "sources/_autogen_"
if os.access(gen_dir, os.W_OK) is False:
os.makedirs(gen_dir)

#
# Iterate through all examples and split out the snippets
#
for f in args.files:
if os.path.exists(f) is False:
print("ERROR: File does not exist: %s" % (f))
sys.exit(1)

print("Processing File: %s" % (f))
example_blocks = process_file(f)
for k, example in example_blocks.items():
out_fname = gen_dir + "/" + example.get_out_fname()
print("\tExample: %s -- Stored in %s" % (example, out_fname))
if args.verbose:
print("CODE BLOCK")
print("-" * 50)
print(example.get_code_block()),
print("-" * 50)
with open(out_fname, 'w') as fd:
fd.write("%s" % (example.get_code_block()))

sys.exit(0)
106 changes: 106 additions & 0 deletions pmix.sty
Original file line number Diff line number Diff line change
Expand Up @@ -760,3 +760,109 @@
\newcounter{pycounter}
\newcommand{\pylabel}[1]{\refstepcounter{pycounter} \label{appB:#1}}
\newcommand{\refpy}[1]{\hyperref[appB:#1]{\code{#1}}}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Code examples
% Install: (if using Python 3 then use pip3)
% sudo pip2 install --upgrade pip setuptools
% sudo pip2 install Pygments
% Ubuntu:
% sudo apt update
% sudo apt install python-pip
% sudo pip3 install Pygments
%
% package to work around "No room for a new \write" message
\usepackage{morewrites}
\morewritessetup{allocate=10}
% package for code highlighting
\usepackage[chapter]{minted}
\usemintedstyle{default}
\usepackage{xcolor}

% Background: Light Gray
\definecolor{pmixbg}{rgb}{0.95,0.95,0.95}
% Highlight: Yellow
\definecolor{pmixhl}{rgb}{0.95,0.95,0}

%-----------------------
% C Code
%
% block of code
% \begin{pmixCodeC}
% return 0;
% \end{pmixCodeC}
%
% Inline C code
% The code \pmixCodeInlineC{printf("Hello World");} prints ``Hello World''.
%
% Import C code from a file:
% - Whole file: \pmixCodeImportC{sources/hello.c}
% - Selection of a file: \pmixCodeImportC[firstline=73, lastline=84]{sources/hello.c}
% - Highlight selection of a file: \pmixCodeImportC[firstline=73, lastline=84, highlightlines={2-4,6}]{sources/hello.c}
%
\newminted[pmixCodeC]{c}{fontsize=\small,
bgcolor=pmixbg,
highlightcolor=pmixhl,
breaklines,
autogobble,
linenos,
numbersep=3pt,
firstnumber=1}
\newmintinline[pmixCodeInlineC]{c}{bgcolor=pmixbg,
highlightcolor=pmixhl,
breaklines,
autogobble,
linenos,
firstnumber=1}
\newmintedfile[pmixCodeImportC]{c}{fontsize=\small,
bgcolor=pmixbg,
highlightcolor=pmixhl,
breaklines,
autogobble,
linenos,
numbersep=3pt,
firstnumber=1}

%-----------------------
% Python Code
%
% block of code
% \begin{pmixCodePy}
% print("Hello")
% \end{pmixCodePy}
%
% Inline C code
% The code \pmixCodeInlinePy{print("Hello World")} prints ``Hello World''.
%
% Import C code from a file:
% - Whole file: \pmixCodeImportPy{sources/hello.py}
% - Selection of a file: \pmixCodeImportPy[firstline=73, lastline=84]{sources/hello.py}
% - Highlight selection of a file: \pmixCodeImportPy[firstline=73, lastline=84, highlightlines={2-4,6}]{sources/hello.py}
%
\newminted[pmixCodepPy]{python}{fontsize=\small,
bgcolor=pmixbg,
highlightcolor=pmixhl,
breaklines,
autogobble,
linenos,
numbersep=3pt,
firstnumber=1}
\newmintinline[pmixCodeInlinePy]{python}{bgcolor=pmixbg,
highlightcolor=pmixhl,
breaklines,
autogobble,
linenos,
firstnumber=1}
\newmintedfile[pmixCodeImportPy]{python}{fontsize=\small,
bgcolor=pmixbg,
highlightcolor=pmixhl,
breaklines,
autogobble,
linenos,
numbersep=3pt,
firstnumber=1}




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Loading

0 comments on commit ec629f8

Please sign in to comment.