I've attached a Python script that takes a LilyPond source file and prints all the include file names it finds, recursively. It keeps track of the current working directory. It does *not* reeport LilyPond include files, but I'm guessing that's not what's wanted. Every include file is reported once and printed sorted. It's pretty rough, but there it is.
-- Knute Snortum On Sat, Mar 15, 2025 at 4:12 PM Saul Tobin <saul.james.to...@gmail.com> wrote: > These are questions I've had to consider as I work on Emacs > lilypond-ts-mode, and I suspect are relevant to lots of tooling projects. > > Since output file names and locations are potentially set via arbitrary > Scheme code, it's not feasible to do this reliably without actually running > the LilyPond binary itself to parse the input file. It's certainly possible > to do that without actually running translation. The easiest way would be > to make a custom init file that defines `default-toplevel-book-handler` to > a function that just calls `get-current-filename` but doesn't actually call > into ly:book-process, then pass the custom init file to LilyPond via `-i`. > For reference, look at the definitions of the built-in toplevel handlers in > lily-library.scm. Note that `get-current-filename` is not exported from > `lily` into the session module. Also note that this will only give you the > filename, not the output directory if one was set via `-o`. I believe you > can get that by simply checking the current Guile working directory. > > The complexity of retrieving output file names is, I believe, the reason > that Frescobaldi just requires users to specify non-standard output files > via a file-local variable. > > Includes I suspect can't easily be retrieved via Scheme, since they > literally just insert file contents verbatim. But just grep-ing for > \include probably gets you far enough to be useful in 99% of LilyPond > projects. Technically, it's possible for a LilyPond project to load Scheme > files that construct music data, but users with such complicated projects > can't reasonably expect tooling to simply work. > > Saul > > On Sat, Mar 15, 2025 at 5:11 PM YTG 1234 <ytg1234.pm...@gmail.com> wrote: > >> I've been wondering what the equivalent of cpp -M for Lilypond is, if >> there is such a thing. >> >> It wouldn't be so simple to implement; as well as tracking \includes >> recursively, tracking the expected names of output files (as there can >> be multiple per source file) is also required. There's probably some >> Guile interface for getting all the books generated by a file, but there >> would have to be a way to evaluate it without compiling the entire score… >> >> Any ideas? >> >> >>
#!/usr/bin/env python3 # Prints all non-LilyPond include file name in a source file. # Execute it in the directory your source file is in. import sys def main(): if len(sys.argv) != 2: print("Usage: python find-includes.py <filename>") sys.exit(1) filename = sys.argv[1] includes = set() cwd = [] def find_includes(filename): try: with open(filename, 'r') as file: for line in file: pos = line.find("\\include") if pos != -1: include_name = line[pos+9:].strip() include_name = include_name[1:-1] # remove quotes includes.add("/".join(cwd) + include_name) d = include_name.rfind('/') # directory in name? if d != -1: cwd.append(include_name[:d+1]) include_name = include_name[d+1:] find_includes("/".join(cwd) + include_name) if d != -1: cwd.pop() except FileNotFoundError: print(f"File {filename} not found (may be a LilyPond file).", file=sys.stderr) includes.discard(filename) except Exception as e: print(f"An error occurred: {e}", file=sys.stderr) find_includes(filename) print(*sorted(includes), sep='\n') if __name__ == "__main__": main()