zeek/src/zeekygen/Manager.h

276 lines
9.2 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <sys/stat.h>
#include <cerrno>
#include <ctime>
#include <map>
#include <string>
#include <vector>
#include "zeek/ID.h"
#include "zeek/Reporter.h"
#include "zeek/zeekygen/Configuration.h"
#include "zeek/zeekygen/SpicyModuleInfo.h"
namespace zeek {
class TypeDecl;
namespace zeekygen::detail {
class PackageInfo;
class ScriptInfo;
/**
* Map of info objects. Just a wrapper around std::map to improve code
* readability (less typedefs for specific map types and not having to use
* iterators directly to find a particular info object).
*/
template<class T>
struct InfoMap {
using map_type = std::map<std::string, T*>;
/**
* @param name Name of an info object to retrieve.
* @return The info object associated with \a name.
*/
T* GetInfo(const std::string& name) const {
typename map_type::const_iterator it = map.find(name);
return it == map.end() ? 0 : it->second;
}
map_type map;
};
/**
* Manages all documentation tracking and generation.
*/
class Manager {
public:
/**
* Ctor.
* @param config Path to a Zeekygen config file if documentation is to be
* written to disk.
* @param command The command used to invoke the Zeek process.
* It's used when checking for out-of-date targets. If the Zeek binary is
* newer then a target, it needs to be rebuilt.
*/
Manager(const std::string& config, const std::string& command);
/**
* Dtor.
*/
~Manager();
/**
* Do initialization that needs to happen before scripts are parsed.
* Currently nothing outside of what's done in ctor is needed.
*/
void InitPreScript();
/**
* Do initialization that needs to happen after scripts are parsed.
* This is primarily dependency resolution/filtering.
*/
void InitPostScript();
/**
* Builds all Zeekygen targets specified by config file and write out
* documentation to disk.
*/
void GenerateDocs() const;
/**
* Register Zeek script for which information/documentation will be gathered.
* @param path Absolute path to Zeek script.
*/
void Script(const std::string& path);
/**
* Register Zeek script dependency ("@load").
* @param path Absolute path to a Zeek script.
* @param dep Absolute path to a Zeek script being "@load"d from script given
* by \a path.
*/
void ScriptDependency(const std::string& path, const std::string& dep);
/**
* Register a module usage (script may export identifiers into the
* module namespace).
* @param path Absolute path to a Zeek script.
* @param module The module which script given by \a path is using.
*/
void ModuleUsage(const std::string& path, const std::string& module);
/**
* Signal that a record or enum type is now being parsed.
* @param id The record or enum type identifier.
*/
void StartType(zeek::detail::IDPtr id);
/**
* Register a script-level identifier for which information/documentation
* will be gathered.
* @param id The script-level identifier.
* @param from_redef The identifier was created from a redef (e.g. an enum).
*/
void Identifier(zeek::detail::IDPtr id, bool from_redef = false);
/**
* Register a record-field for which information/documentation will be
* gathered.
* @param id The identifier of the record type which has the field.
* @param field The field name/type information.
* @param path Absolute path to a Zeek script in which this field is
* declared. This can be different from the place where the record type
* is declared due to redefs.
* @param from_redef The field is from a record redefinition.
*/
void RecordField(const zeek::detail::ID* id, const TypeDecl* field, const std::string& path, bool from_redef);
/**
* Register a redefinition of a particular identifier.
* @param id The identifier being redef'd.
* @param path Absolute path to a Zeek script doing the redef.
* @param ic The initialization class that was used (e.g. =, +=, -=).
* @param init_expr The initialization expression that was used.
*/
void Redef(const zeek::detail::ID* id, const std::string& path, zeek::detail::InitClass ic,
zeek::detail::ExprPtr init_expr);
void Redef(const zeek::detail::ID* id, const std::string& path,
zeek::detail::InitClass ic = zeek::detail::INIT_NONE);
/**
* Register a Spicy EVT module.
* @param info the module information
*/
void AddSpicyModule(std::unique_ptr<SpicyModuleInfo> info) {
spicy_modules.map[info->Name()] = info.get();
all_info.push_back(info.release()); // switch to manual memory mgmt like all other infos
}
const auto& SpicyModules() const { return spicy_modules.map; }
/**
* Register Zeekygen script summary content.
* @param path Absolute path to a Zeek script.
* @param comment Zeekygen-style summary comment ("##!") to associate with
* script given by \a path.
*/
void SummaryComment(const std::string& path, const std::string& comment);
/**
* Register a Zeekygen comment ("##") for an upcoming identifier (i.e.
* this content is buffered and consumed by next identifier/field
* declaration.
* @param comment Content of the Zeekygen comment.
*/
void PreComment(const std::string& comment);
/**
* Register a Zeekygen comment ("##<") for the last identifier seen.
* @param comment Content of the Zeekygen comment.
* @param identifier_hint Expected name of identifier with which to
* associate \a comment.
*/
void PostComment(const std::string& comment, const std::string& identifier_hint = "");
/**
* @param id Name of script-level enum identifier.
* @return The name of the enum's type.
*/
std::string GetEnumTypeName(const std::string& id) const;
/**
* @param name Name of a script-level identifier.
* @return an identifier info object associated with \a name or a null
* pointer if it's not a known identifier.
*/
IdentifierInfo* GetIdentifierInfo(const std::string& name) const { return identifiers.GetInfo(name); }
/**
* @param name Name of a Zeek script ("normalized" to be a path relative
* to a component within ZEEKPATH).
* @return a script info object associated with \a name or a null pointer
* if it's not a known script name.
*/
ScriptInfo* GetScriptInfo(const std::string& name) const { return scripts.GetInfo(name); }
/**
* @param name Name of a Zeek script package ("normalized" to be a path
* relative to a component within ZEEKPATH).
* @return a package info object associated with \a name or a null pointer
* if it's not a known package name.
*/
PackageInfo* GetPackageInfo(const std::string& name) const { return packages.GetInfo(name); }
/**
* Check if a Zeekygen target is up-to-date.
* @param target_file output file of a Zeekygen target.
* @param dependencies all dependencies of the target.
* @return true if modification time of \a target_file is newer than
* modification time of Zeek binary, Zeekygen config file, and all
* dependencies, else false.
*/
template<class T>
bool IsUpToDate(const std::string& target_file, const std::vector<T*>& dependencies) const;
/** Returns true if Zeekygen is enabled. */
bool IsEnabled() const { return ! disabled; }
private:
using comment_buffer_t = std::vector<std::string>;
using comment_buffer_map_t = std::map<std::string, comment_buffer_t>;
IdentifierInfo* CreateIdentifierInfo(zeek::detail::IDPtr id, ScriptInfo* script, bool from_redef = false);
bool disabled = false;
comment_buffer_t comment_buffer; // For whatever next identifier comes in.
comment_buffer_map_t comment_buffer_map; // For a particular identifier.
InfoMap<PackageInfo> packages;
InfoMap<ScriptInfo> scripts;
InfoMap<IdentifierInfo> identifiers;
InfoMap<SpicyModuleInfo> spicy_modules;
std::vector<Info*> all_info;
IdentifierInfo* last_identifier_seen = nullptr;
IdentifierInfo* incomplete_type = nullptr;
std::map<std::string, std::string> enum_mappings; // enum id -> enum type id
Config config;
time_t mtime = 0;
};
template<class T>
bool Manager::IsUpToDate(const std::string& target_file, const std::vector<T*>& dependencies) const {
struct stat s;
if ( stat(target_file.c_str(), &s) < 0 ) {
if ( errno == ENOENT )
// Doesn't exist.
return false;
reporter->InternalError("Zeekygen failed to stat target file '%s': %s", target_file.c_str(), strerror(errno));
}
if ( difftime(mtime, s.st_mtime) > 0 )
return false;
if ( difftime(config.GetModificationTime(), s.st_mtime) > 0 )
return false;
for ( size_t i = 0; i < dependencies.size(); ++i )
if ( difftime(dependencies[i]->GetModificationTime(), s.st_mtime) > 0 )
return false;
return true;
}
} // namespace zeekygen::detail
namespace detail {
extern zeekygen::detail::Manager* zeekygen_mgr;
} // namespace detail
} // namespace zeek