From 5e2047ab345c801e1e2ffbfb2e6c9ce6e753186c Mon Sep 17 00:00:00 2001 From: "Efimov Alexander/AI Tools Lab/./Samsung Electronics" Date: Mon, 13 Aug 2018 18:33:43 +0300 Subject: [PATCH] Generalize options for soft backend (#923) Replace specific file paths with output directory and common name Signed-off-by: Efimov Alexander --- contrib/nnc/README.md | 11 +-- .../nnc/libs/backend/soft/include/base_generator.h | 27 ++--- .../nnc/libs/backend/soft/include/c_generator.h | 7 +- .../nnc/libs/backend/soft/include/cpp_generator.h | 7 +- .../nnc/libs/backend/soft/include/soft_backend.h | 7 +- .../nnc/libs/backend/soft/src/base_generator.cpp | 97 +++++------------- contrib/nnc/libs/backend/soft/src/c_backend.cpp | 2 +- contrib/nnc/libs/backend/soft/src/c_generator.cpp | 7 +- contrib/nnc/libs/backend/soft/src/cpp_backend.cpp | 2 +- .../nnc/libs/backend/soft/src/cpp_generator.cpp | 16 ++- contrib/nnc/libs/backend/soft/src/serializer.cpp | 2 +- contrib/nnc/libs/backend/soft/src/soft_backend.cpp | 72 ++++++++------ contrib/nnc/unittests/soft_backend/generator.cpp | 110 +++++++++++---------- 13 files changed, 161 insertions(+), 206 deletions(-) diff --git a/contrib/nnc/README.md b/contrib/nnc/README.md index 42e9825..ee23a80 100644 --- a/contrib/nnc/README.md +++ b/contrib/nnc/README.md @@ -59,16 +59,13 @@ Assuming that current directory is a build root and we have tflite model(for exa ./nnc --plugins-path . \ --input-filename inceptionv3.tflite \ --emit-c++ \ ---out-code nn.cpp \ ---out-header nn.h \ ---out-model param.file

+--out-dir output \ +--out-name inception

``` ``--emit-c++`` is a _soft backend_ option that selects target language to emit(for now supported ``--emit-c++`` and ``--emit-c``) -``--out-code nn.cpp`` is a _soft backend_ option, it sets path to source code file +``--out-dir output`` is a _soft backend_ option, it sets path for output files, parameter is optional with default value ``out`` -``--out-header nn.h`` is a _soft backend_ option, it sets path to header file - -``--out-model param.file`` is a _soft backend_ option, it sets path to file with model parameters +``--out-name inception`` is a _soft backend_ option, it sets name for output files(in case of c++ it is ``inception.cpp``, ``inception.h``, ``inception.params``) diff --git a/contrib/nnc/libs/backend/soft/include/base_generator.h b/contrib/nnc/libs/backend/soft/include/base_generator.h index 17a3ca1..9825c4e 100644 --- a/contrib/nnc/libs/backend/soft/include/base_generator.h +++ b/contrib/nnc/libs/backend/soft/include/base_generator.h @@ -22,6 +22,15 @@ class Serializer; class BaseCodeGenerator { public: + + struct Parameters + { + // directory for output files + std::string _outDir; + // common name for output files + std::string _outName; + }; + void generate(nncc::contrib::core::IR::model::Graph *g); protected: @@ -32,20 +41,16 @@ protected: BaseCodeGenerator(BaseCodeGenerator &g) = default; - // check validity of selected output files, throws appropriate exception on error - void checkCorrectness(); - - BaseCodeGenerator(const std::string &headerFile, - const std::string &codeFile, const std::string &modelFile); + BaseCodeGenerator(const Parameters &opt); std::vector _formattedTensors; - // Code output file - std::string _headerFile; - // Code output file - std::string _codeFile; - // Model output file - std::string _modelFile; + // set of options + Parameters _params; + + std::string _headerPath; + std::string _codePath; + std::string _paramsPath; }; } // namespace soft diff --git a/contrib/nnc/libs/backend/soft/include/c_generator.h b/contrib/nnc/libs/backend/soft/include/c_generator.h index a19afc8..5bff59a 100644 --- a/contrib/nnc/libs/backend/soft/include/c_generator.h +++ b/contrib/nnc/libs/backend/soft/include/c_generator.h @@ -15,16 +15,13 @@ namespace soft // C generator class CCodeGenerator: public BaseCodeGenerator { - CCodeGenerator(const std::string &headerFile, const std::string &codeFile, const std::string &modelFile): - BaseCodeGenerator(headerFile, codeFile, modelFile) + CCodeGenerator(const Parameters ¶ms): BaseCodeGenerator(params) { // EMPTY } public: - static CCodeGenerator create(const std::string &headerFile, - const std::string &codeFile, - const std::string &modelFile); + static CCodeGenerator create(const Parameters ¶ms); protected: void formatTensorNames(const ModelAnalyzer &ma) override; diff --git a/contrib/nnc/libs/backend/soft/include/cpp_generator.h b/contrib/nnc/libs/backend/soft/include/cpp_generator.h index 46ff6f7..eb21e76 100644 --- a/contrib/nnc/libs/backend/soft/include/cpp_generator.h +++ b/contrib/nnc/libs/backend/soft/include/cpp_generator.h @@ -15,16 +15,13 @@ namespace soft // C++ generator class CPPCodeGenerator: public BaseCodeGenerator { - CPPCodeGenerator(const std::string &headerFile, const std::string &codeFile, const std::string &modelFile): - BaseCodeGenerator(headerFile, codeFile, modelFile) + CPPCodeGenerator(const Parameters ¶ms): BaseCodeGenerator(params) { // EMPTY } public: - static CPPCodeGenerator create(const std::string &headerFile, - const std::string &codeFile, - const std::string &modelFile); + static CPPCodeGenerator create(const Parameters ¶ms); protected: void formatTensorNames(const ModelAnalyzer &ma) override; diff --git a/contrib/nnc/libs/backend/soft/include/soft_backend.h b/contrib/nnc/libs/backend/soft/include/soft_backend.h index 679f8d2..4dfdaa9 100644 --- a/contrib/nnc/libs/backend/soft/include/soft_backend.h +++ b/contrib/nnc/libs/backend/soft/include/soft_backend.h @@ -3,6 +3,7 @@ #include "PluginInstance.h" #include "nnc/core/IR/model/graph/graph.h" +#include "base_generator.h" #include #include @@ -36,12 +37,10 @@ public: void setParam(const std::string &name, const std::string &value) override; protected: - std::string _outHeaderFile; - std::string _outCodeFile; - std::string _outModelFile; - const std::string _pluginName; + BaseCodeGenerator::Parameters _params; + // general options with some expected value std::map _opts; diff --git a/contrib/nnc/libs/backend/soft/src/base_generator.cpp b/contrib/nnc/libs/backend/soft/src/base_generator.cpp index b6671e5..79785e9 100644 --- a/contrib/nnc/libs/backend/soft/src/base_generator.cpp +++ b/contrib/nnc/libs/backend/soft/src/base_generator.cpp @@ -4,8 +4,10 @@ #include "PluginException.h" #include "nnc/core/IR/model/actions/ShapeInference.h" -#include +#include "param_constants.def" + #include +#include #include #include @@ -27,95 +29,41 @@ namespace backend namespace soft { -namespace parameters_format -{ -const int MAGIC_LEN = 4; -const int VERSION_LEN = 4; -const int HASH_LEN = 4; -const int HEADER_LEN = MAGIC_LEN + VERSION_LEN + HASH_LEN; - -const char MAGIC[MAGIC_LEN + 1] = "NNMP"; // Neural Network Model Parameters -} - namespace { -bool areFilesEqual(const struct stat &f1, const struct stat &f2) +unique_ptr getStream(const string &path) { - return f1.st_dev == f2.st_dev && f1.st_ino == f2.st_ino; -} - -bool fillFileStats(const string &path, struct stat &s) -{ - if (path.empty()) - return false; - if (stat(path.c_str(), &s)) + unique_ptr ofs(new ofstream(path)); + if (ofs->fail()) { - if (errno == ENOENT) - { - // file not found, try to create it - ofstream f(path); - if (!f.good()) - throw PluginException("Can not create output file: " + path); - // try again - int res = stat(path.c_str(), &s); - (void) res; - // on this step everythink should be fine, since we just created file - assert(!res); - return true; - } - else - throw PluginException("Can not get info for file: " + path); + throw PluginException("Can not open code output file: " + path); } - // check that selected file is regular or char device(to accept /dev/null, and simular) - if (!(s.st_mode & (S_IFREG | S_IFCHR))) - throw PluginException("Not a regular file: " + path); - return true; + return ofs; } -using ostream_ptr = unique_ptr; - -ostream_ptr getStream(const string &path) +void createDir(const string &path) { - if (path.empty()) - return ostream_ptr(&cout, [](ostream *){}); - ofstream *ofs = new ofstream(path); - if (ofs->fail()) + int res = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (res != 0 && errno != EEXIST) { - delete ofs; - throw PluginException("Can not open code output file: " + path); + throw PluginException("Failed to create output directory"); } - return ostream_ptr(ofs, [](ostream *ofs){delete ofs;}); } } // unnamed namespace -BaseCodeGenerator::BaseCodeGenerator(const string &headerFile, const string &codeFile, const string &modelFile): - _headerFile(headerFile), _codeFile(codeFile), _modelFile(modelFile) +BaseCodeGenerator::BaseCodeGenerator(const Parameters ¶ms): _params(params) { - // EMPTY -} - -void BaseCodeGenerator::checkCorrectness() -{ - // check that header, code and model file paths refers to different files. - // Exception: stdout could be common output stream - struct stat headerStat, codeStat, modelStat; - bool checkHeader = fillFileStats(_headerFile, headerStat); - bool checkCode = fillFileStats(_codeFile, codeStat); - bool checkModel = fillFileStats(_modelFile, modelStat); - - if ((checkHeader && checkCode && areFilesEqual(headerStat, codeStat)) || - (checkHeader && checkModel && areFilesEqual(headerStat, modelStat)) || - (checkCode && checkModel && areFilesEqual(codeStat, modelStat))) - { - throw PluginException("output files should not be equal"); - } + string basePath = _params._outDir + "/" + _params._outName; + _headerPath = basePath + ".h"; + _codePath = basePath + ".cpp"; + _paramsPath = basePath + ".params"; } void BaseCodeGenerator::materializeModelParams(ostream &out, const Serializer &s) { - using namespace parameters_format; + using namespace params; // First form a dump header char header[HEADER_LEN]; @@ -153,18 +101,21 @@ void BaseCodeGenerator::generate(Graph *g) serializer.serialize(ma.getInferenceSequence()); // rename tensors for specific backend language formatTensorNames(ma); + + createDir(_params._outDir); + // Print header - auto headerStream = getStream(_headerFile); + auto headerStream = getStream(_headerPath); materializeHeader(*headerStream, ma); headerStream.reset(); // Print code - auto codeStream = getStream(_codeFile); + auto codeStream = getStream(_codePath); materializeCode(*codeStream, ma, serializer); codeStream.reset(); // Print model parameters - auto modelStream = getStream(_modelFile); + auto modelStream = getStream(_paramsPath); materializeModelParams(*modelStream, serializer); modelStream.reset(); } diff --git a/contrib/nnc/libs/backend/soft/src/c_backend.cpp b/contrib/nnc/libs/backend/soft/src/c_backend.cpp index 173799e..6bc40fa 100644 --- a/contrib/nnc/libs/backend/soft/src/c_backend.cpp +++ b/contrib/nnc/libs/backend/soft/src/c_backend.cpp @@ -37,7 +37,7 @@ public: void generate(nncc::contrib::core::IR::model::Graph *g) override { - CCodeGenerator::create(_outHeaderFile, _outCodeFile, _outModelFile).generate(g); + CCodeGenerator::create(_params).generate(g); } static CSoftBackend &getInstance() diff --git a/contrib/nnc/libs/backend/soft/src/c_generator.cpp b/contrib/nnc/libs/backend/soft/src/c_generator.cpp index 074351e..122de00 100644 --- a/contrib/nnc/libs/backend/soft/src/c_generator.cpp +++ b/contrib/nnc/libs/backend/soft/src/c_generator.cpp @@ -14,12 +14,9 @@ namespace backend namespace soft { -CCodeGenerator CCodeGenerator::create(const string &headerFile, - const string &codeFile, - const string &modelFile) +CCodeGenerator CCodeGenerator::create(const Parameters ¶ms) { - CCodeGenerator gen(headerFile, codeFile, modelFile); - gen.checkCorrectness(); + CCodeGenerator gen(params); return gen; } diff --git a/contrib/nnc/libs/backend/soft/src/cpp_backend.cpp b/contrib/nnc/libs/backend/soft/src/cpp_backend.cpp index a0a3ae4..8d3e5d9 100644 --- a/contrib/nnc/libs/backend/soft/src/cpp_backend.cpp +++ b/contrib/nnc/libs/backend/soft/src/cpp_backend.cpp @@ -37,7 +37,7 @@ public: void generate(nncc::contrib::core::IR::model::Graph *g) override { - CPPCodeGenerator::create(_outHeaderFile, _outCodeFile, _outModelFile).generate(g); + CPPCodeGenerator::create(_params).generate(g); } static CPPSoftBackend &getInstance() diff --git a/contrib/nnc/libs/backend/soft/src/cpp_generator.cpp b/contrib/nnc/libs/backend/soft/src/cpp_generator.cpp index e552d31..3435329 100644 --- a/contrib/nnc/libs/backend/soft/src/cpp_generator.cpp +++ b/contrib/nnc/libs/backend/soft/src/cpp_generator.cpp @@ -3,16 +3,16 @@ #include "serializer.h" #include "PluginException.h" -#include "param_constants.def" - using namespace std; using namespace nncc::contrib; using namespace nncc::contrib::core::IR::model; #include "cpp_header_types.h" #include "cpp_operations.h" -#include "param_constants.h" +#include "param_constants.def" + +#include "param_constants.h" #include "eigen.h" #include "cpp_common_funcs.h" #include "cpp_add_bias.h" @@ -34,12 +34,9 @@ namespace backend namespace soft { -CPPCodeGenerator CPPCodeGenerator::create(const std::string &headerFile, - const std::string &codeFile, - const std::string &modelFile) +CPPCodeGenerator CPPCodeGenerator::create(const Parameters ¶ms) { - CPPCodeGenerator gen(headerFile, codeFile, modelFile); - gen.checkCorrectness(); + CPPCodeGenerator gen(params); return gen; } @@ -171,7 +168,6 @@ void CPPCodeGenerator::materializeInferenceSequence(ostream &out, const ModelAna for (const ModelAnalyzer::OpDescr &op: ma.getInferenceSequence()) { using Type = OpDescr::Type; - using TensorDescription = ModelAnalyzer::TensorDescription; if (op._type == Type::IN) continue; // create temporary tensors @@ -199,7 +195,7 @@ void CPPCodeGenerator::materializeCode(ostream &out, const ModelAnalyzer &ma, co { string className = ma.getModelName() + "Model"; - out << "#include \"" << _headerFile << "\"\n"; + out << "#include \"" << _params._outName << ".h\"\n"; // put operations from tflite out.write(eigen, sizeof(eigen)); diff --git a/contrib/nnc/libs/backend/soft/src/serializer.cpp b/contrib/nnc/libs/backend/soft/src/serializer.cpp index 6828238..dd682f2 100644 --- a/contrib/nnc/libs/backend/soft/src/serializer.cpp +++ b/contrib/nnc/libs/backend/soft/src/serializer.cpp @@ -105,7 +105,7 @@ void Serializer::serializePads(const Op &op, uint32_t padsRank) // serialize pads assert(padsRank < MAX_DIMS); serializeT(padsRank); - for (int i = 0; i < padsRank; ++i) + for (int i = 0; i < static_cast(padsRank); ++i) { auto pad = op.getPadding(i); assert(pad <= MAX_DIM_SIZE); diff --git a/contrib/nnc/libs/backend/soft/src/soft_backend.cpp b/contrib/nnc/libs/backend/soft/src/soft_backend.cpp index ca8188e..c3fa362 100644 --- a/contrib/nnc/libs/backend/soft/src/soft_backend.cpp +++ b/contrib/nnc/libs/backend/soft/src/soft_backend.cpp @@ -9,6 +9,9 @@ #include "PluginType.h" #include "nnc/core/IR/model/graph/graph.h" +#include +#include + using namespace std; using namespace nncc::contrib; using namespace nncc::contrib::config; @@ -31,9 +34,8 @@ const string pluginVersion = "0.01"; const string pluginDesc = "Generates source code for selected programming language from IR"; const PluginType pluginType = typeBackEnd; -const char *outCodeOpt = "out-code"; -const char *outHeaderOpt = "out-header"; -const char *outModelOpt = "out-model"; +const char *outDir = "out-dir"; +const char *outName = "out-name"; } // unnamed namespace // Helpers @@ -41,17 +43,15 @@ enum class OPT_ID { INVALID, TARGET, - OUT_HEADER, - OUT_CODE, - OUT_MODEL + OUT_DIR, + OUT_NAME }; void BaseSoftBackend::fillSession() { const static map info = {{"module description", pluginDesc}}; - const static vector moduleParams = {{outCodeOpt, "output file for code", true}, - {outHeaderOpt, "output file for header", true}, - {outModelOpt, "output file for model parameters", true}}; + const static vector moduleParams = {{outDir, "path to output directory", true}, + {outName, "common name for generated files", true}}; AbstractPluginInstance::fillSessionBase(pluginType, pluginVersion, _pluginName); @@ -64,7 +64,33 @@ void BaseSoftBackend::fillSession() void BaseSoftBackend::checkConfig() { - // Nothing to check for now + if (_params._outName.empty()) + { + throw PluginException("Output file name should not be empty"); + } + if (_params._outDir.empty()) + { + throw PluginException("Output directory should not be empty"); + } + auto dir = opendir(_params._outDir.c_str()); + if (dir) + { + closedir(dir); + return; + } + auto err = errno; + switch (err) + { + case ENOENT: + return; + case ENOTDIR: + throw PluginException("Output path is not directory"); + case EACCES: + throw PluginException("Has no permission to open output directory"); + default: + throw PluginException("Can not open output directory"); + } + } void *BaseSoftBackend::execute(void *data) @@ -82,9 +108,7 @@ void BaseSoftBackend::setParam(const string &name) switch (optionIt->second) { case OPT_ID::TARGET: - { break; - } default: throw ConfigException("[" + _pluginName + "] Unsupported flag parameter <" + name + ">"); } @@ -95,33 +119,23 @@ void BaseSoftBackend::setParam(const string &name, const string &value) auto optionIt = _opts.find(name); switch (optionIt->second) { - case OPT_ID::OUT_HEADER: - { - _outHeaderFile = value; - break; - } - case OPT_ID::OUT_CODE: - { - _outCodeFile = value; + case OPT_ID::OUT_DIR: + _params._outDir = value; break; - } - case OPT_ID::OUT_MODEL: - { - _outModelFile = value; + case OPT_ID::OUT_NAME: + _params._outName = value; break; - } default: throw ConfigException("[" + _pluginName + "] Unsupported parameter with value <" + name + ">"); } } BaseSoftBackend::BaseSoftBackend(const std::string &name, const std::string &target_opt): - _pluginName(name) + _pluginName(name), _params({"out", "nnmodel"}) { _flags[target_opt] = OPT_ID::TARGET; - _opts[outCodeOpt] = OPT_ID::OUT_CODE; - _opts[outHeaderOpt] = OPT_ID::OUT_HEADER; - _opts[outModelOpt] = OPT_ID::OUT_MODEL; + _opts[outDir] = OPT_ID::OUT_DIR; + _opts[outName] = OPT_ID::OUT_NAME; } } // namespace soft diff --git a/contrib/nnc/unittests/soft_backend/generator.cpp b/contrib/nnc/unittests/soft_backend/generator.cpp index 0aeb150..62600b2 100644 --- a/contrib/nnc/unittests/soft_backend/generator.cpp +++ b/contrib/nnc/unittests/soft_backend/generator.cpp @@ -10,10 +10,12 @@ #include #include #include +#include using namespace std; using namespace nncc::contrib; using namespace nncc::contrib::backend::soft; +using namespace nncc::contrib::core::IR::model; static bool isFileExists(const string &path) { @@ -21,76 +23,76 @@ static bool isFileExists(const string &path) return f.good(); } -static void createFile(const string &path) +static void deleteFile(const string &path) { - assert(!isFileExists(path)); - ofstream f(path); - assert(f.good()); - f << "hello world\n"; + int res = remove(path.c_str()); + assert(!res && "failed to remove file"); } -static void createDir(const string &path) +int removeRec(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { - int res = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - assert(!res); + deleteFile(fpath); + return 0; } -static void deleteFile(const string &path) +static void deleteDir(const string &path) { - int res = remove(path.c_str()); - assert(!res && "failed to remove file"); + int res = nftw(path.c_str(), removeRec, 1, FTW_DEPTH | FTW_PHYS); + assert(!res && "failed to remove dir"); } -static void deleteDir(const string &path) +static void checkOutputExists(const string &commonPath) { - int res = rmdir(path.c_str()); - assert(!res && "failed to remove dir"); + ASSERT_TRUE(isFileExists(commonPath + ".h")); + ASSERT_TRUE(isFileExists(commonPath + ".cpp")); + ASSERT_TRUE(isFileExists(commonPath + ".params")); +} + +static void emptyFile(const string &path) +{ + ofstream of(path); } -TEST(Generator, check_path_consistency) +TEST(Generator, check_generator_call) { // assume here that c++ and c code generators behave identically in terms of parameters check // test only c++ generator - const char *TEST_FILE1 = "test_file1"; - const char *TEST_FILE2 = "test_file2"; - const char *TEST_DIR = "test_dir"; - - // iterate this arrays to check that all arguments are working equally - const char *fileNames1[] = {"", "" , TEST_FILE1, "", ""}; - const char *fileNames2[] = {TEST_FILE1, TEST_FILE1, "", TEST_FILE1, TEST_FILE1}; - const char *fileNames3[] = {TEST_FILE1, TEST_FILE2, "", TEST_FILE1, TEST_FILE2}; - const char *dirNames[] = {"", "" , TEST_DIR, "", ""}; - - assert(!isFileExists(TEST_DIR) && "remove test_dir"); - assert(!isFileExists(TEST_FILE2) && "remove test_file2"); - createDir(TEST_DIR); - for (int i = 0; i < 3; ++i) + #define TEST_DIR "out_dir" + #define TEST_NAME "someName" + #define BASE_NAME TEST_DIR "/" TEST_NAME + + nncc::contrib::core::IR::model::Graph g; + g.create("input"); + + // test that generator creates output dir and files + if (isFileExists(TEST_DIR)) { - // check that test file is created - assert(!isFileExists(TEST_FILE1) && "remove test_file1"); - CPPCodeGenerator::create(fileNames1[i], fileNames1[i + 1], fileNames1[i + 2]); - ASSERT_TRUE(isFileExists(TEST_FILE1)); - // file already exists - struct stat s1; - int res = stat(TEST_FILE1, &s1); - assert(!res); - CPPCodeGenerator::create(fileNames1[i], fileNames1[i + 1], fileNames1[i + 2]); - struct stat s2; - res = stat(TEST_FILE1, &s2); - assert(!res); - deleteFile(TEST_FILE1); - // check that file is not changed at this point - ASSERT_EQ(s1.st_ino, s2.st_ino); - ASSERT_EQ(s1.st_size, s2.st_size); - // check that dir is not valid output - EXPECT_THROW(CPPCodeGenerator::create(dirNames[i], dirNames[i + 1], dirNames[i + 2]), PluginException); - // check that equal files are not permitted - EXPECT_THROW(CPPCodeGenerator::create(fileNames2[i], fileNames2[i + 1], fileNames2[i + 2]), PluginException); - // check that different files are ok - CPPCodeGenerator::create(fileNames3[i], fileNames3[i + 1], fileNames3[i + 2]); - deleteFile(TEST_FILE1); + deleteDir(TEST_DIR); } + assert(!isFileExists(TEST_DIR) && "remove output dir"); + CPPCodeGenerator::create(BaseCodeGenerator::Parameters({TEST_DIR, TEST_NAME})).generate(&g); + checkOutputExists(BASE_NAME); + + // test that generator creates output files in existing empty dir + deleteFile(BASE_NAME ".h"); + deleteFile(BASE_NAME ".cpp"); + deleteFile(BASE_NAME ".params"); + CPPCodeGenerator::create(BaseCodeGenerator::Parameters({TEST_DIR, TEST_NAME})).generate(&g); + checkOutputExists(BASE_NAME); + + // test that generator rewrites existing files + emptyFile(BASE_NAME ".h"); + struct stat sBefore, sAfter; + int res = stat(BASE_NAME ".h", &sBefore); + assert(res == 0); + assert(sBefore.st_size == 0); + + CPPCodeGenerator::create(BaseCodeGenerator::Parameters({TEST_DIR, TEST_NAME})).generate(&g); + + res = stat(BASE_NAME ".h", &sAfter); + assert(res == 0); + + ASSERT_NE(sBefore.st_size, sAfter.st_size); + deleteDir(TEST_DIR); - deleteFile(TEST_FILE2); } - -- 2.7.4