diff --git a/bazel/scip.patch b/bazel/scip.patch index f05587d44b..21cfc6d7e1 100644 --- a/bazel/scip.patch +++ b/bazel/scip.patch @@ -1,414 +1,61 @@ -diff --git a/.gitignore b/.gitignore -index 4fbbc0e3b3..964b2d030d 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -83,8 +83,6 @@ hooks/ - localhooks/ - - # created when packaging, don't version control this --src/scip/githash.c --src/scip/buildflags.c - - # settings - settings/ -diff --git a/src/scip/benders_xyz.c b/src/scip/benders_xyz.c -index 0d812ba6bd..ffe1badee0 100644 ---- a/src/scip/benders_xyz.c -+++ b/src/scip/benders_xyz.c -@@ -47,6 +47,7 @@ - /** Benders' decomposition data */ - struct SCIP_BendersData - { -+ void* ptr; - }; - - -diff --git a/src/scip/benderscut_xyz.c b/src/scip/benderscut_xyz.c -index 0f05582fe4..7fb99f648a 100644 ---- a/src/scip/benderscut_xyz.c -+++ b/src/scip/benderscut_xyz.c -@@ -41,6 +41,7 @@ - /** Benders' decomposition cut data */ - struct SCIP_BenderscutData - { -+ void* ptr; - }; - - -diff --git a/src/scip/branch_xyz.c b/src/scip/branch_xyz.c -index 1f13ac7460..e8ded751cf 100644 ---- a/src/scip/branch_xyz.c -+++ b/src/scip/branch_xyz.c -@@ -42,6 +42,7 @@ - /** branching rule data */ - struct SCIP_BranchruleData - { -+ void* ptr; - }; - - -@@ -216,7 +217,7 @@ SCIP_RETCODE SCIPincludeBranchruleXyz( - /* use SCIPincludeBranchrule() if you want to set all callbacks explicitly and realize (by getting compiler errors) when - * new callbacks are added in future SCIP versions - */ -- SCIP_CALL( SCIPincludeBranchrule(scip, BRANCHRULE_NAME, BRANCHRULE_DESC, BRANCHRULE_PRIORITY, BRANCHRULE_MAXDEPTH, -+ SCIP_CALL( SCIPincludeBranchrule(scip, BRANCHRULE_NAME, BRANCHRULE_DESC, BRANCHRULE_PRIORITY, BRANCHRULE_MAXDEPTH, - BRANCHRULE_MAXBOUNDDIST, - branchCopyXyz, branchFreeXyz, branchInitXyz, branchExitXyz, branchInitsolXyz, branchExitsolXyz, - branchExeclpXyz, branchExecextXyz, branchExecpsXyz, -diff --git a/src/scip/compr_xyz.c b/src/scip/compr_xyz.c -index 2f6b29e88c..a6142d7785 100644 ---- a/src/scip/compr_xyz.c -+++ b/src/scip/compr_xyz.c -@@ -41,6 +41,7 @@ - /** tree compression data */ - struct SCIP_ComprData - { -+ void* ptr; - }; - - -diff --git a/src/scip/cons_xyz.c b/src/scip/cons_xyz.c -index 8141039a2e..e3f5d2b94b 100644 ---- a/src/scip/cons_xyz.c -+++ b/src/scip/cons_xyz.c -@@ -69,11 +69,13 @@ - /** constraint data for xyz constraints */ - struct SCIP_ConsData - { -+ void* ptr; - }; - - /** constraint handler data */ - struct SCIP_ConshdlrData - { -+ void* ptr; - }; - - -diff --git a/src/scip/cutsel_xyz.c b/src/scip/cutsel_xyz.c -index c660098bb0..92f3fb9d92 100644 ---- a/src/scip/cutsel_xyz.c -+++ b/src/scip/cutsel_xyz.c -@@ -40,6 +40,7 @@ - /** cut selector data */ - struct SCIP_CutselData - { -+ void* ptr; - }; - - -diff --git a/src/scip/dialog_xyz.c b/src/scip/dialog_xyz.c -index 1918057017..8506d526e6 100644 ---- a/src/scip/dialog_xyz.c -+++ b/src/scip/dialog_xyz.c -@@ -28,7 +28,7 @@ - - #define DIALOG_NAME "xyz" - #define DIALOG_DESC "xyz user interface dialog" --#define DIALOG_ISSUBMENU FALSE -+#define DIALOG_ISSUBMENU FALSE - - - -@@ -42,6 +42,7 @@ - /** dialog data */ - struct SCIP_DialogData - { -+ void* ptr; - }; - - -@@ -154,7 +155,7 @@ SCIP_RETCODE SCIPincludeDialogXyz( - /* create, include, and release dialog */ - if( !SCIPdialogHasEntry(parentdialog, DIALOG_NAME) ) +diff --git a/src/lpi/lpi_glop.cpp b/src/lpi/lpi_glop.cpp +index 2471778a8f..17fd1e8c34 100644 +--- a/src/lpi/lpi_glop.cpp ++++ b/src/lpi/lpi_glop.cpp +@@ -3190,6 +3190,6 @@ SCIP_RETCODE SCIPlpiReadLP( + const std::string filespec(fname); + MPModelProto proto; +- if ( ! ReadFileToProto(filespec, &proto) ) ++ if ( ! ReadFileToProto(filespec, &proto).ok() ) { -- SCIP_CALL( SCIPincludeDialog(scip, &dialog, -+ SCIP_CALL( SCIPincludeDialog(scip, &dialog, - dialogCopyXyz, dialogExecXyz, dialogDescXyz, dialogFreeXyz, - DIALOG_NAME, DIALOG_DESC, DIALOG_ISSUBMENU, dialogdata) ); - SCIP_CALL( SCIPaddDialogEntry(scip, parentdialog, dialog) ); -diff --git a/src/scip/disp_xyz.c b/src/scip/disp_xyz.c -index 6c6a776091..b00cf1e036 100644 ---- a/src/scip/disp_xyz.c -+++ b/src/scip/disp_xyz.c -@@ -26,13 +26,13 @@ - #include "scip/disp_xyz.h" - - --#define DISP_NAME "xyz" -+#define DISP_NAME "xyz" - #define DISP_DESC "xyz display column" --#define DISP_HEADER "xyz" -+#define DISP_HEADER "xyz" - #define DISP_WIDTH 14 /**< the width of the display column */ - #define DISP_PRIORITY 110000 /**< the priority of the display column */ - #define DISP_POSITION 30100 /**< the relative position of the display column */ --#define DISP_STRIPLINE TRUE /**< the default for whether the display column should be separated -+#define DISP_STRIPLINE TRUE /**< the default for whether the display column should be separated - * with a line from its right neighbor */ - - -@@ -47,6 +47,7 @@ - /** display column data */ - struct SCIP_DispData - { -+ void* ptr; - }; - - -@@ -188,10 +189,10 @@ SCIP_RETCODE SCIPincludeDispXyz( - /* TODO: (optional) create display column specific data here */ - - /* include display column */ -- SCIP_CALL( SCIPincludeDisp(scip, DISP_NAME, DISP_DESC, DISP_HEADER, SCIP_DISPSTATUS_AUTO, -+ SCIP_CALL( SCIPincludeDisp(scip, DISP_NAME, DISP_DESC, DISP_HEADER, SCIP_DISPSTATUS_AUTO, - dispCopyXyz, -- dispFreeXyz, dispInitXyz, dispExitXyz, -- dispInitsolXyz, dispExitsolXyz, dispOutputXyz, -+ dispFreeXyz, dispInitXyz, dispExitXyz, -+ dispInitsolXyz, dispExitsolXyz, dispOutputXyz, - dispdata, DISP_WIDTH, DISP_PRIORITY, DISP_POSITION, DISP_STRIPLINE) ); - - /* add xyz display column parameters */ -diff --git a/src/scip/event_xyz.c b/src/scip/event_xyz.c -index 31fd333f98..c793d69bc4 100644 ---- a/src/scip/event_xyz.c -+++ b/src/scip/event_xyz.c -@@ -36,6 +36,7 @@ - /** event handler data */ - struct SCIP_EventhdlrData - { -+ void* ptr; - }; - - /* -@@ -179,7 +180,7 @@ SCIP_RETCODE SCIPincludeEventHdlrXyz( - */ - SCIP_CALL( SCIPincludeEventhdlr(scip, EVENTHDLR_NAME, EVENTHDLR_DESC, - eventCopyXyz, -- eventFreeXyz, eventInitXyz, eventExitXyz, -+ eventFreeXyz, eventInitXyz, eventExitXyz, - eventInitsolXyz, eventExitsolXyz, eventDeleteXyz, eventExecXyz, - eventhdlrdata) ); - #else -diff --git a/src/scip/expr_xyz.c b/src/scip/expr_xyz.c -index 2eb7914e1d..4e924b03b5 100644 ---- a/src/scip/expr_xyz.c -+++ b/src/scip/expr_xyz.c -@@ -38,11 +38,13 @@ - /** expression handler data */ - struct SCIP_ExprhdlrData - { -+ void* ptr; - }; - - /** expression data */ - struct SCIP_ExprData - { -+ void* ptr; - }; - - /* + SCIPerrorMessage("Could not read <%s>\n", fname); + return SCIP_READERROR; +@@ -3214,7 +3214,7 @@ SCIP_RETCODE SCIPlpiWriteLP( + MPModelProto proto; + LinearProgramToMPModelProto(*lpi->linear_program, &proto); + const std::string filespec(fname); +- if ( ! WriteProtoToFile(filespec, proto, operations_research::ProtoWriteFormat::kProtoText, true) ) ++ if ( ! WriteProtoToFile(filespec, proto, operations_research::ProtoWriteFormat::kProtoText, true).ok() ) + { + SCIPerrorMessage("Could not write <%s>\n", fname); + return SCIP_READERROR; +diff --git a/src/symmetry/compute_symmetry_bliss.cpp b/src/symmetry/compute_symmetry_bliss.cpp +index 484627c4b9..27c2895165 100644 +--- a/src/symmetry/compute_symmetry_bliss.cpp ++++ b/src/symmetry/compute_symmetry_bliss.cpp +@@ -25,5 +25,5 @@ + #include "compute_symmetry.h" + + /* include bliss graph */ +-#include +-#include ++#include ++#include + + #include + #include + diff --git a/src/scip/githash.c b/src/scip/githash.c new file mode 100644 index 0000000000..2891bc72de --- /dev/null +++ b/src/scip/githash.c -@@ -0,0 +1 @@ +@@ -0,0 +1,1 @@ +#define SCIP_GITHASH "a740f0891e" -diff --git a/src/scip/heur_xyz.c b/src/scip/heur_xyz.c -index 9f7d804f4d..e33bb83b7c 100644 ---- a/src/scip/heur_xyz.c -+++ b/src/scip/heur_xyz.c -@@ -46,6 +46,7 @@ - /** primal heuristic data */ - struct SCIP_HeurData - { -+ void* ptr; - }; - - -diff --git a/src/scip/nlhdlr_xyz.c b/src/scip/nlhdlr_xyz.c -index bc90f3dafe..056af7b6d3 100644 ---- a/src/scip/nlhdlr_xyz.c -+++ b/src/scip/nlhdlr_xyz.c -@@ -40,11 +40,13 @@ - /** nonlinear handler data */ - struct SCIP_NlhdlrData - { -+ void* ptr; - }; - - /** nonlinear handler expression data */ - struct SCIP_NlhdlrExprData - { -+ void* ptr; - }; - - /* -diff --git a/src/scip/nlpi_xyz.c b/src/scip/nlpi_xyz.c -index 3509410b23..901433d2d4 100644 ---- a/src/scip/nlpi_xyz.c -+++ b/src/scip/nlpi_xyz.c -@@ -43,12 +43,14 @@ - - struct SCIP_NlpiData - { -+ void* ptr; - }; - - /* TODO: fill in the necessary NLP problem instance data */ - - struct SCIP_NlpiProblem - { -+ void* ptr; - }; - - -diff --git a/src/scip/nodesel_xyz.c b/src/scip/nodesel_xyz.c -index a5b6d9d7d6..0aacc3c8d2 100644 ---- a/src/scip/nodesel_xyz.c -+++ b/src/scip/nodesel_xyz.c -@@ -41,6 +41,7 @@ - /** node selector data */ - struct SCIP_NodeselData - { -+ void* ptr; - }; - - -diff --git a/src/scip/presol_xyz.c b/src/scip/presol_xyz.c -index 38ba9df72e..04fe8605f5 100644 ---- a/src/scip/presol_xyz.c -+++ b/src/scip/presol_xyz.c -@@ -42,6 +42,7 @@ - /** presolver data */ - struct SCIP_PresolData - { -+ void* ptr; - }; - - -diff --git a/src/scip/pricer_xyz.c b/src/scip/pricer_xyz.c -index 16c968b951..5090a91e35 100644 ---- a/src/scip/pricer_xyz.c -+++ b/src/scip/pricer_xyz.c -@@ -43,6 +43,7 @@ - /** variable pricer data */ - struct SCIP_PricerData - { -+ void* ptr; - }; - - -@@ -204,7 +205,7 @@ SCIP_RETCODE SCIPincludePricerXyz( - * new callbacks are added in future SCIP versions - */ - SCIP_CALL( SCIPincludePricer(scip, PRICER_NAME, PRICER_DESC, PRICER_PRIORITY, PRICER_DELAY, -- pricerCopyXyz, pricerFreeXyz, pricerInitXyz, pricerExitXyz, -+ pricerCopyXyz, pricerFreeXyz, pricerInitXyz, pricerExitXyz, - pricerInitsolXyz, pricerExitsolXyz, pricerRedcostXyz, pricerFarkasXyz, - pricerdata) ); - #else -diff --git a/src/scip/prop_xyz.c b/src/scip/prop_xyz.c -index 431d8e909b..975564f12b 100644 ---- a/src/scip/prop_xyz.c -+++ b/src/scip/prop_xyz.c -@@ -28,7 +28,7 @@ - /* fundamental propagator properties */ - #define PROP_NAME "xyz" - #define PROP_DESC "propagator template" --#define PROP_PRIORITY 0 /**< propagator priority */ -+#define PROP_PRIORITY 0 /**< propagator priority */ - #define PROP_FREQ 10 /**< propagator frequency */ - #define PROP_DELAY FALSE /**< should propagation method be delayed, if other propagators found reductions? */ - #define PROP_TIMING SCIP_PROPTIMING_BEFORELP/**< propagation timing mask */ -@@ -50,6 +50,7 @@ - /** propagator data */ - struct SCIP_PropData - { -+ void* ptr; - }; - - -diff --git a/src/scip/reader_xyz.c b/src/scip/reader_xyz.c -index 08e5a6ff7b..c8fdd9d4f5 100644 ---- a/src/scip/reader_xyz.c -+++ b/src/scip/reader_xyz.c -@@ -40,6 +40,7 @@ - /** data for xyz reader */ - struct SCIP_ReaderData - { -+ void* ptr; - }; - - -diff --git a/src/scip/relax_xyz.c b/src/scip/relax_xyz.c -index 4d4acc80b3..74dfbda91f 100644 ---- a/src/scip/relax_xyz.c -+++ b/src/scip/relax_xyz.c -@@ -43,6 +43,7 @@ - /** relaxator data */ - struct SCIP_RelaxData - { -+ void* ptr; - }; - - diff --git a/src/scip/scipbuildflags.c b/src/scip/scipbuildflags.c index b54b9112cb..dc8e62b5e0 100644 --- a/src/scip/scipbuildflags.c +++ b/src/scip/scipbuildflags.c @@ -21,10 +21,9 @@ - + /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/ - + +#define SCIP_BUILDFLAGS " ARCH=x86_64\n COMP=gnu\n DEBUGSOL=false\n EXPRINT=none\n GAMS=false\n SYM=bliss\n GMP=false\n IPOPT=false\n IPOPTOPT=opt\n WORHP=false\n WORHPOPT=opt\n LPS=spx2\n LPSCHECK=false\n LPSOPT=opt\n NOBLKBUFMEM=false\n NOBLKMEM=false\n NOBUFMEM=false\n OPT=opt\n OSTYPE=linux\n PARASCIP=true\n READLINE=false\n SANITIZE=\n SHARED=false\n USRARFLAGS=\n USRCFLAGS=-fPIC\n USRCXXFLAGS=-fPIC\n USRDFLAGS=\n USRFLAGS=\n USRLDFLAGS=\n USROFLAGS=\n VERSION=7.0.1\n ZIMPL=false\n ZIMPLOPT=opt\n ZLIB=true" + #include "scip/scipbuildflags.h" -#ifdef NO_CONFIG_HEADER -#include "buildflags.c" -#endif - + /** returns the flags that were used to build SCIP */ const char* SCIPgetBuildFlags( -diff --git a/src/scip/sepa_xyz.c b/src/scip/sepa_xyz.c -index 40d3c1c5f7..f68658951d 100644 ---- a/src/scip/sepa_xyz.c -+++ b/src/scip/sepa_xyz.c -@@ -44,6 +44,7 @@ - /** separator data */ - struct SCIP_SepaData - { -+ void* ptr; - }; - - -diff --git a/src/scip/table_xyz.c b/src/scip/table_xyz.c -index 2c93a43235..4f1fd28de4 100644 ---- a/src/scip/table_xyz.c -+++ b/src/scip/table_xyz.c -@@ -43,6 +43,7 @@ - /** statistics table data */ - struct SCIP_TableData - { -+ void* ptr; - }; - - -diff --git a/src/symmetry/compute_symmetry_bliss.cpp b/src/symmetry/compute_symmetry_bliss.cpp -index 484627c4b9..27c2895165 100644 ---- a/src/symmetry/compute_symmetry_bliss.cpp -+++ b/src/symmetry/compute_symmetry_bliss.cpp -@@ -25,8 +25,8 @@ - #include "compute_symmetry.h" - - /* include bliss graph */ --#include --#include -+#include -+#include - - #include - #include diff --git a/cmake/dependencies/CMakeLists.txt b/cmake/dependencies/CMakeLists.txt index af3a28c113..e2624fc18d 100644 --- a/cmake/dependencies/CMakeLists.txt +++ b/cmake/dependencies/CMakeLists.txt @@ -247,7 +247,7 @@ if(BUILD_SCIP) scip GIT_REPOSITORY "https://github.com/scipopt/scip.git" GIT_TAG "v804" - #PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/scip-v804.patch" + PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/scip-v804.patch" ) FetchContent_MakeAvailable(scip) set(LPI_GLOP_SRC ${scip_SOURCE_DIR}/src/lpi/lpi_glop.cpp PARENT_SCOPE) diff --git a/ortools/base/gzipstring.h b/ortools/base/gzipstring.h index c31eed8a8a..5d7cead558 100644 --- a/ortools/base/gzipstring.h +++ b/ortools/base/gzipstring.h @@ -19,7 +19,7 @@ #include "ortools/base/logging.h" #include "zlib.h" -bool GunzipString(const std::string& str, std::string* out) { +bool GunzipString(absl::string_view str, std::string* out) { z_stream zs; zs.zalloc = Z_NULL; zs.zfree = Z_NULL; diff --git a/ortools/glop/lp_solver.cc b/ortools/glop/lp_solver.cc index db8a59a6d1..fe8f495988 100644 --- a/ortools/glop/lp_solver.cc +++ b/ortools/glop/lp_solver.cc @@ -103,10 +103,8 @@ void DumpLinearProgramIfRequiredByFlags(const LinearProgram& linear_program, const ProtoWriteFormat write_format = absl::GetFlag(FLAGS_lp_dump_binary_file) ? ProtoWriteFormat::kProtoBinary : ProtoWriteFormat::kProtoText; - if (!WriteProtoToFile(filespec, proto, write_format, - absl::GetFlag(FLAGS_lp_dump_compressed_file))) { - LOG(DFATAL) << "Could not write " << filespec; - } + CHECK_OK(WriteProtoToFile(filespec, proto, write_format, + absl::GetFlag(FLAGS_lp_dump_compressed_file))); #endif } diff --git a/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java b/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java index 35eabfc735..9ee12e760c 100644 --- a/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java +++ b/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java @@ -317,7 +317,17 @@ public final class ModelBuilder { * @return true if the model was correctly written. */ public boolean exportToFile(String file) { - return helper.writeModelToFile(file); + return helper.writeModelToProtoFile(file); + } + + /** + * import the model from protocol buffer 'file'. + * + * @param file file to read the model from. + * @return true if the model was correctly loaded. + */ + public boolean importFromFile(String file) { + return helper.readModelFromProtoFile(file); } public String exportToMpsString(boolean obfuscate) { diff --git a/ortools/linear_solver/csharp/ModelBuilder.cs b/ortools/linear_solver/csharp/ModelBuilder.cs index 36b5d6c0aa..4a6daee80a 100644 --- a/ortools/linear_solver/csharp/ModelBuilder.cs +++ b/ortools/linear_solver/csharp/ModelBuilder.cs @@ -376,7 +376,17 @@ public class Model ///@return true if the model was correctly written. public bool ExportToFile(String file) { - return helper_.WriteModelToFile(file); + return helper_.WriteModelToProtoFile(file); + } + + /// + /// load the model as a protocol buffer from 'file'. + /// + /// @param file file to read the model from. + ///@return true if the model was correctly loaded. + public bool ImportFromFile(String file) + { + return helper_.ReadModelFromProtoFile(file); } public String ExportToMpsString(bool obfuscate) diff --git a/ortools/linear_solver/csharp/model_builder.i b/ortools/linear_solver/csharp/model_builder.i index bf0d3af08a..989a362f0e 100644 --- a/ortools/linear_solver/csharp/model_builder.i +++ b/ortools/linear_solver/csharp/model_builder.i @@ -116,7 +116,8 @@ VECTOR_AS_CSHARP_ARRAY(double, double, double, DoubleVector); %rename (ConstraintsCount) operations_research::ModelBuilderHelper::num_constraints; %rename (Name) operations_research::ModelBuilderHelper::name; %unignore operations_research::ModelBuilderHelper::SetName; -%unignore operations_research::ModelBuilderHelper::WriteModelToFile; +%unignore operations_research::ModelBuilderHelper::ReadModelFromProtoFile; +%unignore operations_research::ModelBuilderHelper::WriteModelToProtoFile; %unignore operations_research::ModelBuilderHelper::ImportFromMpsString; %unignore operations_research::ModelBuilderHelper::ImportFromMpsFile; %unignore operations_research::ModelBuilderHelper::ImportFromLpString; diff --git a/ortools/linear_solver/java/modelbuilder.i b/ortools/linear_solver/java/modelbuilder.i index 9d00d90579..c987ccd496 100644 --- a/ortools/linear_solver/java/modelbuilder.i +++ b/ortools/linear_solver/java/modelbuilder.i @@ -174,7 +174,8 @@ class GlobalRefGuard { %rename (numConstraints) operations_research::ModelBuilderHelper::num_constraints; %rename (getName) operations_research::ModelBuilderHelper::name; %rename (setName) operations_research::ModelBuilderHelper::SetName; -%rename (writeModelToFile) operations_research::ModelBuilderHelper::WriteModelToFile; +%rename (readModelFromProtoFile) operations_research::ModelBuilderHelper::ReadModelFromProtoFile; +%rename (writeModelToProtoFile) operations_research::ModelBuilderHelper::WriteModelToProtoFile; %rename (importFromMpsString) operations_research::ModelBuilderHelper::ImportFromMpsString; %rename (importFromMpsFile) operations_research::ModelBuilderHelper::ImportFromMpsFile; %rename (importFromLpString) operations_research::ModelBuilderHelper::ImportFromLpString; diff --git a/ortools/linear_solver/linear_solver.h b/ortools/linear_solver/linear_solver.h index 33ebec9b89..a08f0e9f1f 100644 --- a/ortools/linear_solver/linear_solver.h +++ b/ortools/linear_solver/linear_solver.h @@ -578,12 +578,19 @@ class MPSolver { * from `MPSolver::Solve()` which by default sets the feasibility tolerance * and the gap limit (as of 2020/02/11, to 1e-7 and 0.0001, respectively). */ +#if !defined(SWIG) + ABSL_DEPRECATED("Prefer SolveMPModel() from solve_mp_model.h.") +#endif // !defined(SWIG) static void SolveWithProto(const MPModelRequest& model_request, MPSolutionResponse* response, // `interrupt` is non-const because the internal // solver may set it to true itself, in some cases. std::atomic* interrupt = nullptr); +#if !defined(SWIG) + ABSL_DEPRECATED( + "Prefer SolverTypeSupportsInterruption() from solve_mp_model.h.") +#endif // !defined(SWIG) static bool SolverTypeSupportsInterruption( const MPModelRequest::SolverType solver) { // Interruption requires that MPSolver::InterruptSolve is supported for the diff --git a/ortools/linear_solver/model_exporter_main.cc b/ortools/linear_solver/model_exporter_main.cc index ec0018e977..29b0a0567b 100644 --- a/ortools/linear_solver/model_exporter_main.cc +++ b/ortools/linear_solver/model_exporter_main.cc @@ -16,6 +16,7 @@ #include #include +#include "absl/log/check.h" #include "absl/strings/match.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/io/zero_copy_stream_impl_lite.h" @@ -51,12 +52,12 @@ int main(int argc, char** argv) { operations_research::MPModelProto model_proto; if (absl::GetFlag(FLAGS_input_is_mp_model_request)) { operations_research::MPModelRequest request_proto; - CHECK(operations_research::ReadFileToProto(absl::GetFlag(FLAGS_input), - &request_proto)); + CHECK_OK(operations_research::ReadFileToProto(absl::GetFlag(FLAGS_input), + &request_proto)); model_proto.Swap(request_proto.mutable_model()); } else { - CHECK(operations_research::ReadFileToProto(absl::GetFlag(FLAGS_input), - &model_proto)); + CHECK_OK(operations_research::ReadFileToProto(absl::GetFlag(FLAGS_input), + &model_proto)); } std::string output_contents; operations_research::MPModelExportOptions options; diff --git a/ortools/linear_solver/proto_solver/sat_proto_solver.h b/ortools/linear_solver/proto_solver/sat_proto_solver.h index f8cfc5ae04..264ed1f51d 100644 --- a/ortools/linear_solver/proto_solver/sat_proto_solver.h +++ b/ortools/linear_solver/proto_solver/sat_proto_solver.h @@ -19,7 +19,6 @@ #include #include "ortools/linear_solver/linear_solver.pb.h" -#include "ortools/sat/sat_parameters.pb.h" #include "ortools/util/logging.h" namespace operations_research { @@ -29,7 +28,7 @@ namespace operations_research { // If possible, std::move the request into this function call to avoid a copy. // // If you need to change the solver parameters, please use the -// EncodeSatParametersAsString() function below to set the request's +// EncodeParametersAsString() function to set the request's // solver_specific_parameters field. // // The optional interrupt_solve can be used to interrupt the solve early. It diff --git a/ortools/linear_solver/python/model_builder.py b/ortools/linear_solver/python/model_builder.py index 66df10dc68..b4e83e8b95 100644 --- a/ortools/linear_solver/python/model_builder.py +++ b/ortools/linear_solver/python/model_builder.py @@ -1460,11 +1460,11 @@ class Model: # Objective. def minimize(self, linear_expr: LinearExprT) -> None: - """Minimize the given objective.""" + """Minimizes the given objective.""" self.__optimize(linear_expr, False) def maximize(self, linear_expr: LinearExprT) -> None: - """Maximize the given objective.""" + """Maximizes the given objective.""" self.__optimize(linear_expr, True) def __optimize(self, linear_expr: LinearExprT, maximize: bool) -> None: @@ -1507,11 +1507,11 @@ class Model: # Hints. def clear_hints(self): - """Clear all solution hints.""" + """Clears all solution hints.""" self.__helper.clear_hints() def add_hint(self, var: Variable, value: NumberT) -> None: - """Add var == value as a hint to the model. + """Adds var == value as a hint to the model. Args: var: The variable of the hint @@ -1537,28 +1537,28 @@ class Model: return mbh.to_mpmodel_proto(self.__helper) def import_from_mps_string(self, mps_string: str) -> bool: - """Loads the a model from an MPS string.""" + """Reads a model from a MPS string.""" return self.__helper.import_from_mps_string(mps_string) def import_from_mps_file(self, mps_file: str) -> bool: - """Loads the a model from an MPS file.""" + """Reads a model from a .mps file.""" return self.__helper.import_from_mps_file(mps_file) def import_from_lp_string(self, lp_string: str) -> bool: - """Loads the a model from an LP string.""" + """Reads a model from a LP string.""" return self.__helper.import_from_lp_string(lp_string) def import_from_lp_file(self, lp_file: str) -> bool: - """Loads the a model from an LP file.""" + """Reads a model from a .lp file.""" return self.__helper.import_from_lp_file(lp_file) def import_from_proto_file(self, proto_file: str) -> bool: - """Loads the a model from an proto file.""" - return self.__helper.load_model_from_file(proto_file) + """Reads a model from a proto file.""" + return self.__helper.read_model_from_proto_file(proto_file) def export_to_proto_file(self, proto_file: str) -> bool: - """Write a model to a proto file.""" - return self.__helper.write_model_to_file(proto_file) + """Writes a model to a proto file.""" + return self.__helper.write_model_to_proto_file(proto_file) # Model getters and Setters diff --git a/ortools/linear_solver/python/model_builder_helper.cc b/ortools/linear_solver/python/model_builder_helper.cc index 5b6cdacbcf..36a5468018 100644 --- a/ortools/linear_solver/python/model_builder_helper.cc +++ b/ortools/linear_solver/python/model_builder_helper.cc @@ -175,10 +175,10 @@ PYBIND11_MODULE(model_builder_helper, m) { arg("options") = MPModelExportOptions()) .def("export_to_lp_string", &ModelBuilderHelper::ExportToLpString, arg("options") = MPModelExportOptions()) - .def("write_model_to_file", &ModelBuilderHelper::WriteModelToFile, - arg("filename")) - .def("load_model_from_file", &ModelBuilderHelper::LoadModelFromFile, - arg("filename")) + .def("read_model_from_proto_file", + &ModelBuilderHelper::ReadModelFromProtoFile, arg("filename")) + .def("write_model_to_proto_file", + &ModelBuilderHelper::WriteModelToProtoFile, arg("filename")) .def("import_from_mps_string", &ModelBuilderHelper::ImportFromMpsString, arg("mps_string")) .def("import_from_mps_file", &ModelBuilderHelper::ImportFromMpsFile, diff --git a/ortools/linear_solver/python/solve_model.py b/ortools/linear_solver/python/solve_model.py index 59d5385114..5420c277d8 100644 --- a/ortools/linear_solver/python/solve_model.py +++ b/ortools/linear_solver/python/solve_model.py @@ -32,7 +32,7 @@ def main(argv: Sequence[str]) -> None: model = model_builder.ModelBuilder() - # Load MPS file. + # Load MPS or proto file. if _INPUT.value.endswith(".mps"): if not model.import_from_mps_file(_INPUT.value): print(f"Cannot import MPS file: '{_INPUT.value}'") @@ -41,7 +41,6 @@ def main(argv: Sequence[str]) -> None: print(f"Cannot import Proto file: '{_INPUT.value}'") return - # Create solver. solver = model_builder.ModelSolver(_SOLVER.value) if not solver.solver_is_supported(): diff --git a/ortools/linear_solver/samples/bin_packing_mip.py b/ortools/linear_solver/samples/bin_packing_mip.py index dbf18c67ad..aefc5b9af9 100755 --- a/ortools/linear_solver/samples/bin_packing_mip.py +++ b/ortools/linear_solver/samples/bin_packing_mip.py @@ -30,6 +30,7 @@ def create_data_model(): data["bins"] = data["items"] data["bin_capacity"] = 100 return data + # [END data_model] diff --git a/ortools/linear_solver/samples/mip_var_array.py b/ortools/linear_solver/samples/mip_var_array.py index e4fcbcec2b..84fae73e5a 100644 --- a/ortools/linear_solver/samples/mip_var_array.py +++ b/ortools/linear_solver/samples/mip_var_array.py @@ -36,6 +36,7 @@ def create_data_model(): data["num_vars"] = 5 data["num_constraints"] = 4 return data + # [END data_model] diff --git a/ortools/linear_solver/solve.cc b/ortools/linear_solver/solve.cc index 12d949e880..1aa10a1aef 100644 --- a/ortools/linear_solver/solve.cc +++ b/ortools/linear_solver/solve.cc @@ -146,8 +146,8 @@ MPModelRequest ReadMipModel(const std::string& input) { QCHECK_OK(glop::MPSReader().ParseFile(input, &model_proto)) << "Error while parsing the mps file '" << input << "'."; } else { - ReadFileToProto(input, &model_proto); - ReadFileToProto(input, &request_proto); + ReadFileToProto(input, &model_proto).IgnoreError(); + ReadFileToProto(input, &request_proto).IgnoreError(); } // If the input is a proto in binary format, both ReadFileToProto could // return true. Instead use the actual number of variables found to test the @@ -326,13 +326,13 @@ void Run() { // If requested, save the model and/or request to file. if (!absl::GetFlag(FLAGS_dump_model).empty()) { - CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_model), - request_proto.model(), write_format, - absl::GetFlag(FLAGS_dump_gzip))); + CHECK_OK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_model), + request_proto.model(), write_format, + absl::GetFlag(FLAGS_dump_gzip))); } if (!absl::GetFlag(FLAGS_dump_request).empty()) { - CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_request), request_proto, - write_format, absl::GetFlag(FLAGS_dump_gzip))); + CHECK_OK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_request), request_proto, + write_format, absl::GetFlag(FLAGS_dump_gzip))); } absl::PrintF( @@ -377,8 +377,8 @@ void Run() { file::Defaults())); } if (!absl::GetFlag(FLAGS_dump_response).empty() && has_solution) { - CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_response), response, - write_format, absl::GetFlag(FLAGS_dump_gzip))); + CHECK_OK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_response), response, + write_format, absl::GetFlag(FLAGS_dump_gzip))); } if (!absl::GetFlag(FLAGS_output_csv).empty() && has_solution) { std::string csv_file; diff --git a/ortools/linear_solver/wrappers/model_builder_helper.cc b/ortools/linear_solver/wrappers/model_builder_helper.cc index a0fe7f8acc..10cbe1e382 100644 --- a/ortools/linear_solver/wrappers/model_builder_helper.cc +++ b/ortools/linear_solver/wrappers/model_builder_helper.cc @@ -24,6 +24,7 @@ #include "absl/log/check.h" #include "absl/strings/match.h" #include "ortools/base/helpers.h" +#include "ortools/base/logging.h" #include "ortools/base/options.h" #include "ortools/linear_solver/linear_solver.h" #include "ortools/linear_solver/linear_solver.pb.h" @@ -60,24 +61,28 @@ std::string ModelBuilderHelper::ExportToLpString( .value_or(""); } -bool ModelBuilderHelper::WriteModelToFile(const std::string& filename) { - if (absl::EndsWith(filename, "txt") || - absl::EndsWith(filename, ".textproto")) { +bool ModelBuilderHelper::ReadModelFromProtoFile(const std::string& filename) { + if (file::GetTextProto(filename, &model_, file::Defaults()).ok() || + file::GetBinaryProto(filename, &model_, file::Defaults()).ok()) { + return true; + } + MPModelRequest request; + if (file::GetTextProto(filename, &request, file::Defaults()).ok() || + file::GetBinaryProto(filename, &request, file::Defaults()).ok()) { + model_ = request.model(); + return true; + } + return false; +} + +bool ModelBuilderHelper::WriteModelToProtoFile(const std::string& filename) { + if (absl::EndsWith(filename, "txt")) { return file::SetTextProto(filename, model_, file::Defaults()).ok(); } else { return file::SetBinaryProto(filename, model_, file::Defaults()).ok(); } } -bool ModelBuilderHelper::LoadModelFromFile(const std::string& filename) { - if (absl::EndsWith(filename, "txt") || - absl::EndsWith(filename, ".textproto")) { - return file::GetTextProto(filename, &model_, file::Defaults()).ok(); - } else { - return file::GetBinaryProto(filename, &model_, file::Defaults()).ok(); - } -} - // See comment in the header file why we need to wrap absl::Status code with // code having simpler APIs. bool ModelBuilderHelper::ImportFromMpsString(const std::string& mps_string) { diff --git a/ortools/linear_solver/wrappers/model_builder_helper.h b/ortools/linear_solver/wrappers/model_builder_helper.h index 1d1f09501b..95ab3ceedc 100644 --- a/ortools/linear_solver/wrappers/model_builder_helper.h +++ b/ortools/linear_solver/wrappers/model_builder_helper.h @@ -50,8 +50,8 @@ class ModelBuilderHelper { options = MPModelExportOptions()); std::string ExportToLpString(const operations_research::MPModelExportOptions& options = MPModelExportOptions()); - bool WriteModelToFile(const std::string& filename); - bool LoadModelFromFile(const std::string& filename); + bool ReadModelFromProtoFile(const std::string& filename); + bool WriteModelToProtoFile(const std::string& filename); bool ImportFromMpsString(const std::string& mps_string); bool ImportFromMpsFile(const std::string& mps_file); diff --git a/ortools/lp_data/model_reader.cc b/ortools/lp_data/model_reader.cc index f7ddfd0e0b..14b19d49dc 100644 --- a/ortools/lp_data/model_reader.cc +++ b/ortools/lp_data/model_reader.cc @@ -29,8 +29,8 @@ bool LoadMPModelProtoFromModelOrRequest(const std::string& input_file_path, MPModelProto* model) { MPModelProto model_proto; MPModelRequest request_proto; - ReadFileToProto(input_file_path, &model_proto); - ReadFileToProto(input_file_path, &request_proto); + ReadFileToProto(input_file_path, &model_proto).IgnoreError(); + ReadFileToProto(input_file_path, &request_proto).IgnoreError(); // If the input proto is in binary format, both ReadFileToProto could return // true. Instead use the actual number of variables found to test the // correct format of the input. diff --git a/ortools/math_opt/python/BUILD.bazel b/ortools/math_opt/python/BUILD.bazel index 957b324d49..cae950b4ed 100644 --- a/ortools/math_opt/python/BUILD.bazel +++ b/ortools/math_opt/python/BUILD.bazel @@ -183,8 +183,5 @@ py_library( name = "normalize", srcs = ["normalize.py"], visibility = ["//ortools/math_opt/python:__subpackages__"], - deps = [ -# "@com_google_protobuf//protobuf:duration_py_pb2", - "@com_google_protobuf//:protobuf_python", - ], + deps = ["@com_google_protobuf//:protobuf_python"], ) diff --git a/ortools/math_opt/solvers/BUILD.bazel b/ortools/math_opt/solvers/BUILD.bazel index 5e821539dd..bae6cfd88a 100644 --- a/ortools/math_opt/solvers/BUILD.bazel +++ b/ortools/math_opt/solvers/BUILD.bazel @@ -215,8 +215,8 @@ cc_library( "//ortools/base:protoutil", "//ortools/base:status_macros", "//ortools/linear_solver:linear_solver_cc_proto", - "//ortools/linear_solver/proto_solver:sat_proto_solver", "//ortools/linear_solver/proto_solver:proto_utils", + "//ortools/linear_solver/proto_solver:sat_proto_solver", "//ortools/math_opt:callback_cc_proto", "//ortools/math_opt:infeasible_subsystem_cc_proto", "//ortools/math_opt:model_cc_proto", diff --git a/ortools/pdlp/quadratic_program_io.cc b/ortools/pdlp/quadratic_program_io.cc index b4d64bde70..5448e28f93 100644 --- a/ortools/pdlp/quadratic_program_io.cc +++ b/ortools/pdlp/quadratic_program_io.cc @@ -69,7 +69,8 @@ QuadraticProgram ReadQuadraticProgramOrDie(const std::string& filename, QuadraticProgram ReadMPModelProtoFileOrDie( const std::string& mpmodel_proto_file, bool include_names) { MPModelProto lp_proto; - QCHECK(ReadFileToProto(mpmodel_proto_file, &lp_proto)) << mpmodel_proto_file; + QCHECK_OK(ReadFileToProto(mpmodel_proto_file, &lp_proto)) + << mpmodel_proto_file; auto result = QpFromMpModelProto(lp_proto, /*relax_integer_variables=*/true, include_names); QCHECK_OK(result); diff --git a/ortools/sat/sat_runner.cc b/ortools/sat/sat_runner.cc index 09b33dd1eb..aff02c68f4 100644 --- a/ortools/sat/sat_runner.cc +++ b/ortools/sat/sat_runner.cc @@ -98,7 +98,7 @@ bool LoadProblem(const std::string& filename, CpModelProto* cp_model) { } } else { LOG(INFO) << "Reading a CpModelProto."; - *cp_model = ReadFileToProtoOrDie(filename); + CHECK_OK(ReadFileToProto(filename, cp_model)); } if (cp_model->name().empty()) { cp_model->set_name(ExtractName(filename)); diff --git a/ortools/util/BUILD.bazel b/ortools/util/BUILD.bazel index fb2bb70dd7..25845573fe 100644 --- a/ortools/util/BUILD.bazel +++ b/ortools/util/BUILD.bazel @@ -295,6 +295,7 @@ cc_library( "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "//ortools/base", + "//ortools/base:dump_vars", "//ortools/base:file", "//ortools/base:hash", "//ortools/base:recordio", diff --git a/ortools/util/file_util.cc b/ortools/util/file_util.cc index 0ac5117e21..8738bb443f 100644 --- a/ortools/util/file_util.cc +++ b/ortools/util/file_util.cc @@ -16,8 +16,11 @@ #include #include "absl/log/check.h" +#include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/str_format.h" #include "absl/strings/string_view.h" +#include "google/protobuf/io/tokenizer.h" #include "google/protobuf/io/zero_copy_stream_impl_lite.h" #include "google/protobuf/json/json.h" #include "google/protobuf/message.h" @@ -45,17 +48,23 @@ absl::StatusOr ReadFileToString(absl::string_view filename) { return contents; } -bool ReadFileToProto(absl::string_view filename, - google::protobuf::Message* proto, bool allow_partial) { +absl::Status ReadFileToProto(absl::string_view filename, + google::protobuf::Message* proto, + bool allow_partial) { std::string data; - CHECK_OK(file::GetContents(filename, &data, file::Defaults())); + RETURN_IF_ERROR(file::GetContents(filename, &data, file::Defaults())); + return util::StatusBuilder(StringToProto(data, proto, allow_partial)) + << " in file '" << filename << "'"; +} + +absl::Status StringToProto(absl::string_view data, + google::protobuf::Message* proto, + bool allow_partial) { // Try decompressing it. - { - std::string uncompressed; - if (GunzipString(data, &uncompressed)) { - VLOG(1) << "ReadFileToProto(): input is gzipped"; - data.swap(uncompressed); - } + std::string uncompressed; + if (GunzipString(data, &uncompressed)) { + VLOG(1) << "ReadFileToProto(): input is gzipped"; + data = uncompressed; } // Try binary format first, then text format, then JSON, then proto3 JSON, // then give up. @@ -68,6 +77,7 @@ bool ReadFileToProto(absl::string_view filename, // the case that the proto version changed and some fields are dropped. // We just fail when the difference is too large. constexpr double kMaxBinaryProtoParseShrinkFactor = 2; + std::string binary_format_error; if (proto->ParsePartialFromString(data) && (allow_partial || proto->IsInitialized())) { // NOTE(user): When using ParseFromString() from a generic @@ -78,60 +88,76 @@ bool ReadFileToProto(absl::string_view filename, proto->DiscardUnknownFields(); if (proto->ByteSizeLong() < data.size() / kMaxBinaryProtoParseShrinkFactor) { - VLOG(1) << "ReadFileToProto(): input may be a binary proto, but of a " - "different proto"; + binary_format_error = + "The input may be a binary protobuf payload, but it" + " probably is from a different proto class"; } else { - VLOG(1) << "ReadFileToProto(): input seems to be a binary proto"; - return true; + VLOG(1) << "StringToProto(): input seems to be a binary proto"; + return absl::OkStatus(); } } + + struct : public google::protobuf::io::ErrorCollector { + void RecordError(int line, google::protobuf::io::ColumnNumber column, + absl::string_view error_message) override { + if (!message.empty()) message += ", "; + absl::StrAppendFormat(&message, "%d:%d: %s", + // We convert the 0-indexed line to 1-indexed. + line + 1, column, error_message); + } + std::string message; + } text_format_error; google::protobuf::TextFormat::Parser text_parser; + text_parser.RecordErrorsTo(&text_format_error); text_parser.AllowPartialMessage(allow_partial); if (text_parser.ParseFromString(data, proto)) { - VLOG(1) << "ReadFileToProto(): input is a text proto"; - return true; + VLOG(1) << "StringToProto(): input is a text proto"; + return absl::OkStatus(); } - // We use `auto` here since protobuf does not use absl::Status. - const auto status = JsonStringToMessage(data, proto, JsonParseOptions()); - if (!status.ok()) { - VLOG(1) << status; - } else { + std::string json_error; + absl::Status json_status = + JsonStringToMessage(data, proto, JsonParseOptions()); + if (json_status.ok()) { // NOTE(user): We protect against the JSON proto3 parser being very lenient // and easily accepting any JSON as a valid JSON for our proto: if the // parsed proto's size is too small compared to the JSON, we probably parsed // a JSON that wasn't representing a valid proto. constexpr int kMaxJsonToBinaryShrinkFactor = 30; if (proto->ByteSizeLong() < data.size() / kMaxJsonToBinaryShrinkFactor) { - VLOG(1) << "ReadFileToProto(): input is probably JSON, but probably not" - " of the right proto"; + json_status = absl::InvalidArgumentError( + "The input looks like valid JSON, but probably not" + " of the right proto class"); } else { - VLOG(1) << "ReadFileToProto(): input is a proto JSON"; - return true; + VLOG(1) << "StringToProto(): input is a proto JSON"; + return absl::OkStatus(); } } - LOG(WARNING) << "Could not parse protocol buffer"; - return false; + return absl::InvalidArgumentError(absl::StrFormat( + "binary format error: '%s', text format error: '%s', json error: '%s'", + binary_format_error, text_format_error.message, json_status.message())); } -bool WriteProtoToFile(absl::string_view filename, - const google::protobuf::Message& proto, - ProtoWriteFormat proto_write_format, bool gzipped, - bool append_extension_to_file_name) { +absl::Status WriteProtoToFile(absl::string_view filename, + const google::protobuf::Message& proto, + ProtoWriteFormat proto_write_format, bool gzipped, + bool append_extension_to_file_name) { std::string file_type_suffix; std::string output_string; google::protobuf::io::StringOutputStream stream(&output_string); + auto make_error = [filename](absl::string_view error_message) { + return absl::InternalError(absl::StrFormat( + "WriteProtoToFile('%s') failed: %s", filename, error_message)); + }; switch (proto_write_format) { case ProtoWriteFormat::kProtoBinary: if (!proto.SerializeToZeroCopyStream(&stream)) { - LOG(WARNING) << "Serialize to stream failed."; - return false; + return make_error("SerializeToZeroCopyStream()"); } file_type_suffix = ".bin"; break; case ProtoWriteFormat::kProtoText: if (!google::protobuf::TextFormat::PrintToString(proto, &output_string)) { - LOG(WARNING) << "Printing to string failed."; - return false; + return make_error("TextFormat::PrintToString()"); } break; case ProtoWriteFormat::kJson: { @@ -142,8 +168,7 @@ bool WriteProtoToFile(absl::string_view filename, if (!google::protobuf::util::MessageToJsonString(proto, &output_string, options) .ok()) { - LOG(WARNING) << "Printing to stream failed."; - return false; + return make_error("Printing to stream failed."); } file_type_suffix = ".json"; break; @@ -154,8 +179,7 @@ bool WriteProtoToFile(absl::string_view filename, if (!google::protobuf::util::MessageToJsonString(proto, &output_string, options) .ok()) { - LOG(WARNING) << "Printing to stream failed."; - return false; + return make_error("Printing to stream failed."); } file_type_suffix = ".json"; break; @@ -168,14 +192,9 @@ bool WriteProtoToFile(absl::string_view filename, } std::string output_filename(filename); if (append_extension_to_file_name) output_filename += file_type_suffix; - VLOG(1) << "Writing " << output_string.size() << " bytes to " - << output_filename; - if (!file::SetContents(output_filename, output_string, file::Defaults()) - .ok()) { - LOG(WARNING) << "Writing to file failed."; - return false; - } - return true; + VLOG(1) << "Writing " << output_string.size() << " bytes to '" + << output_filename << "'"; + return file::SetContents(output_filename, output_string, file::Defaults()); } } // namespace operations_research diff --git a/ortools/util/file_util.h b/ortools/util/file_util.h index 73ce1445e0..58c2b572a3 100644 --- a/ortools/util/file_util.h +++ b/ortools/util/file_util.h @@ -21,9 +21,11 @@ #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "google/protobuf/message.h" +#include "ortools/base/dump_vars.h" #include "ortools/base/file.h" #include "ortools/base/options.h" #include "ortools/base/recordio.h" +#include "ortools/base/status_macros.h" namespace operations_research { @@ -31,23 +33,27 @@ namespace operations_research { absl::StatusOr ReadFileToString(absl::string_view filename); // Reads a proto from a file. Supports the following formats: binary, text, -// JSON, all of those optionally gzipped. Crashes on filesystem failures, e.g. -// file unreadable. Returns false on format failures, e.g. the file could be -// read, but the contents couldn't be parsed -- or maybe it was a valid JSON, -// text proto, or binary proto, but not of the right proto message. -// Returns true on success. -bool ReadFileToProto(absl::string_view filename, - google::protobuf::Message* proto, - // If true, unset required fields don't cause errors. This - // boolean doesn't work for JSON inputs. - bool allow_partial = false); +// JSON, all of those optionally gzipped. Returns errors as expected: filesystem +// error, parsing errors, or type error: maybe it was a valid JSON, text proto, +// or binary proto, but not of the right proto message (this is not an exact +// science, but the heuristics used should work well in practice). +absl::Status ReadFileToProto( + absl::string_view filename, google::protobuf::Message* proto, + // If true, unset required fields don't cause errors. This + // boolean doesn't work for JSON inputs. + bool allow_partial = false); + +// Exaclty like ReadFileToProto(), but directly from the contents. +absl::Status StringToProto(absl::string_view data, + google::protobuf::Message* proto, + bool allow_partial = false); template -Proto ReadFileToProtoOrDie(absl::string_view filename, - bool allow_partial = false) { +absl::StatusOr ReadFileToProto(absl::string_view filename, + bool allow_partial = false) { Proto proto; - CHECK(ReadFileToProto(filename, &proto, allow_partial)) - << "with file: '" << filename << "'"; + RETURN_IF_ERROR(ReadFileToProto(filename, &proto, allow_partial)) + << DUMP_VARS(filename); return proto; } @@ -56,14 +62,15 @@ Proto ReadFileToProtoOrDie(absl::string_view filename, enum class ProtoWriteFormat { kProtoText, kProtoBinary, kJson, kCanonicalJson }; // Writes a proto to a file. Supports the following formats: binary, text, JSON, -// all of those optionally gzipped. Returns false on failure. +// all of those optionally gzipped. // If 'proto_write_format' is kProtoBinary, ".bin" is appended to file_name. If // 'proto_write_format' is kJson or kCanonicalJson, ".json" is appended to // file_name. If 'gzipped' is true, ".gz" is appended to file_name. -bool WriteProtoToFile(absl::string_view filename, - const google::protobuf::Message& proto, - ProtoWriteFormat proto_write_format, bool gzipped = false, - bool append_extension_to_file_name = true); +absl::Status WriteProtoToFile(absl::string_view filename, + const google::protobuf::Message& proto, + ProtoWriteFormat proto_write_format, + bool gzipped = false, + bool append_extension_to_file_name = true); namespace internal { // General method to read expected_num_records from a file. If