//----------------------------------------------------------- // Copyright Christian Arnault LAL-Orsay CNRS // arnault@lal.in2p3.fr // See the complete license in cmt_license.txt "http://www.cecill.info". //----------------------------------------------------------- #include "cmt_deps_builder.h" #include "cmt_system.h" #include "cmt_use.h" #include "cmt_include.h" #include "cmt_symbol.h" #include "cmt_log.h" // // While parsing a C++ file, these are the possible usefull // states we can reach. // Each state correspond to a specific state action function. // enum state_def { at_start, // beginning of the file in_line, // along a line in_string, // inside a quoted string in_char, // inside a quoted char in_comment, // inside a multi-line comment in_string_comment, // inside a quoted string in a comment in_char_comment, // inside a quoted char in a comment in_line_comment // inside a single-line comment }; //-------------------------------------------------- static int build_deps (const cmt_string& name, const cmt_string& dir_name, int current_path_index, const CmtSystem::cmt_string_vector& include_paths, const CmtSystem::cmt_string_vector& substitutions, CmtSystem::cmt_string_vector& all_deps, CmtSystem::cmt_string_vector& deps); //-------------------------------------------------- static void header_file_action (const char* header_file, const cmt_string& dir_name, int current_path_index, const CmtSystem::cmt_string_vector& include_paths, const CmtSystem::cmt_string_vector& substitutions, CmtSystem::cmt_string_vector& all_deps, CmtSystem::cmt_string_vector& deps) { bool found = false; for (int i = 0; i < all_deps.size (); i++) { if (all_deps[i] == header_file) { found = true; break; } } if (!found) { all_deps.push_back (header_file); int path_index = build_deps (header_file, dir_name, current_path_index, include_paths, substitutions, all_deps, deps); if (path_index >= 0) { cmt_string full_name; if (path_index == 1) { full_name = dir_name; full_name += CmtSystem::file_separator (); if (current_path_index >= 2) { full_name.replace (include_paths[current_path_index - 2], substitutions[current_path_index - 2]); } } else if (path_index > 1) { full_name = substitutions[path_index - 2]; full_name += CmtSystem::file_separator (); } full_name += header_file; deps.push_back (full_name); } } } //-------------------------------------------------- static char* at_start_action (char* ptr, state_def& state, const cmt_string& dir_name, int current_path_index, const CmtSystem::cmt_string_vector& include_paths, const CmtSystem::cmt_string_vector& substitutions, CmtSystem::cmt_string_vector& all_deps, CmtSystem::cmt_string_vector& deps) { char term = 0; if (*ptr == '#') { ptr++; while ((*ptr == ' ') || (*ptr == '\t')) ptr++; if (!strncmp (ptr, "include", 7)) { ptr += 7; while (*ptr == ' ') ptr++; if (*ptr == '<') { term = '>'; ptr++; } else if (*ptr == '"') { term = '"'; ptr++; } else { state = in_line; ptr += strlen (ptr); return (ptr); } } else { state = in_line; ptr += strlen (ptr); return (ptr); } } else if (!strncmp (ptr, " include", 13)) { ptr += 13; while ((*ptr == ' ') || (*ptr == '\t')) ptr++; if (*ptr == '\'') { term = '\''; ptr++; } else { state = in_line; return (ptr); } } else if (!strncmp (ptr, "\tinclude", 8)) { ptr += 8; while ((*ptr == ' ') || (*ptr == '\t')) ptr++; if (*ptr == '\'') { term = '\''; ptr++; } else { state = in_line; return (ptr); } } else { state = in_line; return (ptr); } char* end; end = strchr (ptr, term); if (end != 0) { *end = 0; } const char* header_file = ptr; header_file_action (header_file, dir_name, current_path_index, include_paths, substitutions, all_deps, deps); if (end != 0) { *end = term; } state = in_line; ptr += strlen (ptr); return (ptr); } //-------------------------------------------------- static char* in_line_action (char* ptr, state_def& state) { char* pattern = &ptr[strlen (ptr)]; char* pos = strchr (ptr, '"'); if (pos != 0) { if (pos < pattern) { state = in_string; pattern = pos; } } pos = strchr (ptr, '\''); if (pos != 0) { if (pos < pattern) { state = in_char; pattern = pos; } } pos = strstr (ptr, "/*"); //*/ if (pos != 0) { if (pos < pattern) { state = in_comment; pattern = pos + 1; } } pos = strstr (ptr, "//"); if (pos != 0) { if (pos < pattern) { state = in_line_comment; pattern = pos + 1; } } if (state != in_line) { ptr = pattern + 1; } else { ptr += strlen (ptr); } return (ptr); } //-------------------------------------------------- static char* in_string_action (char* ptr, state_def& state) { char* pos = strchr (ptr, '"'); if (pos == 0) { // This string is not finished till the end of the line.. // we expect it continues to the nex line... ptr += strlen (ptr); } else { pos--; if (*pos == '\\') { ptr = pos + 2; } else { ptr = pos + 2; state = in_line; } } return (ptr); } //-------------------------------------------------- static char* in_char_action (char* ptr, state_def& state) { char* pos = strchr (ptr, '\''); if (pos == 0) { // This string is not finished till the end of the line.. // we expect it continues to the nex line... ptr += strlen (ptr); } else { pos--; if (*pos == '\\') { ptr = pos + 2; } else { ptr = pos + 2; state = in_line; } } return (ptr); } //-------------------------------------------------- static char* in_comment_action (char* ptr, state_def& state) { char* pattern = &ptr[strlen (ptr)]; char* pos = strchr (ptr, '"'); if (pos != 0) { if (pos < pattern) { state = in_string_comment; pattern = pos; } } pos = strchr (ptr, '\''); if (pos != 0) { if (pos < pattern) { state = in_char_comment; pattern = pos; } } pos = strstr (ptr, "*/"); if (pos != 0) { if (pos < pattern) { state = in_line; pattern = pos + 1; } } if (state == in_comment) { ptr += strlen (ptr); } else { ptr = pattern + 1; } return (ptr); } //-------------------------------------------------- static char* in_string_comment_action (char* ptr, state_def& state) { char* pos = strchr (ptr, '"'); if (pos == 0) { // This string is not finished till the end of the line.. // we expect it continues to the nex line... ptr += strlen (ptr); } else { pos--; if (*pos == '\\') { ptr = pos + 2; } else { ptr = pos + 2; state = in_comment; } } return (ptr); } //-------------------------------------------------- static char* in_char_comment_action (char* ptr, state_def& state) { char* pos = strchr (ptr, '\''); if (pos == 0) { // This string is not finished till the end of the line.. // we expect it continues to the nex line... ptr += strlen (ptr); } else { pos--; if (*pos == '\\') { ptr = pos + 2; } else { ptr = pos + 2; state = in_comment; } } return (ptr); } //-------------------------------------------------- static char* in_line_comment_action (char* ptr, state_def& state) { ptr += strlen (ptr); return (ptr); } //-------------------------------------------------- static void build_deps_stream (istream& input, const cmt_string& dir_name, int current_path_index, const CmtSystem::cmt_string_vector& include_paths, const CmtSystem::cmt_string_vector& substitutions, CmtSystem::cmt_string_vector& all_deps, CmtSystem::cmt_string_vector& deps) { Log; if (input) { log << "CMT> build_deps_stream dir_name=" << dir_name << log_endl; while (!input.eof ()) { char line[16384]; input.getline (line, sizeof (line)); char* ptr = &line[0]; state_def state = at_start; log << "CMT> build_deps_stream2 line=[" << line << "]" << log_endl; while (strlen (ptr) > 0) { switch (state) { case at_start: ptr = at_start_action (ptr, state, dir_name, current_path_index, include_paths, substitutions, all_deps, deps); break; case in_line: ptr = in_line_action (ptr, state); break; case in_string: ptr = in_string_action (ptr, state); break; case in_char: ptr = in_char_action (ptr, state); break; case in_comment: ptr = in_comment_action (ptr, state); break; case in_string_comment: ptr = in_string_comment_action (ptr, state); break; case in_char_comment: ptr = in_char_comment_action (ptr, state); break; case in_line_comment: ptr = in_line_action (ptr, state); break; } } } } } //-------------------------------------------------- static int build_deps (const cmt_string& name, const cmt_string& dir_name, int current_path_index, const CmtSystem::cmt_string_vector& include_paths, const CmtSystem::cmt_string_vector& substitutions, CmtSystem::cmt_string_vector& all_deps, CmtSystem::cmt_string_vector& deps) { Log; int result = -1; cmt_string new_dir; log << "CMT> build_deps name=" << name << " dir_name=" << dir_name << log_endl; // // Return 0 when the file is found in the current directory // if (CmtSystem::test_file (name)) { ifstream input (name.c_str ()); if (input) { CmtSystem::dirname (name, new_dir); build_deps_stream (input, new_dir, current_path_index, include_paths, substitutions, all_deps, deps); return (0); } } cmt_string full_name; full_name = dir_name; full_name += CmtSystem::file_separator (); full_name += name; // // Return 1 when the file is found in the directory of the // upper level source file // if (CmtSystem::test_file (full_name)) { ifstream input (full_name.c_str ()); if (input) { CmtSystem::dirname (full_name, new_dir); build_deps_stream (input, new_dir, current_path_index, include_paths, substitutions, all_deps, deps); return (1); } } int path_index = -1; // // Return [path_index + 2] when the include file is found at one of // the include_paths // for (path_index = 0; path_index < include_paths.size (); path_index++) { full_name = include_paths[path_index]; full_name += CmtSystem::file_separator (); full_name += name; log << "CMT> build_deps2 full_name=" << full_name << log_endl; if (CmtSystem::test_file (full_name)) { ifstream in (full_name.c_str ()); if (in) { CmtSystem::dirname (full_name, new_dir); log << "CMT> build_deps3 new_dir=" << new_dir << log_endl; build_deps_stream (in, new_dir, path_index + 2, include_paths, substitutions, all_deps, deps); return (path_index + 2); } } } log << "CMT> build_deps3" << log_endl; return (-1); } //-------------------------------------------------------------------------- void DepsBuilder::clear () { m_include_paths.clear (); m_substitutions.clear (); } //-------------------------------------------------------------------------- void DepsBuilder::add (const cmt_string& path, const cmt_string& substitution) { if (path[path.size () - 1] == CmtSystem::file_separator ()) { cmt_string p = path; p.erase (path.size () - 1); m_include_paths.push_back (p); } else { m_include_paths.push_back (path); } m_substitutions.push_back (substitution); } //-------------------------------------------------------------------------- void DepsBuilder::add_includes (const Use& use) { Log; const Include::IncludeVector& includes = use.includes; int include_number; for (include_number = 0; include_number < includes.size (); include_number++) { const Include& include = includes[include_number]; cmt_string temp = include.name; cmt_string pattern; cmt_string name; char end_pattern; int start = 0; for (;;) { int begin; begin = temp.find (start, "${"); if (begin != cmt_string::npos) { end_pattern = '}'; } else { begin = temp.find (start, "$("); if (begin != cmt_string::npos) { end_pattern = ')'; } else { break; } } start = begin + 2; int end; end = temp.find (start, end_pattern); if (end == cmt_string::npos) break; if (end < begin) break; start = end + 1; temp.substr (begin, end - begin + 1, pattern); temp.substr (begin + 2, end - begin - 2, name); Symbol* macro = Symbol::find (name); if (macro != 0) { cmt_string value = macro->resolve_macro_value (); value += CmtSystem::file_separator (); temp.replace_all (pattern, value); } else { cmt_string value = CmtSystem::getenv (name); value += CmtSystem::file_separator (); temp.replace_all (pattern, value); } } log << "include = " << temp << log_endl; add (temp, include.name); } } //-------------------------------------------------------------------------- CmtSystem::cmt_string_vector& DepsBuilder::run (const cmt_string& file_name) { m_deps.clear (); m_all_deps.clear (); cmt_string preprocessor; Symbol* macro = Symbol::find ("preprocessor_command"); if (macro != 0) { preprocessor = macro->resolve_macro_value (); } if (preprocessor == "") { // // Since no preprocessor command is defined, // we use the internal mechanism provided here. // cmt_string new_dir; CmtSystem::dirname (file_name, new_dir); build_deps (file_name, new_dir, 0, m_include_paths, m_substitutions, m_all_deps, m_deps); } else { // // An external preprocessor command is defined. We expect it // to follow a "standard" syntax for its output, ie: // o It starts with: // .o: ... // o There may be many lines with trailing back-slashes // o All entries are space-separated // o One of the entries is the source file name itself // // The preprocessor command expects the list of -I options // (resolved from the "includes" macro) and the list of // -D/-U options (resolved from the "*_pp_*flags" macros) // // // Building the complete command (still the pp_*flags are // missing) // preprocessor += " "; macro = Symbol::find ("includes"); preprocessor += macro->resolve_macro_value (); preprocessor += " "; preprocessor += file_name; cmt_string output; CmtSystem::execute (preprocessor, output); // // Make the output as one single big line. // output.replace_all ("\n", " "); output.replace_all ("\\ ", " "); CmtSystem::cmt_string_vector files; CmtSystem::split (output, " \t", files); // // Analyze each entry // for (int i = 1; i < files.size (); i++) { const cmt_string& file = files[i]; if (file == file_name) continue; cmt_string dir; cmt_string name; cmt_string full_name; CmtSystem::dirname (file, dir); // // Only declared include_paths will be taken into account // Others are considered as system include paths. // for (int j = 0; j < m_include_paths.size (); j++) { const cmt_string& p = m_include_paths[j]; if (dir == p) { CmtSystem::basename (file, name); full_name = m_substitutions[j]; full_name += name; // // We add in the "m_deps" list the symbolic form // of the path rather that the expanded one. // m_deps.push_back (full_name); break; } } } } return (m_deps); }