Implement `--file-names-only` (#8788)

* flatc builds and seems to work, some of the extra targets are having linker errors

* fix build system

* pipeline failures

* un-rename files

* refactor to use unique_ptr

* typo

* rm make_unique, add comments

* fix cmake

---------

Co-authored-by: Wouter van Oortmerssen <aardappel@gmail.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 93dac0a..81b994e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -147,6 +147,8 @@
   include/flatbuffers/vector.h
   include/flatbuffers/vector_downward.h
   include/flatbuffers/verifier.h
+  src/file_manager.cpp
+  src/file_name_manager.cpp
   src/idl_parser.cpp
   src/idl_gen_text.cpp
   src/reflection.cpp
@@ -173,9 +175,6 @@
   src/idl_gen_grpc.cpp
   src/idl_gen_json_schema.cpp
   src/idl_gen_swift.cpp
-  src/file_name_saving_file_manager.cpp
-  src/file_binary_writer.cpp
-  src/file_writer.cpp
   src/idl_namer.h
   src/namer.h
   src/flatc.cpp
diff --git a/grpc/src/compiler/python_generator.cc b/grpc/src/compiler/python_generator.cc
index f217c9d..7e50266 100644
--- a/grpc/src/compiler/python_generator.cc
+++ b/grpc/src/compiler/python_generator.cc
@@ -76,31 +76,6 @@
   ss << "\n\n";
 }
 
-bool SaveStub(const std::string& filename, const Imports& imports,
-              const std::string& content) {
-  std::stringstream ss;
-  ss << "# Generated by the gRPC FlatBuffers compiler. DO NOT EDIT!\n"
-     << '\n'
-     << "from __future__ import annotations\n"
-     << '\n';
-  FormatImports(ss, imports);
-  ss << content << '\n';
-
-  EnsureDirExists(StripFileName(filename));
-  return flatbuffers::SaveFile(filename.c_str(), ss.str(), false);
-}
-
-bool SaveService(const std::string& filename, const Imports& imports,
-                 const std::string& content) {
-  std::stringstream ss;
-  ss << "# Generated by the gRPC FlatBuffers compiler. DO NOT EDIT!\n" << '\n';
-  FormatImports(ss, imports);
-  ss << content << '\n';
-
-  EnsureDirExists(StripFileName(filename));
-  return flatbuffers::SaveFile(filename.c_str(), ss.str(), false);
-}
-
 class BaseGenerator {
  protected:
   BaseGenerator(const Parser& parser, const Namer::Config& config,
@@ -175,6 +150,20 @@
   }
 
  private:
+  bool SaveStub(const std::string& filename, const Imports& imports,
+                const std::string& content) {
+    std::stringstream ss;
+    ss << "# Generated by the gRPC FlatBuffers compiler. DO NOT EDIT!\n"
+       << '\n'
+       << "from __future__ import annotations\n"
+       << '\n';
+    FormatImports(ss, imports);
+    ss << content << '\n';
+
+    EnsureDirExists(StripFileName(filename));
+    return parser_.opts.file_saver->SaveFile(filename.c_str(), ss.str(), false);
+  }
+
   void Generate(std::stringstream& ss, const ServiceDef* service,
                 Imports* imports) {
     imports->Import("grpc");
@@ -293,6 +282,18 @@
   }
 
  private:
+  bool SaveService(const std::string& filename, const Imports& imports,
+                   const std::string& content) {
+    std::stringstream ss;
+    ss << "# Generated by the gRPC FlatBuffers compiler. DO NOT EDIT!\n"
+       << '\n';
+    FormatImports(ss, imports);
+    ss << content << '\n';
+
+    EnsureDirExists(StripFileName(filename));
+    return parser_.opts.file_saver->SaveFile(filename.c_str(), ss.str(), false);
+  }
+
   void GenerateStub(std::stringstream& ss, const ServiceDef* service,
                     Imports* imports) {
     ss << "class " << service->name << "Stub";
diff --git a/include/flatbuffers/code_generator.h b/include/flatbuffers/code_generator.h
index cc4df7f..bfd771d 100644
--- a/include/flatbuffers/code_generator.h
+++ b/include/flatbuffers/code_generator.h
@@ -19,12 +19,14 @@
 
 #include <string>
 
+#include "flatbuffers/file_manager.h"
 #include "flatbuffers/idl.h"
 
 namespace flatbuffers {
 
 struct CodeGenOptions {
   std::string output_path;
+  FileSaver* file_saver{nullptr};
 };
 
 // A code generator interface for producing converting flatbuffer schema into
diff --git a/include/flatbuffers/file_manager.h b/include/flatbuffers/file_manager.h
index 0941fae..bafe6f2 100644
--- a/include/flatbuffers/file_manager.h
+++ b/include/flatbuffers/file_manager.h
@@ -17,30 +17,52 @@
 #ifndef FLATBUFFERS_FILE_MANAGER_H_
 #define FLATBUFFERS_FILE_MANAGER_H_
 
+#include <cstddef>
 #include <set>
 #include <string>
 
-#include "flatbuffers/util.h"
-
 namespace flatbuffers {
 
 // A File interface to write data to file by default or
 // save only file names
-class FileManager {
+class FileSaver {
  public:
-  FileManager() = default;
-  virtual ~FileManager() = default;
+  FileSaver() = default;
+  virtual ~FileSaver() = default;
 
-  virtual bool SaveFile(const std::string& absolute_file_name,
-                        const std::string& content) = 0;
+  virtual bool SaveFile(const char* name, const char* buf, size_t len,
+                        bool binary) = 0;
 
-  virtual bool LoadFile(const std::string& absolute_file_name,
-                        std::string* buf) = 0;
+  bool SaveFile(const char* name, const std::string& buf, bool binary) {
+    return SaveFile(name, buf.c_str(), buf.size(), binary);
+  }
+
+  virtual void Finish() {}
 
  private:
   // Copying is not supported.
-  FileManager(const FileManager&) = delete;
-  FileManager& operator=(const FileManager&) = delete;
+  FileSaver(const FileSaver&) = delete;
+  FileSaver& operator=(const FileSaver&) = delete;
+  // Rule of 5
+  FileSaver(FileSaver&&) = default;
+  FileSaver& operator=(FileSaver&&) = default;
+};
+
+class RealFileSaver final : public FileSaver {
+ public:
+  bool SaveFile(const char* name, const char* buf, size_t len,
+                bool binary) final;
+};
+
+class FileNameSaver final : public FileSaver {
+ public:
+  bool SaveFile(const char* name, const char* buf, size_t len,
+                bool binary) final;
+
+  void Finish() final;
+
+ private:
+  std::set<std::string> file_names_{};
 };
 
 }  // namespace flatbuffers
diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h
index 1c551e9..4139529 100644
--- a/include/flatbuffers/idl.h
+++ b/include/flatbuffers/idl.h
@@ -25,6 +25,7 @@
 #include <vector>
 
 #include "flatbuffers/base.h"
+#include "flatbuffers/file_manager.h"
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/flexbuffers.h"
 #include "flatbuffers/hash.h"
@@ -634,6 +635,10 @@
 
 // Container of options that may apply to any of the source/text generators.
 struct IDLOptions {
+  // file saver
+  // shared pointer since this object gets copied and modified.
+  FileSaver* file_saver = nullptr;
+
   // field case style options for C++
   enum CaseStyle { CaseStyle_Unchanged = 0, CaseStyle_Upper, CaseStyle_Lower };
   enum class ProtoIdGapAction { NO_OP, WARNING, ERROR };
diff --git a/include/flatbuffers/util.h b/include/flatbuffers/util.h
index 0313e74..d8387e1 100644
--- a/include/flatbuffers/util.h
+++ b/include/flatbuffers/util.h
@@ -448,12 +448,16 @@
 // If "binary" is false data is written using ifstream's
 // text mode, otherwise data is written with no
 // transcoding.
+// NOTE: this function is deprecated in favor of FileSaver class,
+// but left here for compatibility.
 bool SaveFile(const char* name, const char* buf, size_t len, bool binary);
 
 // Save data "buf" into file "name" returning true if
 // successful, false otherwise.  If "binary" is false
 // data is written using ifstream's text mode, otherwise
 // data is written with no transcoding.
+// NOTE: this function is deprecated in favor of FileSaver class,
+// but left here for compatibility.
 inline bool SaveFile(const char* name, const std::string& buf, bool binary) {
   return SaveFile(name, buf.c_str(), buf.size(), binary);
 }
diff --git a/src/BUILD.bazel b/src/BUILD.bazel
index 4b9fb7a..cefc0b8 100644
--- a/src/BUILD.bazel
+++ b/src/BUILD.bazel
@@ -103,9 +103,8 @@
         "bfbs_gen_nim.cpp",
         "bfbs_gen_nim.h",
         "bfbs_namer.h",
-        "file_binary_writer.cpp",
-        "file_name_saving_file_manager.cpp",
-        "file_writer.cpp",
+        "file_manager.cpp",
+        "file_name_manager.cpp",
         "flatc_main.cpp",
         "idl_gen_binary.cpp",
         "idl_gen_binary.h",
diff --git a/src/bfbs_gen_lua.cpp b/src/bfbs_gen_lua.cpp
index 01cd2d9..f218929 100644
--- a/src/bfbs_gen_lua.cpp
+++ b/src/bfbs_gen_lua.cpp
@@ -703,7 +703,7 @@
     EnsureDirExists(path);
     const std::string file_name =
         options_.output_path + path + "/" + namer_.File(name);
-    SaveFile(file_name.c_str(), code, false);
+    options_.file_saver->SaveFile(file_name.c_str(), code, false);
   }
 
   std::unordered_set<std::string> keywords_;
diff --git a/src/bfbs_gen_nim.cpp b/src/bfbs_gen_nim.cpp
index ec72930..d5ec2bd 100644
--- a/src/bfbs_gen_nim.cpp
+++ b/src/bfbs_gen_nim.cpp
@@ -718,7 +718,7 @@
     EnsureDirExists(path);
     const std::string file_name =
         options_.output_path + path + "/" + namer_.File(name);
-    SaveFile(file_name.c_str(), code, false);
+    options_.file_saver->SaveFile(file_name.c_str(), code, false);
   }
 
   std::unordered_set<std::string> keywords_;
diff --git a/src/file_binary_writer.cpp b/src/file_binary_writer.cpp
deleted file mode 100644
index 6fc762f..0000000
--- a/src/file_binary_writer.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2023 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fstream>
-#include <set>
-#include <string>
-
-#include "flatbuffers/file_manager.h"
-
-namespace flatbuffers {
-
-class FileBinaryWriter : public FileManager {
- public:
-  bool SaveFile(const std::string& absolute_file_name,
-                const std::string& content) override {
-    std::ofstream ofs(absolute_file_name, std::ofstream::binary);
-    if (!ofs.is_open()) return false;
-    ofs.write(content.c_str(), content.size());
-    return !ofs.bad();
-  }
-
-  bool Loadfile(const std::string& absolute_file_name, std::string* output) {
-    if (DirExists(absolute_file_name.c_str())) return false;
-    std::ifstream ifs(absolute_file_name, std::ifstream::binary);
-    if (!ifs.is_open()) return false;
-    // The fastest way to read a file into a string.
-    ifs.seekg(0, std::ios::end);
-    auto size = ifs.tellg();
-    (*output).resize(static_cast<size_t>(size));
-    ifs.seekg(0, std::ios::beg);
-    ifs.read(&(*output)[0], (*output).size());
-    return !ifs.bad();
-  }
-};
-
-}  // namespace flatbuffers
diff --git a/src/file_manager.cpp b/src/file_manager.cpp
new file mode 100644
index 0000000..13b1580
--- /dev/null
+++ b/src/file_manager.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fstream>
+#include <set>
+#include <string>
+
+#include "flatbuffers/file_manager.h"
+
+namespace flatbuffers {
+
+bool RealFileSaver::SaveFile(const char* name, const char* buf, size_t len,
+                             bool binary) {
+  std::ofstream ofs(name, binary ? std::ofstream::binary : std::ofstream::out);
+  if (!ofs.is_open()) return false;
+  ofs.write(buf, len);
+  return !ofs.bad();
+}
+
+}  // namespace flatbuffers
diff --git a/src/file_name_manager.cpp b/src/file_name_manager.cpp
new file mode 100644
index 0000000..aad63de
--- /dev/null
+++ b/src/file_name_manager.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+#include <tuple>
+
+#include "flatbuffers/file_manager.h"
+
+namespace flatbuffers {
+
+bool FileNameSaver::SaveFile(const char* name, const char* buf, size_t len,
+                             bool binary) {
+  (void)buf;
+  (void)len;
+  (void)binary;
+
+  std::ignore = file_names_.insert(name);
+
+  // we want to simulate always successful save
+  return true;
+}
+
+void FileNameSaver::Finish() {
+  for (const auto& file_name : file_names_) {
+    // Just print the file names to standard output.
+    // No actual file is created.
+    std::cout << file_name << "\n";
+  }
+}
+
+}  // namespace flatbuffers
diff --git a/src/file_name_saving_file_manager.cpp b/src/file_name_saving_file_manager.cpp
deleted file mode 100644
index f0a356c..0000000
--- a/src/file_name_saving_file_manager.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2023 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fstream>
-#include <set>
-#include <string>
-
-#include "flatbuffers/file_manager.h"
-
-namespace flatbuffers {
-
-class FileNameSavingFileManager : public FileManager {
- public:
-  FileNameSavingFileManager(std::set<std::string> file_names)
-      : file_names_(file_names) {}
-
-  bool SaveFile(const std::string& absolute_file_name,
-                const std::string& content) override {
-    (void)content;
-    auto pair = file_names_.insert(absolute_file_name);
-    // pair.second indicates whether the insertion is
-    // successful or not.
-    return pair.second;
-  }
-
-  bool Loadfile(const std::string& absolute_file_name, std::string* content) {
-    (void)absolute_file_name;
-    (void)content;
-    return false;
-  }
-
- private:
-  std::set<std::string> file_names_;
-};
-
-}  // namespace flatbuffers
diff --git a/src/file_writer.cpp b/src/file_writer.cpp
deleted file mode 100644
index 5cba285..0000000
--- a/src/file_writer.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2023 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fstream>
-#include <set>
-#include <string>
-
-#include "flatbuffers/file_manager.h"
-
-namespace flatbuffers {
-
-class FileWriter : public FileManager {
- public:
-  bool SaveFile(const std::string& absolute_file_name,
-                const std::string& content) override {
-    std::ofstream ofs(absolute_file_name, std::ofstream::out);
-    if (!ofs.is_open()) return false;
-    ofs.write(content.c_str(), content.size());
-    return !ofs.bad();
-  }
-
-  bool Loadfile(const std::string& absolute_file_name, std::string* output) {
-    if (DirExists(absolute_file_name.c_str())) return false;
-    std::ifstream ifs(absolute_file_name, std::ifstream::in);
-    if (!ifs.is_open()) return false;
-    // This is slower, but works correctly on all platforms for text files.
-    std::ostringstream oss;
-    oss << ifs.rdbuf();
-    *output = oss.str();
-    return !ifs.bad();
-  }
-};
-
-}  // namespace flatbuffers
diff --git a/src/flatc.cpp b/src/flatc.cpp
index 612f797..cab0ee3 100644
--- a/src/flatc.cpp
+++ b/src/flatc.cpp
@@ -716,7 +716,6 @@
         if (++argi >= argc) Error("missing path following: " + arg, true);
         options.annotate_schema = flatbuffers::PosixPath(argv[argi]);
       } else if (arg == "--file-names-only") {
-        // TODO (khhn): Provide 2 implementation
         options.file_names_only = true;
       } else if (arg == "--grpc-filename-suffix") {
         if (++argi >= argc) Error("missing gRPC filename suffix: " + arg, true);
@@ -963,6 +962,7 @@
         if (code_generator->SupportsBfbsGeneration()) {
           CodeGenOptions code_gen_options;
           code_gen_options.output_path = options.output_path;
+          code_gen_options.file_saver = options.opts.file_saver;
 
           const CodeGenerator::Status status = code_generator->GenerateCode(
               bfbs_buffer, bfbs_length, code_gen_options);
diff --git a/src/flatc_main.cpp b/src/flatc_main.cpp
index bcbb55d..ce9b8c4 100644
--- a/src/flatc_main.cpp
+++ b/src/flatc_main.cpp
@@ -181,9 +181,25 @@
       flatbuffers::NewTsCodeGenerator());
 
   // Create the FlatC options by parsing the command line arguments.
-  const flatbuffers::FlatCOptions& options =
+  flatbuffers::FlatCOptions options =
       flatc.ParseFromCommandLineArguments(argc, argv);
 
+  // this exists here to ensure file_saver outlives the compilation process
+  std::unique_ptr<flatbuffers::FileSaver> file_saver;
+  if (options.file_names_only) {
+    file_saver.reset(new flatbuffers::FileNameSaver{});
+  } else {
+    file_saver.reset(new flatbuffers::RealFileSaver{});
+  }
+
+  options.opts.file_saver = file_saver.get();
+  FLATBUFFERS_ASSERT(options.opts.file_saver);
+
   // Compile with the extracted FlatC options.
-  return flatc.Compile(options);
+  int success = flatc.Compile(options);
+
+  // print file names if file-names-only option is set
+  options.opts.file_saver->Finish();
+
+  return success;
 }
diff --git a/src/idl_gen_binary.cpp b/src/idl_gen_binary.cpp
index a028055..2afc6f7 100644
--- a/src/idl_gen_binary.cpp
+++ b/src/idl_gen_binary.cpp
@@ -45,12 +45,12 @@
     auto data_vec = parser.flex_builder_.GetBuffer();
     auto data_ptr = reinterpret_cast<char*>(data(data_vec));
     return !parser.flex_builder_.GetSize() ||
-           flatbuffers::SaveFile(
+           parser.opts.file_saver->SaveFile(
                BinaryFileName(parser, path, file_name).c_str(), data_ptr,
                parser.flex_builder_.GetSize(), true);
   }
   return !parser.builder_.GetSize() ||
-         flatbuffers::SaveFile(
+         parser.opts.file_saver->SaveFile(
              BinaryFileName(parser, path, file_name).c_str(),
              reinterpret_cast<char*>(parser.builder_.GetBufferPointer()),
              parser.builder_.GetSize(), true);
diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp
index d3441db..6207071 100644
--- a/src/idl_gen_cpp.cpp
+++ b/src/idl_gen_cpp.cpp
@@ -433,7 +433,8 @@
         GeneratedFileName(path_, file_name_ + "_bfbs", opts_);
     const auto final_code = code_.ToString();
 
-    return SaveFile(file_path.c_str(), final_code, false);
+    return parser_.opts.file_saver->SaveFile(file_path.c_str(), final_code,
+                                             false);
   }
 
   // Iterate through all definitions we haven't generate code for (enums,
@@ -764,8 +765,9 @@
     const auto final_code = code_.ToString();
 
     // Save the file and optionally generate the binary schema code.
-    return SaveFile(file_path.c_str(), final_code, false) &&
-           (!parser_.opts.binary_schema_gen_embed || generate_bfbs_embed());
+    return parser_.opts.file_saver->SaveFile(
+        file_path.c_str(), final_code,
+        (!parser_.opts.binary_schema_gen_embed || generate_bfbs_embed()));
   }
 
  private:
diff --git a/src/idl_gen_csharp.cpp b/src/idl_gen_csharp.cpp
index 223402f..f02a17f 100644
--- a/src/idl_gen_csharp.cpp
+++ b/src/idl_gen_csharp.cpp
@@ -241,7 +241,7 @@
     }
     filename +=
         options.filename_extension.empty() ? ".cs" : options.filename_extension;
-    return SaveFile(filename.c_str(), code, false);
+    return options.file_saver->SaveFile(filename.c_str(), code, false);
   }
 
   const Namespace* CurrentNameSpace() const { return cur_name_space_; }
diff --git a/src/idl_gen_dart.cpp b/src/idl_gen_dart.cpp
index d0b0930..589e5d9 100644
--- a/src/idl_gen_dart.cpp
+++ b/src/idl_gen_dart.cpp
@@ -163,7 +163,8 @@
       code += "\n";
       code += kv->second;
 
-      if (!SaveFile(Filename(kv->first).c_str(), code, false)) {
+      if (!parser_.opts.file_saver->SaveFile(Filename(kv->first).c_str(), code,
+                                             false)) {
         return false;
       }
     }
diff --git a/src/idl_gen_fbs.cpp b/src/idl_gen_fbs.cpp
index 1d7fd9a..da42895 100644
--- a/src/idl_gen_fbs.cpp
+++ b/src/idl_gen_fbs.cpp
@@ -409,7 +409,8 @@
             "When you use --proto, that you should check for conformity "
             "yourself, using the existing --conform");
   }
-  return SaveFile((path + file_name + ".fbs").c_str(), fbs, false);
+  return parser.opts.file_saver->SaveFile((path + file_name + ".fbs").c_str(),
+                                          fbs, false);
 }
 
 class FBSCodeGenerator : public CodeGenerator {
diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp
index 46585b3..9e743dd 100644
--- a/src/idl_gen_go.cpp
+++ b/src/idl_gen_go.cpp
@@ -113,7 +113,7 @@
       code += one_file_code;
       const std::string filename =
           GeneratedFileName(path_, file_name_, parser_.opts);
-      return SaveFile(filename.c_str(), code, false);
+      return parser_.opts.file_saver->SaveFile(filename.c_str(), code, false);
     }
 
     return true;
@@ -1627,7 +1627,7 @@
     std::string file = namer_.File(def, SkipFile::Suffix);
     EnsureDirExists(directory);
     std::string filename = directory + file;
-    return SaveFile(filename.c_str(), code, false);
+    return parser_.opts.file_saver->SaveFile(filename.c_str(), code, false);
   }
 
   // Create the full name of the imported namespace (format: A__B__C).
diff --git a/src/idl_gen_grpc.cpp b/src/idl_gen_grpc.cpp
index dc5bed3..476fa5f 100644
--- a/src/idl_gen_grpc.cpp
+++ b/src/idl_gen_grpc.cpp
@@ -331,7 +331,8 @@
           grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
       std::string filename =
           NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
-      if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
+      if (!parser_.opts.file_saver->SaveFile(filename.c_str(), output, false))
+        return false;
     }
     return true;
   }
@@ -393,11 +394,11 @@
       grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
       grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
 
-  return flatbuffers::SaveFile(
+  return parser.opts.file_saver->SaveFile(
              (path + file_name + ".grpc" + opts.grpc_filename_suffix + ".h")
                  .c_str(),
              header_code, false) &&
-         flatbuffers::SaveFile(
+         parser.opts.file_saver->SaveFile(
              (path + file_name + ".grpc" + opts.grpc_filename_suffix + ".cc")
                  .c_str(),
              source_code, false);
@@ -421,7 +422,8 @@
           grpc_java_generator::GenerateServiceSource(&file, service.get(), &p);
       std::string filename =
           NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java";
-      if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
+      if (!parser_.opts.file_saver->SaveFile(filename.c_str(), output, false))
+        return false;
     }
     return true;
   }
@@ -479,7 +481,8 @@
     }
     const auto final_code = code_.ToString();
     const auto filename = GeneratedFileName(path_, file_name_);
-    return SaveFile(filename.c_str(), final_code, false);
+    return parser_.opts.file_saver->SaveFile(filename.c_str(), final_code,
+                                             false);
   }
 
   static std::string GeneratedFileName(const std::string& path,
@@ -516,13 +519,16 @@
       auto service = file.service(i);
       code_ += grpc_ts_generator::Generate(&file, service.get(), file_name_);
       const auto ts_name = GeneratedFileName(path_, file_name_);
-      if (!SaveFile(ts_name.c_str(), code_.ToString(), false)) return false;
+      if (!parser_.opts.file_saver->SaveFile(ts_name.c_str(), code_.ToString(),
+                                             false))
+        return false;
 
       code_.Clear();
       code_ += grpc_ts_generator::GenerateInterface(&file, service.get(),
                                                     file_name_);
       const auto ts_interface_name = GeneratedFileName(path_, file_name_, true);
-      if (!SaveFile(ts_interface_name.c_str(), code_.ToString(), false))
+      if (!parser_.opts.file_saver->SaveFile(ts_interface_name.c_str(),
+                                             code_.ToString(), false))
         return false;
     }
     return true;
diff --git a/src/idl_gen_java.cpp b/src/idl_gen_java.cpp
index 143a361..32214e4 100644
--- a/src/idl_gen_java.cpp
+++ b/src/idl_gen_java.cpp
@@ -225,7 +225,7 @@
     EnsureDirExists(dirs);
     const std::string filename =
         dirs + namer_.File(defname, /*skips=*/SkipFile::Suffix);
-    return SaveFile(filename.c_str(), code, false);
+    return parser_.opts.file_saver->SaveFile(filename.c_str(), code, false);
   }
 
   const Namespace* CurrentNameSpace() const { return cur_name_space_; }
diff --git a/src/idl_gen_json_schema.cpp b/src/idl_gen_json_schema.cpp
index 9ab2905..e24d6ce 100644
--- a/src/idl_gen_json_schema.cpp
+++ b/src/idl_gen_json_schema.cpp
@@ -330,7 +330,7 @@
 
   bool save() const {
     const auto file_path = GeneratedFileName(path_, file_name_, parser_.opts);
-    return SaveFile(file_path.c_str(), code_, false);
+    return parser_.opts.file_saver->SaveFile(file_path.c_str(), code_, false);
   }
 
   const std::string getJson() { return code_; }
diff --git a/src/idl_gen_kotlin.cpp b/src/idl_gen_kotlin.cpp
index cda07a8..2ed89d4 100644
--- a/src/idl_gen_kotlin.cpp
+++ b/src/idl_gen_kotlin.cpp
@@ -156,7 +156,7 @@
     EnsureDirExists(dirs);
     const std::string filename =
         dirs + namer_.File(defname, /*skips=*/SkipFile::Suffix);
-    return SaveFile(filename.c_str(), code, false);
+    return parser_.opts.file_saver->SaveFile(filename.c_str(), code, false);
   }
 
   static bool IsEnum(const Type& type) {
diff --git a/src/idl_gen_kotlin_kmp.cpp b/src/idl_gen_kotlin_kmp.cpp
index 89bee94..571d452 100644
--- a/src/idl_gen_kotlin_kmp.cpp
+++ b/src/idl_gen_kotlin_kmp.cpp
@@ -161,7 +161,7 @@
     EnsureDirExists(dirs);
     const std::string filename =
         dirs + namer_.File(defname, /*skips=*/SkipFile::Suffix);
-    return SaveFile(filename.c_str(), code, false);
+    return parser_.opts.file_saver->SaveFile(filename.c_str(), code, false);
   }
 
   static bool IsEnum(const Type& type) {
diff --git a/src/idl_gen_lobster.cpp b/src/idl_gen_lobster.cpp
index 33c1071..dfe3ac8 100644
--- a/src/idl_gen_lobster.cpp
+++ b/src/idl_gen_lobster.cpp
@@ -403,8 +403,9 @@
       auto& struct_def = **it;
       GenStruct(struct_def, &code);
     }
-    return SaveFile(GeneratedFileName(path_, file_name_, parser_.opts).c_str(),
-                    code, false);
+    return parser_.opts.file_saver->SaveFile(
+        GeneratedFileName(path_, file_name_, parser_.opts).c_str(), code,
+        false);
   }
 
  private:
diff --git a/src/idl_gen_php.cpp b/src/idl_gen_php.cpp
index 48de73c..4b05703 100644
--- a/src/idl_gen_php.cpp
+++ b/src/idl_gen_php.cpp
@@ -95,7 +95,7 @@
 
     std::string filename =
         NamespaceDir(*def.defined_namespace) + def.name + ".php";
-    return SaveFile(filename.c_str(), code, false);
+    return parser_.opts.file_saver->SaveFile(filename.c_str(), code, false);
   }
 
   // Begin a class declaration.
diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp
index b034ec8..314c3c2 100644
--- a/src/idl_gen_python.cpp
+++ b/src/idl_gen_python.cpp
@@ -120,7 +120,7 @@
     ss << content.str() << '\n';
 
     EnsureDirExists(StripFileName(filename));
-    return flatbuffers::SaveFile(filename.c_str(), ss.str(), false);
+    return parser_.opts.file_saver->SaveFile(filename.c_str(), ss.str(), false);
   }
 
   static void DeclareUOffset(std::stringstream& stub, Imports* imports) {
@@ -2889,11 +2889,11 @@
          i = directories.find(kPathSeparator, i + 1)) {
       const std::string init_py =
           directories.substr(0, i) + kPathSeparator + "__init__.py";
-      SaveFile(init_py.c_str(), "", false);
+      parser_.opts.file_saver->SaveFile(init_py.c_str(), "", false);
     }
 
     const std::string filename = directories + defname;
-    return SaveFile(filename.c_str(), code, false);
+    return parser_.opts.file_saver->SaveFile(filename.c_str(), code, false);
   }
 
  private:
diff --git a/src/idl_gen_rust.cpp b/src/idl_gen_rust.cpp
index 22df1dc..05a58e6 100644
--- a/src/idl_gen_rust.cpp
+++ b/src/idl_gen_rust.cpp
@@ -336,8 +336,8 @@
       "Do not modify.";
   code += "// @generated";
   root_module.GenerateImports(code);
-  const bool success =
-      SaveFile((output_dir + "mod.rs").c_str(), code.ToString(), false);
+  const bool success = parser.opts.file_saver->SaveFile(
+      (output_dir + "mod.rs").c_str(), code.ToString(), false);
   code.Clear();
   return success;
 }
@@ -394,8 +394,8 @@
           namer_.Directories(*symbol.defined_namespace);
       EnsureDirExists(directories);
       const std::string file_path = directories + namer_.File(symbol);
-      const bool save_success =
-          SaveFile(file_path.c_str(), code_.ToString(), /*binary=*/false);
+      const bool save_success = parser_.opts.file_saver->SaveFile(
+          file_path.c_str(), code_.ToString(), /*binary=*/false);
       if (!save_success) return false;
     }
     return true;
@@ -496,7 +496,8 @@
 
     const auto file_path = GeneratedFileName(path_, file_name_, parser_.opts);
     const auto final_code = code_.ToString();
-    return SaveFile(file_path.c_str(), final_code, false);
+    return parser_.opts.file_saver->SaveFile(file_path.c_str(), final_code,
+                                             false);
   }
 
  private:
diff --git a/src/idl_gen_swift.cpp b/src/idl_gen_swift.cpp
index e0e2319..6e49109 100644
--- a/src/idl_gen_swift.cpp
+++ b/src/idl_gen_swift.cpp
@@ -191,7 +191,8 @@
 
     const auto filename = GeneratedFileName(path_, file_name_, parser_.opts);
     const auto final_code = code_.ToString();
-    return SaveFile(filename.c_str(), final_code, false);
+    return parser_.opts.file_saver->SaveFile(filename.c_str(), final_code,
+                                             false);
   }
 
   void GenerateCode() {
diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp
index ea1738c..6908305 100644
--- a/src/idl_gen_text.cpp
+++ b/src/idl_gen_text.cpp
@@ -440,8 +440,9 @@
   if (parser.opts.use_flexbuffers) {
     std::string json;
     parser.flex_root_.ToString(true, parser.opts.strict_json, json);
-    return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(),
-                                 json.c_str(), json.size(), true)
+    return parser.opts.file_saver->SaveFile(
+               TextFileName(path, file_name).c_str(), json.c_str(), json.size(),
+               true)
                ? nullptr
                : "SaveFile failed";
   }
@@ -449,8 +450,8 @@
   std::string text;
   auto err = GenText(parser, parser.builder_.GetBufferPointer(), &text);
   if (err) return err;
-  return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(), text,
-                               false)
+  return parser.opts.file_saver->SaveFile(TextFileName(path, file_name).c_str(),
+                                          text, false)
              ? nullptr
              : "SaveFile failed";
 }
diff --git a/src/idl_gen_ts.cpp b/src/idl_gen_ts.cpp
index aed7d59..446be30 100644
--- a/src/idl_gen_ts.cpp
+++ b/src/idl_gen_ts.cpp
@@ -178,7 +178,7 @@
     EnsureDirExists(dirs);
     auto basename = dirs + namer_.File(definition, SkipFile::Suffix);
 
-    return SaveFile(basename.c_str(), code, false);
+    return parser_.opts.file_saver->SaveFile(basename.c_str(), code, false);
   }
 
   void TrackNsDef(const Definition& definition, std::string type_name) {
@@ -319,7 +319,9 @@
         export_counter++;
       }
 
-      if (export_counter > 0) SaveFile(it.second.filepath.c_str(), code, false);
+      if (export_counter > 0)
+        parser_.opts.file_saver->SaveFile(it.second.filepath.c_str(), code,
+                                          false);
     }
   }
 
diff --git a/tests/fuzzer/CMakeLists.txt b/tests/fuzzer/CMakeLists.txt
index 03a5086..272c121 100644
--- a/tests/fuzzer/CMakeLists.txt
+++ b/tests/fuzzer/CMakeLists.txt
@@ -110,6 +110,8 @@
     ${FLATBUFFERS_DIR}/include/flatbuffers/vector.h
     ${FLATBUFFERS_DIR}/include/flatbuffers/vector_downward.h
     ${FLATBUFFERS_DIR}/include/flatbuffers/verifier.h
+    ${FLATBUFFERS_DIR}/src/file_manager.cpp
+    ${FLATBUFFERS_DIR}/src/file_name_manager.cpp
     ${FLATBUFFERS_DIR}/src/idl_parser.cpp
     ${FLATBUFFERS_DIR}/src/idl_gen_text.cpp
     ${FLATBUFFERS_DIR}/src/reflection.cpp
@@ -139,9 +141,8 @@
   ${FLATBUFFERS_DIR}/src/idl_gen_grpc.cpp
   ${FLATBUFFERS_DIR}/src/idl_gen_json_schema.cpp
   ${FLATBUFFERS_DIR}/src/idl_gen_swift.cpp
-  ${FLATBUFFERS_DIR}/src/file_name_saving_file_manager.cpp
-  ${FLATBUFFERS_DIR}/src/file_binary_writer.cpp
-  ${FLATBUFFERS_DIR}/src/file_writer.cpp
+  ${FLATBUFFERS_DIR}/src/file_manager.cpp
+  ${FLATBUFFERS_DIR}/src/file_name_manager.cpp
   ${FLATBUFFERS_DIR}/src/idl_namer.h
   ${FLATBUFFERS_DIR}/src/namer.h
   ${FLATBUFFERS_DIR}/src/flatc.cpp
@@ -260,7 +261,7 @@
       >
       -fno-limit-debug-info
   )
-  
+
   target_link_libraries(
     flatbuffers_nonfuzz
     PUBLIC
@@ -293,5 +294,5 @@
     COMMAND ${CMAKE_COMMAND} -E copy
     ${CMAKE_SOURCE_DIR}/../monster_test.bfbs
     ${CMAKE_CURRENT_BINARY_DIR}/monster_test.bfbs)
-  
+
 endif(BUILD_DEBUGGER)