Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for generating a Make compatible depfile #41

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
37 changes: 37 additions & 0 deletions bin/fypp
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,13 @@ class Parser:
# 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 @@ class Parser:


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 @@ class Processor:
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 @@ -2574,6 +2588,16 @@ class Fypp:
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 @@ -2666,6 +2690,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 @@ -2685,6 +2711,7 @@ class FyppOptions(optparse.Values):
self.encoding = 'utf-8'
self.create_parent_folder = False
self.file_var_root = None
self.depfile = None
dcbaker marked this conversation as resolved.
Show resolved Hide resolved


class FortranLineFolder:
Expand Down Expand Up @@ -2911,6 +2938,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 @@ -2921,9 +2952,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
6 changes: 5 additions & 1 deletion docs/fypp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2109,11 +2109,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
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 @@ -2942,6 +2949,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 @@ -2989,6 +3017,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 @@ -3086,6 +3144,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()