Quellcode durchsuchen

pvs-studio: Add support for linting with PVS-Studio

Martin Duffy vor 3 Monaten
Ursprung
Commit
ef12ef1bd6

+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -327,6 +327,7 @@ Properties on Targets
    /prop_tgt/LANG_ICSTAT
    /prop_tgt/LANG_INCLUDE_WHAT_YOU_USE
    /prop_tgt/LANG_LINKER_LAUNCHER
+   /prop_tgt/LANG_PVS_STUDIO
    /prop_tgt/LANG_STANDARD
    /prop_tgt/LANG_STANDARD_REQUIRED
    /prop_tgt/LANG_VISIBILITY_PRESET

+ 1 - 0
Help/manual/cmake-variables.7.rst

@@ -529,6 +529,7 @@ Variables that Control the Build
    /variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG
    /variable/CMAKE_LANG_LINKER_LAUNCHER
+   /variable/CMAKE_LANG_PVS_STUDIO
    /variable/CMAKE_LANG_USING_LINKER_TYPE
    /variable/CMAKE_LANG_VISIBILITY_PRESET
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY

+ 6 - 5
Help/prop_sf/SKIP_LINTING.rst

@@ -6,11 +6,12 @@ SKIP_LINTING
 This property allows you to exclude a specific source file
 from the linting process. The linting process involves running
 tools such as :prop_tgt:`<LANG>_CPPLINT`, :prop_tgt:`<LANG>_CLANG_TIDY`,
-:prop_tgt:`<LANG>_CPPCHECK`, :prop_tgt:`<LANG>_ICSTAT` and
-:prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE` on the source files, as well
-as compiling header files as part of :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS`.
-By setting ``SKIP_LINTING`` on a source file, the mentioned linting tools
-will not be executed for that particular file.
+:prop_tgt:`<LANG>_CPPCHECK`, :prop_tgt:`<LANG>_ICSTAT`,
+:prop_tgt:`<LANG>_PVS_STUDIO` and :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE` on the
+source files, as well as compiling header files as part of
+:prop_tgt:`VERIFY_INTERFACE_HEADER_SETS`. By setting ``SKIP_LINTING`` on a
+source file, the mentioned linting tools will not be executed for that
+particular file.
 
 Example
 ^^^^^^^

+ 39 - 0
Help/prop_tgt/LANG_PVS_STUDIO.rst

@@ -0,0 +1,39 @@
+<LANG>_PVS_STUDIO
+-----------------
+
+.. versionadded:: 4.3
+
+This property is implemented only when ``<LANG>`` is ``C`` or ``CXX``.
+
+Specify a :ref:`semicolon-separated list <CMake Language Lists>` containing
+a command line for the ``pvs-studio-analyzer`` tool (named
+``CompilerCommandsAnalyzer`` on Windows).  The :ref:`Makefile Generators` and
+:ref:`Ninja Generators` will run this tool along with the compiler and
+report a warning if the tool reports any problems.
+
+The specified ``pvs-studio-analyzer`` command line will be invoked with
+the following additional arguments:
+
+- ``--source-file``: The source file.
+- ``--output-file``: A path adjacent to the object file to write the PVS log.
+- ``--cl-params``: The compile options.
+- ``--preprocessor``: The preprocessor, based on
+  :variable:`CMAKE_<LANG>_COMPILER_ID`, if determined to be one of:
+  ``visualcpp``, ``clang``, ``gcc``, ``bcc``, ``iar``.
+- ``--platform``: The target platform, if determined to be one of: ``arm``,
+  ``win32``, ``x64``, ``linux32``, ``linux64``, ``macOS``.
+
+See the
+`PVS-Studio documentation <https://pvs-studio.com/en/docs/manual/6615/#flags>`_
+for details on these and other available options.
+
+CMake will look for the ``plog-converter`` tool in the same directory as the
+provided ``pvs-studio-analyzer``, and in the user's path if not present in that
+directory. The ``plog-converter`` will run automatically with the ``Txt``
+output type on Windows, and ``errorfile`` on other platforms, and the contents
+of that file will be sent to ``stderr``. The PVS log file will be deleted after
+the converter runs.
+
+This property is initialized by the value of
+the :variable:`CMAKE_<LANG>_PVS_STUDIO` variable if it is set
+when a target is created.

+ 7 - 0
Help/release/dev/pvs-analyze.rst

@@ -0,0 +1,7 @@
+pvs-analyze
+-----------
+
+* A :prop_tgt:`<LANG>_PVS_STUDIO` target property and supporting
+  :variable:`CMAKE_<LANG>_PVS_STUDIO` variable were introduced to tell
+  :ref:`Makefile Generators` and :ref:`Ninja Generators` to run
+  ``pvs-studio-analyzer`` with the compiler for ``C`` and ``CXX`` languages.

+ 15 - 0
Help/variable/CMAKE_LANG_PVS_STUDIO.rst

@@ -0,0 +1,15 @@
+CMAKE_<LANG>_PVS_STUDIO
+-----------------------
+
+.. versionadded:: 4.3
+
+Default value for :prop_tgt:`<LANG>_PVS_STUDIO` target property
+when ``<LANG>`` is ``C`` or ``CXX``.
+
+This variable is used to initialize the property on each target as it is
+created.  For example:
+
+.. code-block:: cmake
+
+  set(CMAKE_CXX_PVS_STUDIO pvs-studio-analyzer analyze -a "GA\;OP")
+  add_executable(foo foo.cxx)

+ 31 - 2
Source/cmCommonTargetGenerator.cxx

@@ -360,6 +360,7 @@ std::string cmCommonTargetGenerator::GenerateCodeCheckRules(
   std::string cpplint;
   std::string cppcheck;
   std::string icstat;
+  std::string pvs;
 
   auto evaluateProp = [&](std::string const& prop) -> std::string {
     auto const value = this->GeneratorTarget->GetProperty(prop);
@@ -386,9 +387,13 @@ std::string cmCommonTargetGenerator::GenerateCodeCheckRules(
 
     std::string const icstat_prop = cmStrCat(lang, "_ICSTAT");
     icstat = evaluateProp(icstat_prop);
+
+    std::string const pvs_prop = cmStrCat(lang, "_PVS_STUDIO");
+    pvs = evaluateProp(pvs_prop);
   }
+
   if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) ||
-      cmNonempty(cppcheck) || cmNonempty(icstat)) {
+      cmNonempty(cppcheck) || cmNonempty(icstat) || cmNonempty(pvs)) {
     std::string code_check = cmakeCmd + " -E __run_co_compile";
     if (!compilerLauncher.empty()) {
       // In __run_co_compile case the launcher command is supplied
@@ -476,6 +481,29 @@ std::string cmCommonTargetGenerator::GenerateCodeCheckRules(
                      exportFixes));
       }
     }
+    if (cmNonempty(pvs)) {
+      cmMakefile* mf =
+        this->GeneratorTarget->GetLocalGenerator()->GetMakefile();
+      std::string extraPvsArgs;
+      if (lang == "CXX") {
+        extraPvsArgs +=
+          cmStrCat(";--cxx;", mf->GetDefinition("CMAKE_CXX_COMPILER"));
+      } else if (lang == "C") {
+        extraPvsArgs +=
+          cmStrCat(";--cc;", mf->GetDefinition("CMAKE_C_COMPILER"));
+      }
+      // cocompile args
+      code_check += " --pvs-studio=";
+      code_check += this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
+        cmStrCat(pvs, extraPvsArgs));
+      code_check += " --object=";
+      code_check +=
+        this->GeneratorTarget->GetLocalGenerator()->ConvertToOutputFormat(
+          cmSystemTools::CollapseFullPath(
+            cmStrCat(this->GeneratorTarget->GetObjectDirectory(config), '/',
+                     this->GeneratorTarget->GetObjectName(&source))),
+          cmOutputConverter::SHELL);
+    }
     if (cmNonempty(cpplint)) {
       code_check += " --cpplint=";
       code_check +=
@@ -504,7 +532,8 @@ std::string cmCommonTargetGenerator::GenerateCodeCheckRules(
       code_check += this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(
         cmStrCat(icstat, checksParam, dbParam));
     }
-    if (cmNonempty(tidy) || (cmNonempty(cpplint)) || (cmNonempty(cppcheck))) {
+    if (cmNonempty(tidy) || (cmNonempty(cpplint)) || (cmNonempty(cppcheck)) ||
+        cmNonempty(pvs)) {
       code_check += " --source=";
       code_check +=
         this->GeneratorTarget->GetLocalGenerator()->ConvertToOutputFormat(

+ 3 - 0
Source/cmTarget.cxx

@@ -473,6 +473,7 @@ TargetProperty const StaticTargetProperties[] = {
   { "C_CPPCHECK"_s, IC::CanCompileSources },
   { "C_ICSTAT"_s, IC::CanCompileSources },
   { "C_INCLUDE_WHAT_YOU_USE"_s, IC::CanCompileSources },
+  { "C_PVS_STUDIO"_s, IC::CanCompileSources },
   // -- C++
   { "CXX_CLANG_TIDY"_s, IC::CanCompileSources },
   { "CXX_CLANG_TIDY_EXPORT_FIXES_DIR"_s, IC::CanCompileSources },
@@ -480,6 +481,7 @@ TargetProperty const StaticTargetProperties[] = {
   { "CXX_CPPCHECK"_s, IC::CanCompileSources },
   { "CXX_ICSTAT"_s, IC::CanCompileSources },
   { "CXX_INCLUDE_WHAT_YOU_USE"_s, IC::CanCompileSources },
+  { "CXX_PVS_STUDIO"_s, IC::CanCompileSources },
   // -- Objective C
   { "OBJC_CLANG_TIDY"_s, IC::CanCompileSources },
   { "OBJC_CLANG_TIDY_EXPORT_FIXES_DIR"_s, IC::CanCompileSources },
@@ -1808,6 +1810,7 @@ void cmTarget::CopyImportedCxxModulesProperties(cmTarget const* tgt)
     "CXX_CPPCHECK",
     "CXX_ICSTAT",
     "CXX_INCLUDE_WHAT_YOU_USE",
+    "CXX_PVS_STUDIO",
     "SKIP_LINTING",
 
     // Build graph properties

+ 85 - 9
Source/cmcmd.cxx

@@ -411,6 +411,81 @@ int HandleTidy(std::string const& runCmd, std::string const& sourceFile,
   return ret;
 }
 
+int HandlePVSStudio(std::string const& runCmd, std::string const& sourceFile,
+                    std::string const& objectFile,
+                    std::vector<std::string> const& orig_cmd)
+{
+  cmList pvsCmd{ runCmd, cmList::EmptyElements::Yes };
+  std::string logFile = cmStrCat(objectFile, "-pvs.log");
+  std::string errFile = cmStrCat(objectFile, "-pvs.err");
+  pvsCmd.reserve(pvsCmd.size() + 5 + orig_cmd.size());
+  pvsCmd.emplace_back("--source-file");
+  pvsCmd.emplace_back(sourceFile);
+  pvsCmd.emplace_back("--output-file");
+  pvsCmd.emplace_back(logFile);
+  pvsCmd.emplace_back("--cl-params");
+  for (size_t i = 1; i < orig_cmd.size(); ++i) {
+    pvsCmd.emplace_back(orig_cmd[i]);
+  }
+  int ret;
+  std::string stdOut;
+  std::string stdErr;
+
+  // Run the PVS command line.  Capture its stdout and hide its stderr.
+  if (!cmSystemTools::RunSingleCommand(pvsCmd, &stdOut, &stdErr, &ret, nullptr,
+                                       cmSystemTools::OUTPUT_NONE)) {
+    std::cerr << "Error running '" << pvsCmd[0] << "': " << stdErr << '\n';
+    return 1;
+  }
+  if (ret != 0) {
+    if (ret == 7 && !cmSystemTools::FileExists(logFile)) {
+      return 0; // Analyzer generated no output from source
+    }
+    std::cout << stdOut;
+    std::cerr << stdErr;
+    return ret;
+  }
+
+  // Find the plog-converter tool
+#ifdef _WIN32
+  std::string plogConvertName = "HtmlGenerator.exe";
+#else
+  std::string plogConvertName = "plog-converter";
+#endif
+  std::string plogConvert = cmStrCat(
+    cmSystemTools::GetFilenamePath(pvsCmd.front()), '/', plogConvertName);
+  if (!cmSystemTools::FileIsExecutable(plogConvert)) {
+    plogConvert = cmSystemTools::FindProgram(plogConvertName);
+  }
+  if (plogConvert.empty()) {
+    std::cerr << "Could not find " << plogConvertName << std::endl;
+    return 1;
+  }
+
+  // Run the plog-converter tool
+  std::vector<std::string> plogCmd{ plogConvert, "-t",    "errorfile",
+                                    "-o",        errFile, logFile };
+  if (!cmSystemTools::RunSingleCommand(plogCmd, &stdOut, &stdErr, &ret,
+                                       nullptr, cmSystemTools::OUTPUT_NONE)) {
+    std::cerr << "Error running '" << plogCmd[0] << "': " << stdErr << '\n';
+    return 1;
+  }
+
+  // Show error messages from plog-converter output
+  if (stdOut.find("Total messages 0") == std::string::npos) {
+    cmsys::ifstream errFileStream(errFile.c_str());
+    // output always begins with a Help message
+    errFileStream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
+    std::cerr << errFileStream.rdbuf();
+  }
+  cmSystemTools::RemoveFile(logFile);
+
+  if (ret != 0) {
+    std::cerr << stdErr;
+  }
+  return ret;
+}
+
 int HandleLWYU(std::string const& runCmd, std::string const& sourceFile,
                std::string const& /*objectFile*/,
                std::vector<std::string> const&)
@@ -585,15 +660,16 @@ struct CoCompiler
   bool NoOriginalCommand;
 };
 
-std::array<CoCompiler, 6> const CoCompilers = {
-  { // Table of options and handlers.
-    { "--cppcheck=", HandleCppCheck, false },
-    { "--cpplint=", HandleCppLint, false },
-    { "--icstat=", HandleIcstat, false },
-    { "--iwyu=", HandleIWYU, false },
-    { "--lwyu=", HandleLWYU, true },
-    { "--tidy=", HandleTidy, false } }
-};
+std::array<CoCompiler, 7> const CoCompilers = { {
+  // Table of options and handlers.
+  { "--cppcheck=", HandleCppCheck, false },
+  { "--cpplint=", HandleCppLint, false },
+  { "--icstat=", HandleIcstat, false },
+  { "--iwyu=", HandleIWYU, false },
+  { "--lwyu=", HandleLWYU, true },
+  { "--pvs-studio=", HandlePVSStudio, false },
+  { "--tidy=", HandleTidy, false },
+} };
 
 struct CoCompileJob
 {

+ 10 - 0
Tests/RunCMake/CMakeLists.txt

@@ -310,6 +310,7 @@ if(CMake_TEST_Qt6 AND Qt6Widgets_FOUND)
       "-DCMAKE_PREFIX_PATH:STRING=${base_dir}"
       -DPSEUDO_TIDY=$<TARGET_FILE:pseudo_tidy>
       -DPSEUDO_IWYU=$<TARGET_FILE:pseudo_iwyu>
+      -DPSEUDO_PVS=$<TARGET_FILE:pseudo_pvs>
       -DPSEUDO_CPPLINT=$<TARGET_FILE:pseudo_cpplint>
       -DPSEUDO_CPPCHECK=$<TARGET_FILE:pseudo_cppcheck>
     )
@@ -1166,6 +1167,13 @@ add_executable(pseudo_tidy pseudo_tidy.c)
 add_executable(pseudo_iwyu pseudo_iwyu.c)
 add_executable(pseudo_cpplint pseudo_cpplint.c)
 add_executable(pseudo_cppcheck pseudo_cppcheck.c)
+add_executable(pseudo_pvs pseudo_pvs.c)
+add_executable(pseudo_plog_converter pseudo_plog_converter.c)
+if (WIN32)
+  set_target_properties(pseudo_plog_converter PROPERTIES OUTPUT_NAME HtmlGenerator)
+else()
+  set_target_properties(pseudo_plog_converter PROPERTIES OUTPUT_NAME plog-converter)
+endif()
 
 if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja|FASTBuild")
   if(UNIX AND NOT CYGWIN)
@@ -1179,11 +1187,13 @@ if("${CMAKE_GENERATOR}" MATCHES "Make|Ninja|FASTBuild")
   endif()
   add_RunCMake_test(ClangTidy -DPSEUDO_TIDY=$<TARGET_FILE:pseudo_tidy>)
   add_RunCMake_test(IncludeWhatYouUse -DPSEUDO_IWYU=$<TARGET_FILE:pseudo_iwyu>)
+  add_RunCMake_test(PVSStudio -DPSEUDO_PVS=$<TARGET_FILE:pseudo_pvs>)
   add_RunCMake_test(Cpplint -DPSEUDO_CPPLINT=$<TARGET_FILE:pseudo_cpplint>)
   add_RunCMake_test(Cppcheck -DPSEUDO_CPPCHECK=$<TARGET_FILE:pseudo_cppcheck>)
   add_RunCMake_test(MultiLint
     -DPSEUDO_TIDY=$<TARGET_FILE:pseudo_tidy>
     -DPSEUDO_IWYU=$<TARGET_FILE:pseudo_iwyu>
+    -DPSEUDO_PVS=$<TARGET_FILE:pseudo_pvs>
     -DPSEUDO_CPPLINT=$<TARGET_FILE:pseudo_cpplint>
     -DPSEUDO_CPPCHECK=$<TARGET_FILE:pseudo_cppcheck>
     )

+ 1 - 0
Tests/RunCMake/CommandLine/E___run_co_compile-no-iwyu-stderr.txt

@@ -4,4 +4,5 @@
 --icstat=
 --iwyu=
 --lwyu=
+--pvs-studio=
 --tidy=

+ 1 - 0
Tests/RunCMake/MultiLint/C.cmake

@@ -3,4 +3,5 @@ set(CMAKE_C_INCLUDE_WHAT_YOU_USE "${PSEUDO_IWYU}" -some -args)
 set(CMAKE_C_CLANG_TIDY "${PSEUDO_TIDY}" -some -args)
 set(CMAKE_C_CPPLINT "${PSEUDO_CPPLINT}" --verbose=0 --linelength=80)
 set(CMAKE_C_CPPCHECK "${PSEUDO_CPPCHECK}")
+set(CMAKE_C_PVS_STUDIO "${PSEUDO_PVS}" analyze)
 add_executable(main main.c)

+ 1 - 0
Tests/RunCMake/MultiLint/CXX.cmake

@@ -3,4 +3,5 @@ set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "$<1:${PSEUDO_IWYU}>" -some -args)
 set(CMAKE_CXX_CLANG_TIDY "$<1:${PSEUDO_TIDY}>" -some -args)
 set(CMAKE_CXX_CPPLINT "$<1:${PSEUDO_CPPLINT}>" --verbose=0 --linelength=80)
 set(CMAKE_CXX_CPPCHECK "$<1:${PSEUDO_CPPCHECK}>")
+set(CMAKE_CXX_PVS_STUDIO "$<1:${PSEUDO_PVS}>" analyze)
 add_executable(main main.cxx)

+ 1 - 0
Tests/RunCMake/MultiLint/RunCMakeTest.cmake

@@ -5,6 +5,7 @@ set(RunCMake_TEST_OPTIONS
   "-DPSEUDO_CPPLINT=${PSEUDO_CPPLINT}"
   "-DPSEUDO_IWYU=${PSEUDO_IWYU}"
   "-DPSEUDO_TIDY=${PSEUDO_TIDY}"
+  "-DPSEUDO_PVS=${PSEUDO_PVS}"
   )
 
 function(run_multilint lang)

+ 3 - 0
Tests/RunCMake/PVSStudio/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 4.1)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 17 - 0
Tests/RunCMake/PVSStudio/RunCMakeTest.cmake

@@ -0,0 +1,17 @@
+include(RunCMake)
+
+set(RunCMake_TEST_OPTIONS "-DPSEUDO_PVS=${PSEUDO_PVS}")
+
+function(run_pvs test)
+  # Use a single build tree for tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test})
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+  run_cmake(${test})
+  set(RunCMake_TEST_OUTPUT_MERGE 1)
+  run_cmake_command(${test}-build ${CMAKE_COMMAND} --build .)
+endfunction()
+
+run_pvs(pvs)
+run_pvs(pvs-bad-arg)

+ 4 - 0
Tests/RunCMake/PVSStudio/main.cxx

@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}

+ 1 - 0
Tests/RunCMake/PVSStudio/pvs-bad-arg-build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/PVSStudio/pvs-bad-arg-build-stdout.txt

@@ -0,0 +1 @@
+stderr from bad command line arg '-bad'

+ 3 - 0
Tests/RunCMake/PVSStudio/pvs-bad-arg.cmake

@@ -0,0 +1,3 @@
+enable_language(CXX)
+set(CMAKE_CXX_PVS_STUDIO "${PSEUDO_PVS};-bad")
+add_executable(main main.cxx)

+ 8 - 0
Tests/RunCMake/PVSStudio/pvs-build-check.cmake

@@ -0,0 +1,8 @@
+file (GLOB_RECURSE errFiles "${RunCMake_TEST_BINARY_DIR}/*-pvs.err")
+file (GLOB_RECURSE logFiles "${RunCMake_TEST_BINARY_DIR}/*-pvs.log")
+if (NOT errFiles)
+  set(RunCMake_TEST_FAILED ".err file not found.")
+endif()
+if (logFiles)
+  set(RunCMake_TEST_FAILED "Leftover .log file found:\n  ${logFiles}\n.err files:\n  ${errFiles}")
+endif()

+ 1 - 0
Tests/RunCMake/PVSStudio/pvs-build-stdout.txt

@@ -0,0 +1 @@
+example warning

+ 3 - 0
Tests/RunCMake/PVSStudio/pvs.cmake

@@ -0,0 +1,3 @@
+enable_language(CXX)
+set(CMAKE_CXX_PVS_STUDIO "$<1:${PSEUDO_PVS}>")
+add_executable(main main.cxx)

+ 2 - 0
Tests/RunCMake/property_init/CompileSources.cmake

@@ -120,12 +120,14 @@ set(properties
   "C_CPPLINT"                               "cpplint"           "<SAME>"
   "C_CPPCHECK"                              "cppcheck"          "<SAME>"
   "C_INCLUDE_WHAT_YOU_USE"                  "iwyu"              "<SAME>"
+  "C_PVS_STUDIO"                            "pvs-studio-analyzer" "<SAME>"
   ## C++
   "CXX_CLANG_TIDY"                          "clang-tidy"        "<SAME>"
   "CXX_CLANG_TIDY_EXPORT_FIXES_DIR"         "${dir}"            "<SAME>"
   "CXX_CPPLINT"                             "cpplint"           "<SAME>"
   "CXX_CPPCHECK"                            "cppcheck"          "<SAME>"
   "CXX_INCLUDE_WHAT_YOU_USE"                "iwyu"              "<SAME>"
+  "CXX_PVS_STUDIO"                          "pvs-studio-analyzer" "<SAME>"
   ## Objective C
   "OBJC_CLANG_TIDY"                         "clang-tidy"        "<SAME>"
   "OBJC_CLANG_TIDY_EXPORT_FIXES_DIR"        "${dir}"            "<SAME>"

+ 46 - 0
Tests/RunCMake/pseudo_plog_converter.c

@@ -0,0 +1,46 @@
+#ifndef _CRT_SECURE_NO_WARNINGS
+#  define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char* argv[])
+{
+  FILE* fin;
+  FILE* fout;
+  char* outFile = NULL;
+  {
+    int i;
+    for (i = 1; i < argc; ++i) {
+      if (strcmp(argv[i], "-o") == 0) {
+        outFile = argv[i + 1];
+      }
+    }
+  }
+  if (outFile == NULL) {
+    printf("No output file.\n");
+    return 1;
+  }
+  fin = fopen(argv[argc - 1], "r");
+  if (fin == NULL) {
+    printf("Error: Could not open input file.\n");
+    return 1;
+  }
+  fout = fopen(outFile, "w");
+  if (fout == NULL) {
+    printf("Error: Could not open output file.\n");
+    fclose(fin);
+    return 1;
+  }
+  {
+    int ch;
+    while ((ch = fgetc(fin)) != EOF) {
+      fputc(ch, fout);
+    }
+  }
+  fprintf(stdout, "Total Messages: 1\n");
+  fclose(fin);
+  fclose(fout);
+  return 0;
+}

+ 30 - 0
Tests/RunCMake/pseudo_pvs.c

@@ -0,0 +1,30 @@
+#ifndef _CRT_SECURE_NO_WARNINGS
+#  define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char* argv[])
+{
+  FILE* f;
+  int i;
+  for (i = 1; i < argc; ++i) {
+    if (strcmp(argv[i], "-bad") == 0) {
+      fprintf(stdout, "stdout from bad command line arg '-bad'\n");
+      fprintf(stderr, "stderr from bad command line arg '-bad'\n");
+      return 1;
+    }
+    if (strcmp(argv[i], "--output-file") == 0) {
+      i++;
+      f = fopen(argv[i], "w");
+      if (!f) {
+        fprintf(stderr, "Error opening %s for writing\n", argv[i]);
+        return 1;
+      }
+      fprintf(f, "discard this line\nexample warning\n");
+      fclose(f);
+    }
+  }
+  return 0;
+}