Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/integration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@ very first version of this example)::
# Generate input file name
set(infile "${CMAKE_CURRENT_SOURCE_DIR}/${infileName}")

# Create the dependency file
set(depfile "${CMAKE_CURRENT_BINARY_DIR/${outfileName}.d")

# Custom command to do the processing
add_custom_command(
OUTPUT "${outfile}"
COMMAND fypp "${infile}" "${outfile}"
COMMAND fypp "${infile}" "${outfile}" --depfile "${depfile}"
MAIN_DEPENDENCY "${infile}"
DEPFILE "${depfile}"
VERBATIM)

# Finally add output file to a list
Expand Down
37 changes: 37 additions & 0 deletions src/fypp.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,13 @@ def __init__(self, includedirs=None, encoding='utf-8'):
# Directory of current file
self._curdir = None

# All files that have been included
self._included_files = []


def get_dependencies(self):
return self._included_files


def parsefile(self, fobj):
'''Parses file or a file like object.
Expand All @@ -252,6 +259,9 @@ def parsefile(self, fobj):


def _includefile(self, span, fobj, fname, curdir):
# Don't add the root file, only later includes
if self._curfile:
self._included_files.append(fname)
oldfile = self._curfile
olddir = self._curdir
self._curfile = fname
Expand Down Expand Up @@ -2413,6 +2423,10 @@ def process_text(self, txt):
return self._render()


def get_dependencies(self):
return self._parser.get_dependencies()


def _render(self):
output = self._renderer.render(self._builder.tree)
self._builder.reset()
Expand Down Expand Up @@ -2579,6 +2593,16 @@ def process_file(self, infile, outfile=None):
return None


def write_dependencies(self, outfile, depfile):
def quote(text):
return text.replace('$', '$$').replace(' ', '\\ ').replace('#', '\\#')

dependencies = [quote(d) for d in self._preprocessor.get_dependencies()]

with open(depfile, 'w', encoding='utf-8') as f:
f.write('{}: {}'.format(quote(outfile), ' '.join(dependencies)))


def process_text(self, txt):
'''Processes a string.

Expand Down Expand Up @@ -2674,6 +2698,8 @@ class FyppOptions(optparse.Values):
setting.
create_parent_folder (bool): Whether the parent folder for the output
file should be created if it does not exist. Default: False.
depfile (str | None): If set, where to write a Makefile compatible
dependency file. Default: None.
'''

def __init__(self):
Expand All @@ -2696,6 +2722,7 @@ def __init__(self):
self.encoding = 'utf-8'
self.create_parent_folder = False
self.file_var_root = None
self.depfile = None


class FortranLineFolder:
Expand Down Expand Up @@ -2939,6 +2966,10 @@ def get_option_parser():
parser.add_option('--file-var-root', metavar='DIR', dest='file_var_root',
default=defs.file_var_root, help=msg)

msg = 'Write a Make-compatible dependency file to this location'
parser.add_option('--depfile', metavar='DEPFILE', dest='depfile',
default=defs.depfile, help=msg)

return parser


Expand All @@ -2949,9 +2980,15 @@ def run_fypp():
opts, leftover = optparser.parse_args(values=options)
infile = leftover[0] if len(leftover) > 0 else '-'
outfile = leftover[1] if len(leftover) > 1 else '-'

if outfile == '-' and opts.depfile:
raise optparse.OptParseError("--depfile cannot be used when writing to stdout")

try:
tool = Fypp(opts)
tool.process_file(infile, outfile)
if opts.depfile:
tool.write_dependencies(outfile, opts.depfile)
except FyppStopRequest as exc:
sys.stderr.write(_formatted_exception(exc))
sys.exit(USER_ERROR_EXIT_CODE)
Expand Down
1 change: 1 addition & 0 deletions test/include/escaped_includes.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#:include 'need$ #escape.inc'
2 changes: 2 additions & 0 deletions test/include/multi_includes.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#:include 'fypp1.inc'
#:include 'fypp2.inc'
1 change: 1 addition & 0 deletions test/include/subfolder/need$ #escape.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#:include 'fypp2.inc'
5 changes: 0 additions & 5 deletions test/runtests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ else
pythons="python3"
fi
root=".."
if [ -z "$PYTHONPATH" ]; then
export PYTHONPATH="$root/src"
else
export PYTHONPATH="$root/src:$PYTHONPATH"
fi
cd $testdir
failed="0"
failing_pythons=""
Expand Down
68 changes: 68 additions & 0 deletions test/test_fypp.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
'''Unit tests for testing Fypp.'''
from pathlib import Path
import os
import platform
import sys
import tempfile
import unittest

# Allow for importing fypp
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))

import fypp


Expand Down Expand Up @@ -3055,6 +3062,27 @@ def _importmodule(module):
),
]

DEPFILE_TESTS = [
('basic',
([_incdir('include')],
'include/subfolder/include_fypp1.inc',
'{output}: include/fypp1.inc',
)
),
('multiple includes',
([_incdir('include/subfolder')],
'include/multi_includes.inc',
'{output}: include/fypp1.inc include/subfolder/fypp2.inc',
)
),
('escapes',
([_incdir('include'), _incdir('include/subfolder')],
'include/escaped_includes.inc',
'{output}: include/subfolder/need$$\\ \\#escape.inc include/subfolder/fypp2.inc',
)
),
]


def _get_test_output_method(args, inp, out):
'''Returns a test method for checking correctness of Fypp output.
Expand Down Expand Up @@ -3102,6 +3130,36 @@ def test_output_from_file_input(self):
return test_output_from_file_input


def _get_test_depfile_method(args, inputfile, expected):
'''Returns a test method for checking correctness of depfile.

Args:
args (list of str): Command-line arguments to pass to Fypp.
inputfile (str): Input file with Fypp directives.
out (str): Expected output.

Returns:
method: Method to test equality of depfile with result delivered by Fypp.
'''

def test_depfile(self):
'''Tests whether Fypp result matches expected output when input is in a file.'''
output = self._get_tempfile()
depfile = self._get_tempfile()

optparser = fypp.get_option_parser()
options, leftover = optparser.parse_args(args + ['--depfile', depfile])
self.assertEqual(len(leftover), 0)
tool = fypp.Fypp(options)
tool.process_file(inputfile, output)
tool.write_dependencies(output, depfile)

with open(depfile, 'r', encoding='utf-8') as f:
got = f.read().strip()
self.assertEqual(got, expected.format(output=output))
return test_depfile



def _get_test_exception_method(args, inp, exceptions):
'''Returns a test method for checking correctness of thrown exception.
Expand Down Expand Up @@ -3199,6 +3257,16 @@ class ExceptionTest(_TestContainer): pass
class ImportTest(_TestContainer): pass
ImportTest.add_test_methods(IMPORT_TESTS, _get_test_output_method)

class DepfileTest(_TestContainer):

def _get_tempfile(self):
_fd, output = tempfile.mkstemp()
os.close(_fd)
self.addCleanup(os.unlink, output)
return output

DepfileTest.add_test_methods(DEPFILE_TESTS, _get_test_depfile_method)


if __name__ == '__main__':
unittest.main()