Commit 68f08e89 authored by Martin Marinov's avatar Martin Marinov Committed by GitHub Enterprise

MTBR-691 refactor C++-related code from Journal::Stream::Impl to allow...

MTBR-691 refactor C++-related code from Journal::Stream::Impl to allow journals in other languages (#3)
parent f29115de
set(my_headers
${CMAKE_CURRENT_SOURCE_DIR}/JournalCpp.hh
${CMAKE_CURRENT_SOURCE_DIR}/JournalFileSystem.hh
${CMAKE_CURRENT_SOURCE_DIR}/JournalStream.hh
${CMAKE_CURRENT_SOURCE_DIR}/JournalStreamImpl.hh
${CMAKE_CURRENT_SOURCE_DIR}/JournalString.hh
PARENT_SCOPE
)
set(my_sources
${CMAKE_CURRENT_SOURCE_DIR}/JournalCpp.cc
${CMAKE_CURRENT_SOURCE_DIR}/JournalFileSystem.cc
${CMAKE_CURRENT_SOURCE_DIR}/JournalStream.cc
${CMAKE_CURRENT_SOURCE_DIR}/JournalStreamImpl.cc
${CMAKE_CURRENT_SOURCE_DIR}/JournalString.cc
PARENT_SCOPE
)
......@@ -34,8 +34,8 @@ namespace Journal
#error Please #include <Base/Journal/JournalStream.hh>
#endif//BASE_JOURNAL_STREAM_HH_INCLUDED
#ifndef BASE_ENVIRONMENT_HH_INCLUDED
#error Please #include <Base/Utils/Environment.hh>
#ifndef BASE_JOURNALFILESYSTEM_HH_INCLUDED
#error Please #include <Base/Journal/JournalFileSystem.hh>
#endif//BASE_ENVIRONMENT_HH_INCLUDED
#ifndef COMPONENT_NAME_STRING
......
// Copyright 2020 Autodesk, Inc. All rights reserved.
//
// This computer source code and related instructions and comments are the
// unpublished confidential and proprietary information of Autodesk, Inc.
// and are protected under applicable copyright and trade secret law. They
// may not be disclosed to, copied or used by any third party without the
// prior written consent of Autodesk, Inc.
#ifdef JOURNAL_ON
#include "Base/Security/Mandatory.hh"
#include "Base/Code/Quality.hh"
#include "JournalCpp.hh"
#include "JournalString.hh"
#include "Base/Utils/Environment.hh"
namespace Journal
{
Stream::Cpp::Cpp(const char* const _cmpn_name, const char* const _base_path)
: Stream::Impl(_cmpn_name, _base_path)
{
// jrnl_path_ should be set by make_directory() above
if (!open_new_file("journal.hh", hh_fs_, &hh_flnm_) ||
!open_new_file("journal.cc", cc_fs_))
{
throw INIT_FAILED;
}
}
Stream::Cpp::~Cpp()
{
os() << "return 0;" << ENDL;
close_scope();
}
void Stream::Cpp::line(const String& _str)
{
for (int i = 0; i < indt_; ++i) // add indent spaces
cc_fs_ << ' ';
cc_fs_ << _str;
cc_fs_.flush();
}
void Stream::Cpp::set_path_transform(const char* const _trns)
{
path_trns_ = _trns;
os() << "#define PATH(FLNM) " << _trns << ENDL;
}
void Stream::Cpp::transform_path(String& _path) const
{
if (!path_trns_.empty())
_path = String("PATH(\"") + _path + String("\")");
}
void Stream::Cpp::open_scope()
{
os() << "{" << ENDL;
indt_ += SCOPE_INDENT_SPACE_NMBR;
}
void Stream::Cpp::close_scope()
{
indt_ -= SCOPE_INDENT_SPACE_NMBR;
os() << "}" << ENDL;
}
void Stream::Cpp::include(const char* const _flnm)
{
const auto cmnt_name_lgnt = cmpn_name_.length();
const char* last_cmpn = _flnm;
for (const char* curr_cmpn = last_cmpn; curr_cmpn != nullptr;
curr_cmpn += cmnt_name_lgnt)
{
curr_cmpn = strstr(curr_cmpn, cmpn_name_.c_str());
if (curr_cmpn == nullptr)
break;
last_cmpn = curr_cmpn;
}
JOURNAL_ERROR_if_do(
last_cmpn == nullptr, "Invalid include: " << _flnm, return);
const auto& emit_include = [this](const char* const _flnm)
{// TODO: cache emitted includes and don't repeat them
hh_fs_ << "#include <" << _flnm << ">" << std::endl;
};
if (last_cmpn == _flnm)
{// header filename not part of the component package, just include it
emit_include(_flnm);
return;
}
String flnm; // remove the path before the component name
for (char c = *last_cmpn; c != '\0'; c = *(++last_cmpn))
{
if (c == '.')
{// replace .cc with .hh and exit
flnm += ".hh";
break;
}
else
{// replace folder separators (for windows)
if (c == '\\')
c = '/';
flnm += c;
}
}
emit_include(flnm.c_str());
}
void Stream::Cpp::main(const char* const _main_fnct)
{
blank_line();
add_includes();
blank_line();
os() << (_main_fnct == nullptr ? "int main()" : _main_fnct) << ENDL;
open_scope();
}
void Stream::Cpp::code_line(const char* const _line)
{
os() << _line;
end_line();
}
void Stream::Cpp::end_line()
{
os() << ';' << ENDL;
}
IOutputStream& Stream::Cpp::comment_os()
{
os() << "// ";
return *this;
}
void Stream::Cpp::add_includes()
{
os() << "#include \"" << hh_flnm_.c_str() + output_path().size() + 1
<< "\"" << ENDL; // include the header, skipping the sub-folder path
}
void Stream::Cpp::emit_arguments(const DataVector& _args)
{
os() << "(";
bool frst = true;
for (const auto& arg : _args)
{
if (frst)
frst = false;
else
os() << ", ";
os() << arg.str;
}
os() << ")";
};
void Stream::Cpp::emit_constructor(
const char* const _fnct, const Key& _obj, const DataVector& _args)
{
// Unlike other output pointers, constructor journaling does not require
// a name pre-booking, we assign the name here
const auto clss = class_name(_fnct);
os() << ENDL << "// Instantiate the " << clss << " SDK " << ENDL;
const auto name = book_unique_name(object_name(clss).c_str());
link_unique_name(_obj, name);
os() << "auto* " << name << " = new " << clss;
emit_arguments(_args);
end_line();
}
void Stream::Cpp::emit_destructor(const char* const /*_fnct*/, const Key& _obj)
{
os() << "delete " << find_unique_name(_obj);
end_line();
}
void Stream::Cpp::emit_method_call(
const char* const _fnct, const Key& _obj, const DataVector& _args)
{
os() << find_unique_name(_obj) << "->" << method_name(_fnct);
emit_arguments(_args);
}
void Stream::Cpp::emit_method_outcome(
const char* const _fnct, const Key& _obj, const DataVector& _args)
{
os() << "CHECK_OUTCOME(";
emit_method_call(_fnct, _obj, _args);
os() << ")";
end_line();
}
void Stream::Cpp::emit_method_void(
const char* const _fnct, const Key& _obj, const DataVector& _args)
{
emit_method_call(_fnct, _obj, _args);
end_line();
}
void Stream::Cpp::emit_method(const char* const _fnct, const Data& _rtrn,
const Key& _obj, const DataVector& _args)
{
// return variable should be pre-declared for now
os() << /*"auto " << */_rtrn.str << " = ";
emit_method_call(_fnct, _obj, _args);
end_line();
}
void Stream::Cpp::emit_function_call(const char* const _fnct, const DataVector& _args)
{
os() << function_name(_fnct);
emit_arguments(_args);
}
void Stream::Cpp::emit_function_outcome(
const char* const _fnct, const DataVector& _args)
{
os() << "CHECK_OUTCOME(";
emit_function_call(_fnct, _args);
os() << ")";
end_line();
}
void Stream::Cpp::emit_function_void(
const char* const _fnct, const DataVector& _args)
{
emit_function_call(_fnct, _args);
end_line();
}
void Stream::Cpp::emit_function(
const char* const _fnct, const Data& _rtrn, const DataVector& _args)
{
// return variable should be pre-declared for now
os() << /*"auto " << */_rtrn.str << " = ";
emit_function_call(_fnct, _args);
end_line();
}
}//namespace Journal
#endif//JOURNAL_ON
// Copyright 2020 Autodesk, Inc. All rights reserved.
//
// This computer source code and related instructions and comments are the
// unpublished confidential and proprietary information of Autodesk, Inc.
// and are protected under applicable copyright and trade secret law. They
// may not be disclosed to, copied or used by any third party without the
// prior written consent of Autodesk, Inc.
#ifdef JOURNAL_ON
#include <Base/Journal/JournalStreamImpl.hh>
#include <fstream>
namespace Journal
{
class Stream::Cpp : public Stream::Impl
{
public:
public:
Cpp(const char* const _cmpn_name, const char* const _base_path);
~Cpp() override;
// Language-specific API
Base::IOutputStream& comment_os() override;
void include(const char* const _flnm) override;
void main(const char* const _main_fnct) override;
void code_line(const char* const _line);
void end_line();
void emit_arguments(const DataVector& _args) override;
void emit_constructor(const char* const _fnct, const Key& _obj,
const DataVector& _args) override;
void emit_destructor(const char* const _fnct, const Key& _obj) override;
void emit_method_call(const char* const _fnct, const Key& _obj,
const DataVector& _args) override;
void emit_method_outcome(const char* const _fnct, const Key& _obj,
const DataVector& _args) override;
void emit_method_void(const char* const _fnct, const Key& _obj,
const DataVector& _args) override;
void emit_method(const char* const _fnct, const Data& _rtrn, const Key& _obj,
const DataVector& _args) override;
void emit_function_call(
const char* const _fnct, const DataVector& _args) override;
void emit_function_outcome(
const char* const _fnct, const DataVector& _args) override;
void emit_function_void(
const char* const _fnct, const DataVector& _args) override;
void emit_function(const char* const _fnct, const Data& _rtrn,
const DataVector& _args) override;
// Data file access API
void set_path_transform(const char* const _trns) override;
void transform_path(String& _path) const override;
protected:
void line(const String& _str) override;
private:
String hh_flnm_;
std::ofstream hh_fs_;
std::ofstream cc_fs_;
std::string path_trns_;
int indt_ = 0;
const int SCOPE_INDENT_SPACE_NMBR = 2;
private:
void open_scope();
void close_scope();
void add_includes();
};
}//namespace Journal
#endif//JOURNAL_ON
// Copyright 2020 Autodesk, Inc. All rights reserved.
//
// This computer source code and related instructions and comments are the
// unpublished confidential and proprietary information of Autodesk, Inc.
// and are protected under applicable copyright and trade secret law. They
// may not be disclosed to, copied or used by any third party without the
// prior written consent of Autodesk, Inc.
#ifdef JOURNAL_ON
#include "Base/Security/Mandatory.hh"
#include "Base/Code/Quality.hh"
#include "JournalFileSystem.hh"
#include "JournalString.hh"
#include "Base/Utils/Environment.hh"
#include <set>
#include <algorithm>
#ifdef JOURNAL_USE_BOOST
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#else // JOURNAL_USE_BOOST
// filesystem uses memcpy() in MSVC, suppress warnings
INSECURE_INCLUDE_SECTION_BEGIN
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <experimental/filesystem>
INSECURE_INCLUDE_SECTION_END
namespace fs = std::experimental::filesystem;
#endif // JOURNAL_USE_BOOST
namespace Journal {
bool environment_base_path(const char* const _cmpn_name, String& _base_path)
{
String envr_vrbl(_cmpn_name);
const auto to_upper = [](const char _c)
{
return static_cast<char>(::toupper(_c));
};
std::transform(
envr_vrbl.begin(), envr_vrbl.end(), envr_vrbl.begin(), to_upper);
envr_vrbl += "_JOURNAL_BASE";
return System::Environment::variable(envr_vrbl.c_str(), _base_path);
}
String compose_base_path(const char* const _cmpn_name)
{
String base_path;
if (environment_base_path(_cmpn_name, base_path))
return base_path; // environment variable defined, we are done
#ifdef _MSC_VER
// Get the Windows Local App Data folder
if (!System::Environment::variable("LOCALAPPDATA", base_path)) // undefined?
return base_path; // exit here
base_path += "\\";
#else // Unix-like systems
if (!System::Environment::variable("HOME", base_path)) // undefined?
return base_path; // exit here
base_path += "/.";
#endif // _MSC_VER
base_path += _cmpn_name;
return base_path;
}
String compose_journal_path(
const char* const _cmpn_name, const char* const _base_path)
{
fs::path jrnl_path;
if (_base_path == nullptr)
{
const auto base_path = compose_base_path(_cmpn_name);
if (base_path.empty())
{
jrnl_path /= ".";
jrnl_path /= _cmpn_name;
}
else
jrnl_path = base_path;
}
else
jrnl_path = _base_path;
jrnl_path /= "Journal";
return jrnl_path.string();
}
bool make_directory(String& _path)
{
const auto MAX_JOURNAL_NUMBER = 10;
auto compose_path = [&_path](const int _i)
{
return fs::path(_path + to_string(_i));
};
// store existing journal folders ordered by time and then lexicographically
typedef std::pair<std::time_t, fs::path> TimePath;
std::set<TimePath> fldrs;
fs::path path; // try to find a journal folder that does not exist yet
for (int i = 0; i < MAX_JOURNAL_NUMBER; ++i)
{
path = compose_path(i);
if (!fs::exists(path)) // the journal folder does not exist?
break;
// path exists, store it together with the last write time
auto time = fs::last_write_time(path);
fldrs.emplace(
#ifdef JOURNAL_USE_BOOST
time,
#else // JOURNAL_USE_BOOST
decltype(time)::clock::to_time_t(time),
#endif // JOURNAL_USE_BOOST
path);
}
if (fs::exists(path)) // all journal folders are already used
{// try to remove the oldest of the existing folders
for (const auto& fldr : fldrs) // iterate starting with the oldest path
{
#ifdef JOURNAL_USE_BOOST
boost::system::error_code err_code;
#else // JOURNAL_USE_BOOST
std::error_code err_code;
#endif // JOURNAL_USE_BOOST
path = fldr.second;
const fs::path path_tmpr(path.string() + "-tmpr");
fs::rename(path, path_tmpr, err_code);
if (err_code) // rename error, i.e., some file in the folder is locked
continue; // cleared the folder, can continue
fs::remove_all(path_tmpr, err_code);
// an error can be caused if another process is using the journal folder
if (!err_code) // no error?
break; // cleared the folder, can continue
}
// if this path still exists, then all journal folders are currently used
if (fs::exists(path)) //exist?
return false; // can't clear the directory, exit here
}
fs::path chkd_path;
for (auto path_it = path.begin(); path_it != path.end(); ++path_it)
{
chkd_path /= *path_it;
if (!fs::exists(chkd_path) && !fs::create_directories(chkd_path))
{
if (!fs::exists(chkd_path)) // cannot make the path for some reason?
return false; // exit
}
}
_path = path.string();
return true;
}
String compose_next_filename(const String& _jrnl_path, const char* const _name)
{
static int file_idx = 0;
// Create filename
const size_t PADDING_SIZE = 4;
auto flnm = to_string(file_idx++);
if (flnm.size() < PADDING_SIZE)
flnm.insert(flnm.begin(), PADDING_SIZE - flnm.size(), '0');
flnm += +"_" + String(_name);
fs::path full_flnm(_jrnl_path);
full_flnm /= flnm;
return full_flnm.string();
}
String extract_filename(const String& _path)
{
fs::path path(_path);
return path.filename().string();
}
}//namespace Journal
#endif//JOURNAL_ON
// Copyright 2020 Autodesk, Inc. All rights reserved.
//
// This computer source code and related instructions and comments are the
// unpublished confidential and proprietary information of Autodesk, Inc.
// and are protected under applicable copyright and trade secret law. They
// may not be disclosed to, copied or used by any third party without the
// prior written consent of Autodesk, Inc.
#ifndef BASE_JOURNALFILESYSTEM_HH_INCLUDED
#define BASE_JOURNALFILESYSTEM_HH_INCLUDED
#ifdef JOURNAL_ON
#include <string>
namespace Journal {
typedef std::string String;
/*!
Obtain the value of the environment variable toupper(cmpn_name)_JOURNAL_BASE
if it is defined, or otherwise get false and leave base_path unchanged.
*/
bool environment_base_path(const char* const _cmpn_name, String& _base_path);
/*!
Compose a base path from the component name as follows:
* from the environment variable toupper(cmpn_name)_JOURNAL_BASE if defined,
* from the local home data directory of the current user/cmpn_name, e.g.,
%LOCALAPP_DATA%/cmpn_name on Windows, or $HOME/.cmpn_name on Linux and OS X,
* the current folder sub-folder ./cmpn_name.
*/
String compose_base_path(const char* const _cmpn_name);
/*!
Compose the full journal path using the optionally provided base path. If a
base path is not provided, \ref compose_base_path() is used. If that fails for
some reason, the journal path is composed using the current path as base.
*/
String compose_journal_path(
const char* const _cmpn_name, const char* const _base_path = nullptr);
/*!
Make a new journal directory under the supplied path and return it in _path, or
return false if failed and leave _path unchanged.
*/
bool make_directory(String& _path);
//! Compose the next available filename in the supplied journal path
String compose_next_filename(const String& _jrnl_path, const char* const _name);
//! Extract the filename from a path
String extract_filename(const String& _path);
}//namespace Journal
#endif//JOURNAL_ON
#endif // BASE_JOURNALFILESYSTEM_HH_INCLUDED
This diff is collapsed.
......@@ -25,6 +25,9 @@ namespace Journal {
typedef std::string String;
const auto ENDL = Base::ENDL;
using Base::IOutputStream;
typedef size_t Key;
const Key INVALID_KEY = 0;
......@@ -46,7 +49,7 @@ typedef std::vector<Data> DataVector;
const Data NULL_PTR("nullptr");
// If Boost is available for use in Journal, we provide a default type_name()
// implementation based on RTTI and boost demangling. Otherwise type_info()
// implementation based on RTTI and Boost demangling. Otherwise type_info()
// has to be specialized for any type that is used as an output argument.
#ifdef JOURNAL_USE_BOOST
......@@ -210,10 +213,14 @@ public:
//! Direct access to the underlying stream, use with caution
Base::IOutputStream& os();
private:
class Impl;
Impl* impl_;
class Cpp;
class Scheme;
private:
template <typename... ArgT>
DataVector arguments(const ArgT&... _args)
......@@ -255,12 +262,6 @@ private:
static Stream* ptr_;