Googler | ebd28a9 | 2018-02-07 08:46:31 -0800 | [diff] [blame] | 1 | // Copyright 2018 The Bazel Authors. All rights reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #include "src/main/cpp/rc_file.h" |
| 16 | |
| 17 | #include <algorithm> |
| 18 | #include <utility> |
| 19 | |
| 20 | #include "src/main/cpp/blaze_util.h" |
| 21 | #include "src/main/cpp/blaze_util_platform.h" |
| 22 | #include "src/main/cpp/util/file.h" |
| 23 | #include "src/main/cpp/util/logging.h" |
| 24 | #include "src/main/cpp/util/strings.h" |
| 25 | #include "src/main/cpp/workspace_layout.h" |
| 26 | |
| 27 | namespace blaze { |
| 28 | |
| 29 | using std::deque; |
| 30 | using std::string; |
| 31 | using std::vector; |
| 32 | |
| 33 | RcFile::RcFile(string filename, const WorkspaceLayout* workspace_layout, |
| 34 | string workspace) |
| 35 | : filename_(std::move(filename)), |
| 36 | workspace_layout_(workspace_layout), |
| 37 | workspace_(std::move(workspace)) {} |
| 38 | |
| 39 | /*static*/ std::unique_ptr<RcFile> RcFile::Parse( |
| 40 | std::string filename, const WorkspaceLayout* workspace_layout, |
| 41 | std::string workspace, ParseError* error, std::string* error_text) { |
| 42 | std::unique_ptr<RcFile> rcfile(new RcFile( |
| 43 | std::move(filename), workspace_layout, std::move(workspace))); |
| 44 | deque<string> initial_import_stack = {rcfile->filename_}; |
| 45 | *error = rcfile->ParseFile( |
| 46 | rcfile->filename_, &initial_import_stack, error_text); |
| 47 | return (*error == ParseError::NONE) ? std::move(rcfile) : nullptr; |
| 48 | } |
| 49 | |
| 50 | RcFile::ParseError RcFile::ParseFile(const string& filename, |
| 51 | deque<string>* import_stack, |
| 52 | string* error_text) { |
| 53 | BAZEL_LOG(INFO) << "Parsing the RcFile " << filename; |
| 54 | string contents; |
| 55 | if (!blaze_util::ReadFile(filename, &contents)) { |
| 56 | blaze_util::StringPrintf(error_text, |
| 57 | "Unexpected error reading .blazerc file '%s'", filename.c_str()); |
| 58 | return ParseError::UNREADABLE_FILE; |
| 59 | } |
| 60 | |
| 61 | rcfile_paths_.push_back(filename); |
| 62 | // Keep a pointer to the filename string in rcfile_paths_ for the RcOptions. |
| 63 | string* filename_ptr = &rcfile_paths_.back(); |
| 64 | |
| 65 | // A '\' at the end of a line continues the line. |
| 66 | blaze_util::Replace("\\\r\n", "", &contents); |
| 67 | blaze_util::Replace("\\\n", "", &contents); |
| 68 | |
| 69 | vector<string> lines = blaze_util::Split(contents, '\n'); |
| 70 | for (string& line : lines) { |
| 71 | blaze_util::StripWhitespace(&line); |
| 72 | |
| 73 | // Check for an empty line. |
| 74 | if (line.empty()) { |
| 75 | continue; |
| 76 | } |
| 77 | |
| 78 | vector<string> words; |
| 79 | |
| 80 | // This will treat "#" as a comment, and properly |
| 81 | // quote single and double quotes, and treat '\' |
| 82 | // as an escape character. |
| 83 | // TODO(bazel-team): This function silently ignores |
| 84 | // dangling backslash escapes and missing end-quotes. |
| 85 | blaze_util::Tokenize(line, '#', &words); |
| 86 | |
| 87 | if (words.empty()) { |
| 88 | // Could happen if line starts with "#" |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | string command = words[0]; |
| 93 | |
| 94 | if (command == "import") { |
| 95 | if (words.size() != 2 || |
| 96 | (words[1].compare(0, workspace_layout_->WorkspacePrefixLength, |
| 97 | workspace_layout_->WorkspacePrefix) == 0 && |
| 98 | !workspace_layout_->WorkspaceRelativizeRcFilePath(workspace_, |
| 99 | &words[1]))) { |
| 100 | blaze_util::StringPrintf( |
| 101 | error_text, |
| 102 | "Invalid import declaration in .blazerc file '%s': '%s'" |
| 103 | " (are you in your source checkout/WORKSPACE?)", |
| 104 | filename.c_str(), line.c_str()); |
| 105 | return ParseError::INVALID_FORMAT; |
| 106 | } |
| 107 | if (std::find(import_stack->begin(), import_stack->end(), words[1]) != |
| 108 | import_stack->end()) { |
| 109 | string loop; |
| 110 | for (const string& imported_rc : *import_stack) { |
| 111 | loop += " " + imported_rc + "\n"; |
| 112 | } |
ccalvarin | 58acb7b | 2018-03-08 10:53:24 -0800 | [diff] [blame] | 113 | loop += " " + words[1] + "\n"; // Include the loop. |
Googler | ebd28a9 | 2018-02-07 08:46:31 -0800 | [diff] [blame] | 114 | blaze_util::StringPrintf(error_text, |
| 115 | "Import loop detected:\n%s", loop.c_str()); |
| 116 | return ParseError::IMPORT_LOOP; |
| 117 | } |
| 118 | |
| 119 | import_stack->push_back(words[1]); |
| 120 | ParseError parse_error = ParseFile(words[1], import_stack, error_text); |
| 121 | if (parse_error != ParseError::NONE) { |
| 122 | return parse_error; |
| 123 | } |
| 124 | import_stack->pop_back(); |
| 125 | } else { |
| 126 | auto words_it = words.begin(); |
| 127 | words_it++; // Advance past command. |
| 128 | for (; words_it != words.end(); words_it++) { |
| 129 | options_[command].push_back({filename_ptr, *words_it}); |
| 130 | } |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | return ParseError::NONE; |
| 135 | } |
| 136 | |
| 137 | } // namespace blaze |