// file : build/scope -*- C++ -*- // copyright : Copyright (c) 2014-2015 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #ifndef BUILD_SCOPE #define BUILD_SCOPE #include // function #include #include #include #include #include #include #include namespace build { class scope { public: const dir_path& path () const {return *path_;} // Absolute and normalized. const dir_path& src_path () const {return *src_path_;} // Corresponding src path. const dir_path* src_path_ {nullptr}; // Cached src_{root,base} var value. scope* parent_scope () const {return parent_;} // Root scope of this scope or NULL if this scope is not (yet) // in any (known) project. Note that if the scope itself is // root, then this function return this. To get to the outer // root, query the root scope of the parent. // scope* root_scope () const {return root_;} bool root () const {return root_ == this;} // Variables. // public: variable_map vars; // Lookup, including in outer scopes. If you only want to lookup // in this scope, do it on the the variables map directly. // value_proxy operator[] (const variable&) const; value_proxy operator[] (const std::string& name) const { return operator[] (variable_pool.find (name)); } // Return a value_proxy suitable for assignment. If the variable // does not exist in this scope's map, then a new one with the // NULL value is added and returned. Otherwise the existing value // if returned. // value_proxy assign (const variable& var) { return vars.assign (var); } value_proxy assign (const std::string& name) { return assign (variable_pool.find (name)); } // Return a value_proxy suitable for appending. If the variable // does not exist in this scope's map, then outer scopes are // searched for the same variable. If found then a new variable // with the found value is added to this scope and returned. // Otherwise this function proceeds as assign(). // value_proxy append (const variable&); value_proxy append (const std::string& name) { return append (variable_pool.find (name)); } public: prerequisite_set prerequisites; // Meta/operations supported by this project (set on the root // scope only). // meta_operation_table meta_operations; operation_table operations; typedef build::path path_type; // Set of buildfiles already loaded for this scope. The included // buildfiles are checked against the project's root scope while // imported -- against the global scope (global_scope). // std::unordered_set buildfiles; private: friend class scope_map; friend class temp_scope; scope () = default; const dir_path* path_; // Pointer to the key in scope_map. scope* parent_; scope* root_; }; // Temporary scope. The idea is to be able to create a temporary // scope in order not to change the variables in the current scope. // Such a scope is not entered in to the scope map. As a result it // can only be used as a temporary set of variables. In particular, // defining targets/prerequisites directly in such a scope will surely // end up badly. Defining any nested scopes will be as if defining // such a scope in the parent (since path() returns parent's path). // class temp_scope: public scope { public: temp_scope (scope& p) {path_ = p.path_; parent_ = &p; root_ = p.root_;} }; using scope_map_base = butl::dir_path_map; class scope_map: public scope_map_base { public: // Note that we assume the first insertion into the map is that // of the global scope. // std::pair insert (const dir_path&, bool root); scope& operator[] (const dir_path& p) {return insert (p, false).first;} // Find the most qualified scope that encompasses this path. // scope& find (const dir_path&); scope& find (const path& p) { // Natural thing to do here would be to call find (p.directory ()). // However, there could be a situation where the passed path is a // directory (i.e., the calling code does not know what it is dealing // with), so let's use the whole path. // return find (dir_path (p.string ())); } }; extern scope_map scopes; extern scope* global_scope; } #endif // BUILD_SCOPE