| #pragma once | #pragma once | ||||
| /** | |||||
| * The `file_deps` module implements the interdependencies of inputs files to their outputs, as well | |||||
| * as the command that was used to generate that output from the inputs. | |||||
| * | |||||
| * For a given output, there is exactly one command that was used to generate it, and some non-zero | |||||
| * number of input relations. A single input relation encapsulates the path to that input as well as | |||||
| * the file modification time at which that input was used. The modification times are specifically | |||||
| * stored on the input relation, and not associated with the input file itself, as more than one | |||||
| * output may make use of a single input, and each output will need to keep track of the | |||||
| * outdated-ness of its inputs separately. | |||||
| * | |||||
| * A toolchain has an associated `file_deps_mode`, which can be deduced from the Compiler-ID. The | |||||
| * three dependency modes are: | |||||
| * | |||||
| * 1. None - No dependency tracking takes place. | |||||
| * 2. GNU-Style - Dependencies are tracked using Clang and GCC's -M flags, which write a | |||||
| * Makefile-syntax file which contains the dependencies of the file that is being compiled. This | |||||
| * file is generated at the same time that the primary output is generated, and does not occur in a | |||||
| * pre-compile dependency pass. | |||||
| * 2. MSVC-Style - Dependencies are tracked using the cl.exe /showIncludes flag, which writes the | |||||
| * path of every file that is read by the preprocsesor to the compiler's output. This also happens | |||||
| * at the same time as main compilation, and does not require a pre-scan pass. Unfortunately, MSVC | |||||
| * localizes this string, so we cannot properly track dependencies without knowing what language it | |||||
| * will emit beforehand. At the moment, we implement dependency tracking for English, but providing | |||||
| * other languages is not difficult. | |||||
| */ | |||||
| #include <dds/util/fs.hpp> | #include <dds/util/fs.hpp> | ||||
| #include <string> | #include <string> | ||||
| namespace dds { | namespace dds { | ||||
| /** | |||||
| * The mode in which we can scan for compilation dependencies. | |||||
| */ | |||||
| enum class file_deps_mode { | enum class file_deps_mode { | ||||
| /// Disable dependency tracking | |||||
| none, | none, | ||||
| /// Track dependencies using MSVC semantics | |||||
| msvc, | msvc, | ||||
| /// Track dependencies using GNU-style generated-Makefile semantics | |||||
| gnu, | gnu, | ||||
| }; | }; | ||||
| /** | |||||
| * The result of performing a dependency scan. A simple aggregate type. | |||||
| */ | |||||
| struct file_deps_info { | struct file_deps_info { | ||||
| fs::path output; | |||||
| /** | |||||
| * The primary output path. | |||||
| */ | |||||
| fs::path output; | |||||
| /** | |||||
| * The paths to each input | |||||
| */ | |||||
| std::vector<fs::path> inputs; | std::vector<fs::path> inputs; | ||||
| std::string command; | |||||
| std::string command_output; | |||||
| /** | |||||
| * The command that was used to generate the output | |||||
| */ | |||||
| std::string command; | |||||
| /** | |||||
| * The output of the command. | |||||
| */ | |||||
| std::string command_output; | |||||
| }; | }; | ||||
| class database; | class database; | ||||
| /** | |||||
| * Parse a compiler-generated Makefile that contains dependency information. | |||||
| * @see `parse_mkfile_deps_str` | |||||
| */ | |||||
| file_deps_info parse_mkfile_deps_file(path_ref where); | file_deps_info parse_mkfile_deps_file(path_ref where); | ||||
| /** | |||||
| * Parse a Makefile-syntax string containing compile-generated dependency | |||||
| * information. | |||||
| * @param str A Makefile-syntax string that will be parsed. | |||||
| * @note The returned `file_deps_info` object will only have the `output` and | |||||
| * `inputs` fields filled in, as the other parameters cannot be deduced from | |||||
| * the Makefile. It is on the caller to fill these fields before passing them | |||||
| * to `update_deps_info` | |||||
| */ | |||||
| file_deps_info parse_mkfile_deps_str(std::string_view str); | file_deps_info parse_mkfile_deps_str(std::string_view str); | ||||
| /** | |||||
| * The result of parsing MSVC output for dependencies | |||||
| */ | |||||
| struct msvc_deps_info { | struct msvc_deps_info { | ||||
| struct file_deps_info deps_info; | |||||
| std::string cleaned_output; | |||||
| /// The actual dependency information | |||||
| file_deps_info deps_info; | |||||
| /// The output from the MSVC compiler that has had the dependency information removed. | |||||
| std::string cleaned_output; | |||||
| }; | }; | ||||
| /** | |||||
| * Parse the output of the CL.exe compiler for file dependencies. | |||||
| * @param output The output from `cl.exe` that has had the /showIncludes flag set | |||||
| * @param leader The text prefix for each line that contains a dependency. | |||||
| * @note The returned `file_deps_info` object only has the `input_files` field set, and does not | |||||
| * include the primary input to the compiler. It is up to the caller to add the necessary fields and | |||||
| * values. | |||||
| * @note The `leader` parameter is localized depending on the language that `cl.exe` will use. In | |||||
| * English, this string is `Note: including file:`. If a line begins with this string, the remainder | |||||
| * of the line will be assumed to be a path to the file that the preprocessor read while compiling. | |||||
| * If the `leader` string does not match the language that `cl.exe` emits, then this parsing will | |||||
| * not see any of these notes, no dependencies will be seen, and the `cleaned_output` field in the | |||||
| * return value will still contain the /showIncludes notes. | |||||
| */ | |||||
| msvc_deps_info parse_msvc_output_for_deps(std::string_view output, std::string_view leader); | msvc_deps_info parse_msvc_output_for_deps(std::string_view output, std::string_view leader); | ||||
| void update_deps_info(database& db, const file_deps_info&); | |||||
| /** | |||||
| * Update the dependency information in the build database for later reference via | |||||
| * `get_rebuild_info`. | |||||
| * @param db The database to update | |||||
| * @param info The dependency information to store | |||||
| */ | |||||
| void update_deps_info(database& db, const file_deps_info& info); | |||||
| /** | |||||
| * The information that is pertinent to the rebuild of a file. This will contain a list of inputs | |||||
| * that have a newer mtime than we have recorded, and the previous command and previous command | |||||
| * output that we have stored. | |||||
| */ | |||||
| struct deps_rebuild_info { | struct deps_rebuild_info { | ||||
| std::vector<fs::path> newer_inputs; | std::vector<fs::path> newer_inputs; | ||||
| std::string previous_command; | std::string previous_command; | ||||
| std::string previous_command_output; | std::string previous_command_output; | ||||
| }; | }; | ||||
| /** | |||||
| * Given the path to an output file, read all the dependency information from the database. If the | |||||
| * given output has never been recorded, then the resulting object will be empty. | |||||
| */ | |||||
| deps_rebuild_info get_rebuild_info(const database& db, path_ref output_path); | deps_rebuild_info get_rebuild_info(const database& db, path_ref output_path); | ||||
| } // namespace dds | } // namespace dds |