1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 // Author: kenton@google.com (Kenton Varda) 9 // Based on original Protocol Buffers design by 10 // Sanjay Ghemawat, Jeff Dean, and others. 11 // 12 // This file is the public interface to the .proto file parser. 13 14 #ifndef GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ 15 #define GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ 16 17 #include <string> 18 #include <utility> 19 #include <vector> 20 21 #include "absl/strings/string_view.h" 22 #include "google/protobuf/compiler/parser.h" 23 #include "google/protobuf/descriptor.h" 24 #include "google/protobuf/descriptor_database.h" 25 26 // Must be included last. 27 #include "google/protobuf/port_def.inc" 28 29 namespace google { 30 namespace protobuf { 31 32 namespace io { 33 class ZeroCopyInputStream; 34 } 35 36 namespace compiler { 37 38 // Defined in this file. 39 class Importer; 40 class MultiFileErrorCollector; 41 class SourceTree; 42 class DiskSourceTree; 43 44 // TODO: Move all SourceTree stuff to a separate file? 45 46 // An implementation of DescriptorDatabase which loads files from a SourceTree 47 // and parses them. 48 // 49 // Note: This class is not thread-safe since it maintains a table of source 50 // code locations for error reporting. However, when a DescriptorPool wraps 51 // a DescriptorDatabase, it uses mutex locking to make sure only one method 52 // of the database is called at a time, even if the DescriptorPool is used 53 // from multiple threads. Therefore, there is only a problem if you create 54 // multiple DescriptorPools wrapping the same SourceTreeDescriptorDatabase 55 // and use them from multiple threads. 56 // 57 // Note: This class does not implement FindFileContainingSymbol() or 58 // FindFileContainingExtension(); these will always return false. 59 class PROTOBUF_EXPORT SourceTreeDescriptorDatabase : public DescriptorDatabase { 60 public: 61 SourceTreeDescriptorDatabase(SourceTree* source_tree); 62 63 // If non-NULL, fallback_database will be checked if a file doesn't exist in 64 // the specified source_tree. 65 SourceTreeDescriptorDatabase(SourceTree* source_tree, 66 DescriptorDatabase* fallback_database); 67 ~SourceTreeDescriptorDatabase() override; 68 69 // Instructs the SourceTreeDescriptorDatabase to report any parse errors 70 // to the given MultiFileErrorCollector. This should be called before 71 // parsing. error_collector must remain valid until either this method 72 // is called again or the SourceTreeDescriptorDatabase is destroyed. RecordErrorsTo(MultiFileErrorCollector * error_collector)73 void RecordErrorsTo(MultiFileErrorCollector* error_collector) { 74 error_collector_ = error_collector; 75 } 76 77 // Gets a DescriptorPool::ErrorCollector which records errors to the 78 // MultiFileErrorCollector specified with RecordErrorsTo(). This collector 79 // has the ability to determine exact line and column numbers of errors 80 // from the information given to it by the DescriptorPool. GetValidationErrorCollector()81 DescriptorPool::ErrorCollector* GetValidationErrorCollector() { 82 using_validation_error_collector_ = true; 83 return &validation_error_collector_; 84 } 85 86 // implements DescriptorDatabase ----------------------------------- 87 bool FindFileByName(const std::string& filename, 88 FileDescriptorProto* output) override; 89 bool FindFileContainingSymbol(const std::string& symbol_name, 90 FileDescriptorProto* output) override; 91 bool FindFileContainingExtension(const std::string& containing_type, 92 int field_number, 93 FileDescriptorProto* output) override; 94 95 private: 96 class SingleFileErrorCollector; 97 98 SourceTree* source_tree_; 99 DescriptorDatabase* fallback_database_; 100 MultiFileErrorCollector* error_collector_; 101 102 class PROTOBUF_EXPORT ValidationErrorCollector 103 : public DescriptorPool::ErrorCollector { 104 public: 105 ValidationErrorCollector(SourceTreeDescriptorDatabase* owner); 106 ~ValidationErrorCollector() override; 107 108 // implements ErrorCollector --------------------------------------- 109 void RecordError(absl::string_view filename, absl::string_view element_name, 110 const Message* descriptor, ErrorLocation location, 111 absl::string_view message) override; 112 113 void RecordWarning(absl::string_view filename, 114 absl::string_view element_name, 115 const Message* descriptor, ErrorLocation location, 116 absl::string_view message) override; 117 118 private: 119 SourceTreeDescriptorDatabase* owner_; 120 }; 121 friend class ValidationErrorCollector; 122 123 bool using_validation_error_collector_; 124 SourceLocationTable source_locations_; 125 ValidationErrorCollector validation_error_collector_; 126 }; 127 128 // Simple interface for parsing .proto files. This wraps the process 129 // of opening the file, parsing it with a Parser, recursively parsing all its 130 // imports, and then cross-linking the results to produce a FileDescriptor. 131 // 132 // This is really just a thin wrapper around SourceTreeDescriptorDatabase. 133 // You may find that SourceTreeDescriptorDatabase is more flexible. 134 // 135 // TODO: I feel like this class is not well-named. 136 class PROTOBUF_EXPORT Importer { 137 public: 138 Importer(SourceTree* source_tree, MultiFileErrorCollector* error_collector); 139 Importer(const Importer&) = delete; 140 Importer& operator=(const Importer&) = delete; 141 ~Importer(); 142 143 // Import the given file and build a FileDescriptor representing it. If 144 // the file is already in the DescriptorPool, the existing FileDescriptor 145 // will be returned. The FileDescriptor is property of the DescriptorPool, 146 // and will remain valid until it is destroyed. If any errors occur, they 147 // will be reported using the error collector and Import() will return NULL. 148 // 149 // A particular Importer object will only report errors for a particular 150 // file once. All future attempts to import the same file will return NULL 151 // without reporting any errors. The idea is that you might want to import 152 // a lot of files without seeing the same errors over and over again. If 153 // you want to see errors for the same files repeatedly, you can use a 154 // separate Importer object to import each one (but use the same 155 // DescriptorPool so that they can be cross-linked). 156 const FileDescriptor* Import(const std::string& filename); 157 158 // The DescriptorPool in which all imported FileDescriptors and their 159 // contents are stored. pool()160 inline const DescriptorPool* pool() const { return &pool_; } 161 162 void AddDirectInputFile(absl::string_view file_name, 163 bool unused_import_is_error = false); 164 void ClearDirectInputFiles(); 165 166 #if !defined(PROTOBUF_FUTURE_RENAME_ADD_UNUSED_IMPORT) && !defined(SWIG) 167 ABSL_DEPRECATED("Use AddDirectInputFile") 168 void AddUnusedImportTrackFile(absl::string_view file_name, 169 bool is_error = false) { 170 AddDirectInputFile(file_name, is_error); 171 } 172 ABSL_DEPRECATED("Use AddDirectInputFile") ClearUnusedImportTrackFiles()173 void ClearUnusedImportTrackFiles() { ClearDirectInputFiles(); } 174 #endif // !PROTOBUF_FUTURE_RENAME_ADD_UNUSED_IMPORT && !SWIG 175 176 177 private: 178 SourceTreeDescriptorDatabase database_; 179 DescriptorPool pool_; 180 }; 181 182 // If the importer encounters problems while trying to import the proto files, 183 // it reports them to a MultiFileErrorCollector. 184 class PROTOBUF_EXPORT MultiFileErrorCollector { 185 public: MultiFileErrorCollector()186 MultiFileErrorCollector() {} 187 MultiFileErrorCollector(const MultiFileErrorCollector&) = delete; 188 MultiFileErrorCollector& operator=(const MultiFileErrorCollector&) = delete; 189 virtual ~MultiFileErrorCollector(); 190 191 // Line and column numbers are zero-based. A line number of -1 indicates 192 // an error with the entire file (e.g. "not found"). 193 virtual void RecordError(absl::string_view filename, int line, int column, 194 absl::string_view message) 195 = 0; RecordWarning(absl::string_view filename,int line,int column,absl::string_view message)196 virtual void RecordWarning(absl::string_view filename, int line, int column, 197 absl::string_view message) { 198 } 199 200 }; 201 202 // Abstract interface which represents a directory tree containing proto files. 203 // Used by the default implementation of Importer to resolve import statements 204 // Most users will probably want to use the DiskSourceTree implementation, 205 // below. 206 class PROTOBUF_EXPORT SourceTree { 207 public: SourceTree()208 SourceTree() {} 209 SourceTree(const SourceTree&) = delete; 210 SourceTree& operator=(const SourceTree&) = delete; 211 virtual ~SourceTree(); 212 213 // Open the given file and return a stream that reads it, or NULL if not 214 // found. The caller takes ownership of the returned object. The filename 215 // must be a path relative to the root of the source tree and must not 216 // contain "." or ".." components. 217 virtual io::ZeroCopyInputStream* Open(absl::string_view filename) = 0; 218 219 // If Open() returns NULL, calling this method immediately will return an 220 // description of the error. 221 // Subclasses should implement this method and return a meaningful value for 222 // better error reporting. 223 // TODO: change this to a pure virtual function. 224 virtual std::string GetLastErrorMessage(); 225 }; 226 227 // An implementation of SourceTree which loads files from locations on disk. 228 // Multiple mappings can be set up to map locations in the DiskSourceTree to 229 // locations in the physical filesystem. 230 class PROTOBUF_EXPORT DiskSourceTree : public SourceTree { 231 public: 232 DiskSourceTree(); 233 DiskSourceTree(const DiskSourceTree&) = delete; 234 DiskSourceTree& operator=(const DiskSourceTree&) = delete; 235 ~DiskSourceTree() override; 236 237 // Map a path on disk to a location in the SourceTree. The path may be 238 // either a file or a directory. If it is a directory, the entire tree 239 // under it will be mapped to the given virtual location. To map a directory 240 // to the root of the source tree, pass an empty string for virtual_path. 241 // 242 // If multiple mapped paths apply when opening a file, they will be searched 243 // in order. For example, if you do: 244 // MapPath("bar", "foo/bar"); 245 // MapPath("", "baz"); 246 // and then you do: 247 // Open("bar/qux"); 248 // the DiskSourceTree will first try to open foo/bar/qux, then baz/bar/qux, 249 // returning the first one that opens successfully. 250 // 251 // disk_path may be an absolute path or relative to the current directory, 252 // just like a path you'd pass to open(). 253 void MapPath(absl::string_view virtual_path, absl::string_view disk_path); 254 255 // Return type for DiskFileToVirtualFile(). 256 enum DiskFileToVirtualFileResult { 257 SUCCESS, 258 SHADOWED, 259 CANNOT_OPEN, 260 NO_MAPPING 261 }; 262 263 // Given a path to a file on disk, find a virtual path mapping to that 264 // file. The first mapping created with MapPath() whose disk_path contains 265 // the filename is used. However, that virtual path may not actually be 266 // usable to open the given file. Possible return values are: 267 // * SUCCESS: The mapping was found. *virtual_file is filled in so that 268 // calling Open(*virtual_file) will open the file named by disk_file. 269 // * SHADOWED: A mapping was found, but using Open() to open this virtual 270 // path will end up returning some different file. This is because some 271 // other mapping with a higher precedence also matches this virtual path 272 // and maps it to a different file that exists on disk. *virtual_file 273 // is filled in as it would be in the SUCCESS case. *shadowing_disk_file 274 // is filled in with the disk path of the file which would be opened if 275 // you were to call Open(*virtual_file). 276 // * CANNOT_OPEN: The mapping was found and was not shadowed, but the 277 // file specified cannot be opened. When this value is returned, 278 // errno will indicate the reason the file cannot be opened. *virtual_file 279 // will be set to the virtual path as in the SUCCESS case, even though 280 // it is not useful. 281 // * NO_MAPPING: Indicates that no mapping was found which contains this 282 // file. 283 DiskFileToVirtualFileResult DiskFileToVirtualFile( 284 absl::string_view disk_file, std::string* virtual_file, 285 std::string* shadowing_disk_file); 286 287 // Given a virtual path, find the path to the file on disk. 288 // Return true and update disk_file with the on-disk path if the file exists. 289 // Return false and leave disk_file untouched if the file doesn't exist. 290 bool VirtualFileToDiskFile(absl::string_view virtual_file, 291 std::string* disk_file); 292 293 // implements SourceTree ------------------------------------------- 294 io::ZeroCopyInputStream* Open(absl::string_view filename) override; 295 296 std::string GetLastErrorMessage() override; 297 298 private: 299 struct Mapping { 300 std::string virtual_path; 301 std::string disk_path; 302 MappingMapping303 inline Mapping(std::string virtual_path_param, std::string disk_path_param) 304 : virtual_path(std::move(virtual_path_param)), 305 disk_path(std::move(disk_path_param)) {} 306 }; 307 std::vector<Mapping> mappings_; 308 std::string last_error_message_; 309 310 // Like Open(), but returns the on-disk path in disk_file if disk_file is 311 // non-NULL and the file could be successfully opened. 312 io::ZeroCopyInputStream* OpenVirtualFile(absl::string_view virtual_file, 313 std::string* disk_file); 314 315 // Like Open() but given the actual on-disk path. 316 io::ZeroCopyInputStream* OpenDiskFile(absl::string_view filename); 317 }; 318 319 } // namespace compiler 320 } // namespace protobuf 321 } // namespace google 322 323 #include "google/protobuf/port_undef.inc" 324 325 #endif // GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ 326