blob: 30e56b3d697b5ff900b3ebe1dcd887b4071385e8 [file] [log] [blame]
Googlerebd28a92018-02-07 08:46:31 -08001// 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
27namespace blaze {
28
29using std::deque;
30using std::string;
31using std::vector;
32
33RcFile::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
50RcFile::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 }
ccalvarin58acb7b2018-03-08 10:53:24 -0800113 loop += " " + words[1] + "\n"; // Include the loop.
Googlerebd28a92018-02-07 08:46:31 -0800114 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