diff --git a/ortools/base/BUILD.bazel b/ortools/base/BUILD.bazel index c51841cfbe..dbcdd14ec0 100644 --- a/ortools/base/BUILD.bazel +++ b/ortools/base/BUILD.bazel @@ -420,6 +420,21 @@ cc_library( ], ) +cc_library( + name = "temp_path", + srcs = ["temp_path.cc"], + hdrs = ["temp_path.h"], + deps = [ + ":base", + ":file", + "@com_google_absl//absl/log", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + ], +) + cc_library( name = "protobuf_util", hdrs = ["protobuf_util.h"], diff --git a/ortools/base/file.cc b/ortools/base/file.cc index 8819865503..43413a4b37 100644 --- a/ortools/base/file.cc +++ b/ortools/base/file.cc @@ -78,7 +78,7 @@ bool File::Close() { } // Deletes "this" on closing. -absl::Status File::Close(int flags) { +absl::Status File::Close(int /*flags*/) { absl::Status status; if (f_ == nullptr) { return status; @@ -168,8 +168,8 @@ void File::Init() {} namespace file { absl::Status Open(absl::string_view filename, absl::string_view mode, File** f, - int flags) { - if (flags == Defaults()) { + Options options) { + if (options == Defaults()) { *f = File::Open(filename, mode); if (*f != nullptr) { return absl::OkStatus(); @@ -179,23 +179,34 @@ absl::Status Open(absl::string_view filename, absl::string_view mode, File** f, absl::StrCat("Could not open '", filename, "'")); } -File* OpenOrDie(absl::string_view filename, absl::string_view mode, int flags) { +File* OpenOrDie(absl::string_view filename, absl::string_view mode, + Options options) { File* f; - CHECK_EQ(flags, Defaults()); + CHECK_EQ(options, Defaults()); f = File::Open(filename, mode); CHECK(f != nullptr) << absl::StrCat("Could not open '", filename, "'"); return f; } +absl::StatusOr GetContents(absl::string_view path, + Options options) { + absl::StatusOr contents_or = std::string(); + absl::Status status = GetContents(path, &contents_or.value(), options); + if (!status.ok()) { + contents_or = status; + } + return contents_or; +} + absl::Status GetContents(absl::string_view filename, std::string* output, - int flags) { + Options options) { File* file; - auto status = file::Open(filename, "r", &file, flags); + auto status = file::Open(filename, "r", &file, options); if (!status.ok()) return status; const int64_t size = file->Size(); if (file->ReadToString(output, size) == size) { - status.Update(file->Close(flags)); + status.Update(file->Close(options)); return status; } #if defined(_MSC_VER) @@ -212,13 +223,14 @@ absl::Status GetContents(absl::string_view filename, std::string* output, } #endif // _MSC_VER - file->Close(flags).IgnoreError(); // Even if ReadToString() fails! + file->Close(options).IgnoreError(); // Even if ReadToString() fails! return absl::Status(absl::StatusCode::kInvalidArgument, absl::StrCat("Could not read from '", filename, "'.")); } -absl::Status WriteString(File* file, absl::string_view contents, int flags) { - if (flags == Defaults() && file != nullptr && +absl::Status WriteString(File* file, absl::string_view contents, + Options options) { + if (options == Defaults() && file != nullptr && file->Write(contents.data(), contents.size()) == contents.size()) { return absl::OkStatus(); } @@ -228,12 +240,12 @@ absl::Status WriteString(File* file, absl::string_view contents, int flags) { } absl::Status SetContents(absl::string_view filename, absl::string_view contents, - int flags) { + Options options) { File* file; - auto status = file::Open(filename, "w", &file, flags); + auto status = file::Open(filename, "w", &file, options); if (!status.ok()) return status; - status = file::WriteString(file, contents, flags); - status.Update(file->Close(flags)); // Even if WriteString() fails! + status = file::WriteString(file, contents, options); + status.Update(file->Close(options)); // Even if WriteString() fails! return status; } @@ -313,8 +325,8 @@ void WriteProtoToFileOrDie(const google::protobuf::Message& proto, } absl::Status GetTextProto(absl::string_view filename, - google::protobuf::Message* proto, int flags) { - if (flags == Defaults()) { + google::protobuf::Message* proto, Options options) { + if (options == Defaults()) { if (ReadFileToProto(filename, proto)) return absl::OkStatus(); } return absl::Status( @@ -323,8 +335,9 @@ absl::Status GetTextProto(absl::string_view filename, } absl::Status SetTextProto(absl::string_view filename, - const google::protobuf::Message& proto, int flags) { - if (flags == Defaults()) { + const google::protobuf::Message& proto, + Options options) { + if (options == Defaults()) { if (WriteProtoToASCIIFile(proto, filename)) return absl::OkStatus(); } return absl::Status( @@ -333,9 +346,9 @@ absl::Status SetTextProto(absl::string_view filename, } absl::Status GetBinaryProto(const absl::string_view filename, - google::protobuf::Message* proto, const int flags) { + google::protobuf::Message* proto, Options options) { std::string str; - if (flags == Defaults() && ReadFileToString(filename, &str) && + if (options == Defaults() && ReadFileToString(filename, &str) && proto->ParseFromString(str)) { return absl::OkStatus(); } @@ -345,8 +358,9 @@ absl::Status GetBinaryProto(const absl::string_view filename, } absl::Status SetBinaryProto(absl::string_view filename, - const google::protobuf::Message& proto, int flags) { - if (flags == Defaults()) { + const google::protobuf::Message& proto, + Options options) { + if (options == Defaults()) { if (WriteProtoToFile(proto, filename)) return absl::OkStatus(); } return absl::Status( @@ -354,8 +368,8 @@ absl::Status SetBinaryProto(absl::string_view filename, absl::StrCat("Could not write proto to '", filename, "'.")); } -absl::Status Delete(absl::string_view path, int flags) { - if (flags == Defaults()) { +absl::Status Delete(absl::string_view path, Options options) { + if (options == Defaults()) { std::string null_terminated_path = std::string(path); if (remove(null_terminated_path.c_str()) == 0) return absl::OkStatus(); } @@ -363,8 +377,8 @@ absl::Status Delete(absl::string_view path, int flags) { absl::StrCat("Could not delete '", path, "'.")); } -absl::Status Exists(absl::string_view path, int flags) { - if (flags == Defaults()) { +absl::Status Exists(absl::string_view path, Options options) { + if (options == Defaults()) { std::string null_terminated_path = std::string(path); if (access(null_terminated_path.c_str(), F_OK) == 0) { return absl::OkStatus(); diff --git a/ortools/base/file.h b/ortools/base/file.h index 0c408ed51b..dcaa5269ef 100644 --- a/ortools/base/file.h +++ b/ortools/base/file.h @@ -112,35 +112,41 @@ inline Options Defaults() { return 0xBABA; } // The caller should free the File after closing it by passing *f to delete. absl::Status Open(absl::string_view filename, absl::string_view mode, File** f, - int flags); + Options options); // The caller should free the File after closing it by passing the returned // pointer to delete. -File* OpenOrDie(absl::string_view filename, absl::string_view mode, int flags); +File* OpenOrDie(absl::string_view filename, absl::string_view mode, + Options options); absl::Status GetTextProto(absl::string_view filename, - google::protobuf::Message* proto, int flags); + google::protobuf::Message* proto, Options options); template -absl::StatusOr GetTextProto(absl::string_view filename, int flags) { +absl::StatusOr GetTextProto(absl::string_view filename, Options options) { T proto; - RETURN_IF_ERROR(GetTextProto(filename, &proto, flags)); + RETURN_IF_ERROR(GetTextProto(filename, &proto, options)); return proto; } absl::Status SetTextProto(absl::string_view filename, - const google::protobuf::Message& proto, int flags); + const google::protobuf::Message& proto, + Options options); absl::Status GetBinaryProto(absl::string_view filename, - google::protobuf::Message* proto, int flags); + google::protobuf::Message* proto, Options options); template -absl::StatusOr GetBinaryProto(absl::string_view filename, int flags) { +absl::StatusOr GetBinaryProto(absl::string_view filename, Options options) { T proto; - RETURN_IF_ERROR(GetBinaryProto(filename, &proto, flags)); + RETURN_IF_ERROR(GetBinaryProto(filename, &proto, options)); return proto; } absl::Status SetBinaryProto(absl::string_view filename, - const google::protobuf::Message& proto, int flags); + const google::protobuf::Message& proto, + Options options); absl::Status SetContents(absl::string_view filename, absl::string_view contents, - int flags); + Options options); +absl::StatusOr GetContents(absl::string_view path, + Options options); absl::Status GetContents(absl::string_view filename, std::string* output, - int flags); -absl::Status WriteString(File* file, absl::string_view contents, int flags); + Options options); +absl::Status WriteString(File* file, absl::string_view contents, + Options options); bool ReadFileToString(absl::string_view file_name, std::string* output); bool WriteStringToFile(absl::string_view data, absl::string_view file_name); @@ -157,8 +163,8 @@ bool WriteProtoToFile(const google::protobuf::Message& proto, void WriteProtoToFileOrDie(const google::protobuf::Message& proto, absl::string_view file_name); -absl::Status Delete(absl::string_view path, int flags); -absl::Status Exists(absl::string_view path, int flags); +absl::Status Delete(absl::string_view path, Options options); +absl::Status Exists(absl::string_view path, Options options); } // namespace file diff --git a/ortools/base/filesystem.cc b/ortools/base/filesystem.cc index 39e9cd5f5b..a6d4e9164a 100644 --- a/ortools/base/filesystem.cc +++ b/ortools/base/filesystem.cc @@ -71,4 +71,15 @@ absl::Status IsDirectory(std::string_view path, const file::Options& options) { } } +absl::Status RecursivelyCreateDir(std::string_view path, + const file::Options& options) { + (void)options; + try { + std::filesystem::create_directories(std::filesystem::path(path)); + return absl::OkStatus(); + } catch (const std::exception& e) { + return absl::InvalidArgumentError(e.what()); + } +} + } // namespace file diff --git a/ortools/base/filesystem.h b/ortools/base/filesystem.h index 5f0d57eae9..466cb2ab0d 100644 --- a/ortools/base/filesystem.h +++ b/ortools/base/filesystem.h @@ -28,6 +28,9 @@ absl::Status Match(std::string_view pattern, std::vector* result, absl::Status IsDirectory(std::string_view path, const file::Options& options); +absl::Status RecursivelyCreateDir(std::string_view path, + const file::Options& options); + } // namespace file #endif // OR_TOOLS_BASE_FILESYSTEM_H_ diff --git a/ortools/base/strong_int.h b/ortools/base/strong_int.h index 3ec2e118e0..aed5f295c8 100644 --- a/ortools/base/strong_int.h +++ b/ortools/base/strong_int.h @@ -153,6 +153,7 @@ #include #include "absl/base/port.h" +#include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "ortools/base/macros.h" @@ -298,6 +299,23 @@ std::ostream& operator<<(std::ostream& os, // NOLINT return os << arg.value(); } +// Define AbslStringify, for absl::StrAppend, absl::StrCat, and absl::StrFormat. +// +// When using StrongInt with absl::StrFormat, use the "%v" specifier. +template +void AbslStringify(Sink& sink, StrongInt arg) { + using ValueType = typename decltype(arg)::ValueType; + // int8_t/uint8_t are not supported by the "%v" specifier due to it being + // ambiguous whether an integer or character should be printed. + if constexpr (std::is_same_v) { + absl::Format(&sink, "%d", arg.value()); + } else if constexpr (std::is_same_v) { + absl::Format(&sink, "%u", arg.value()); + } else { + absl::Format(&sink, "%v", arg.value()); + } +} + // -- NON-MEMBER ARITHMETIC OPERATORS ------------------------------------------ // We support only the +, -, *, and / operators with the same StrongInt and // ValueType types. The reason is to allow simple manipulation on these IDs diff --git a/ortools/base/temp_path.cc b/ortools/base/temp_path.cc new file mode 100644 index 0000000000..a248abc174 --- /dev/null +++ b/ortools/base/temp_path.cc @@ -0,0 +1,77 @@ +// Copyright 2010-2024 Google LLC +// 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 "ortools/base/temp_path.h" + +#include + +#include "absl/log/check.h" +#include "absl/strings/str_cat.h" +#include "absl/time/time.h" +#include "ortools/base/filesystem.h" +#include "ortools/base/logging.h" + +namespace file { + +std::string TempFile(const char* prefix) { + std::string path; + if (prefix != nullptr) { + path = absl::StrCat(prefix, "_", absl::ToUnixMicros(absl::Now())); + } else { + path = absl::StrCat(absl::ToUnixMicros(absl::Now())); + } + return path; +} + +} // namespace file + +TempPath::TempPath(const std::string& prefix) + : path_(file::TempFile(prefix.c_str())) { + CHECK_OK(Init(kDefaultMode)); +} + +TempPath::TempPath(TempPath&& rhs) : path_(std::move(rhs.path_)) {} + +TempPath& TempPath::operator=(TempPath&& rhs) { + TempPath tmp(std::move(*this)); + path_ = std::move(rhs.path_); + return *this; +} + +TempPath::~TempPath() {} + +TempPath* TempPath::Create(Location location) { + switch (location) { + case Local: + return new TempPath(file::TempFile(nullptr)); + } + // never reach + return nullptr; +} + +TempPath::TempPath(const std::string& dirname, file::Options options, + absl::Status* status) + : path_(dirname) { + *status = Init(options); +} + +absl::Status TempPath::Init(file::Options options) { + const absl::Status status = file::RecursivelyCreateDir(path(), options); + if (!status.ok()) { + return absl::Status( + status.code(), absl::StrCat("Unable to create directory ", path(), ": ", + status.message())); + } + VLOG(1) << "Created temp path \"" << path() << "\""; + return ::absl::OkStatus(); +} diff --git a/ortools/base/temp_path.h b/ortools/base/temp_path.h new file mode 100644 index 0000000000..6d74b23804 --- /dev/null +++ b/ortools/base/temp_path.h @@ -0,0 +1,60 @@ +// Copyright 2010-2024 Google LLC +// 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. + +#ifndef OR_TOOLS_BASE_TEMP_PATH_H_ +#define OR_TOOLS_BASE_TEMP_PATH_H_ + +#include + +#include "absl/status/status.h" +#include "ortools/base/file.h" + +class TempPath { + public: + // default mode to create directories (a+rwx): + static constexpr int kDefaultMode = 0777; + + explicit TempPath(const std::string& prefix); + + // TempPath is moveable, but not copyable. + TempPath(TempPath&& rhs); + TempPath(const TempPath& rhs) = delete; + TempPath& operator=(TempPath&& rhs); + TempPath& operator=(const TempPath& rhs) = delete; + + ~TempPath(); + + // Returns the path which was created by this object. + std::string path() const { return path_; } + + enum Location { + Local, + }; + + static TempPath* Create(Location location); + + private: + // Internal constructor for Create* methods. + TempPath(const std::string& dirname, file::Options options, + absl::Status* status); + + // Shared initialization among constructors. + // Makes directory given by path() and `options`. + absl::Status Init(file::Options options); + + std::string path_; +}; + +namespace file {} // namespace file + +#endif // OR_TOOLS_BASE_TEMP_PATH_H_