Browse Source

cmake -E: Add cmake_transform_depfile internal command

Kyle Edwards 5 years ago
parent
commit
b2c14bc774

+ 2 - 0
Source/CMakeLists.txt

@@ -439,6 +439,8 @@ set(SRCS
   cmTest.h
   cmTestGenerator.cxx
   cmTestGenerator.h
+  cmTransformDepfile.cxx
+  cmTransformDepfile.h
   cmUuid.cxx
   cmUVHandlePtr.cxx
   cmUVHandlePtr.h

+ 114 - 0
Source/cmTransformDepfile.cxx

@@ -0,0 +1,114 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmTransformDepfile.h"
+
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <cm/optional>
+
+#include "cmsys/FStream.hxx"
+
+#include "cmGccDepfileReader.h"
+#include "cmGccDepfileReaderTypes.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+namespace {
+void WriteFilenameGcc(cmsys::ofstream& fout, const std::string& filename)
+{
+  for (auto c : filename) {
+    switch (c) {
+      case ' ':
+        fout << "\\ ";
+        break;
+      case '\\':
+        fout << "\\\\";
+        break;
+      default:
+        fout << c;
+        break;
+    }
+  }
+}
+
+void WriteGccDepfile(cmsys::ofstream& fout, const cmGccDepfileContent& content)
+{
+  for (auto const& dep : content) {
+    bool first = true;
+    for (auto const& rule : dep.rules) {
+      if (!first) {
+        fout << " \\\n  ";
+      }
+      first = false;
+      WriteFilenameGcc(fout, rule);
+    }
+    fout << ':';
+    for (auto const& path : dep.paths) {
+      fout << " \\\n  " << path;
+    }
+    fout << '\n';
+  }
+}
+
+void WriteVsTlog(cmsys::ofstream& fout, const cmGccDepfileContent& content)
+{
+  for (auto const& dep : content) {
+    fout << '^';
+    bool first = true;
+    for (auto const& rule : dep.rules) {
+      if (!first) {
+        fout << '|';
+      }
+      first = false;
+      fout << cmSystemTools::ConvertToOutputPath(rule);
+    }
+    fout << "\r\n";
+    for (auto const& path : dep.paths) {
+      fout << cmSystemTools::ConvertToOutputPath(path) << "\r\n";
+    }
+  }
+}
+}
+
+bool cmTransformDepfile(cmDepfileFormat format, const std::string& prefix,
+                        const std::string& infile, const std::string& outfile)
+{
+  cmGccDepfileContent content;
+  if (cmSystemTools::FileExists(infile)) {
+    auto result = cmReadGccDepfile(infile.c_str());
+    if (!result) {
+      return false;
+    }
+    content = *std::move(result);
+  }
+
+  for (auto& dep : content) {
+    for (auto& rule : dep.rules) {
+      if (!cmSystemTools::FileIsFullPath(rule)) {
+        rule = cmStrCat(prefix, rule);
+      }
+    }
+    for (auto& path : dep.paths) {
+      if (!cmSystemTools::FileIsFullPath(path)) {
+        path = cmStrCat(prefix, path);
+      }
+    }
+  }
+
+  cmsys::ofstream fout(outfile.c_str());
+  if (!fout) {
+    return false;
+  }
+  switch (format) {
+    case cmDepfileFormat::GccDepfile:
+      WriteGccDepfile(fout, content);
+      break;
+    case cmDepfileFormat::VsTlog:
+      WriteVsTlog(fout, content);
+      break;
+  }
+  return true;
+}

+ 14 - 0
Source/cmTransformDepfile.h

@@ -0,0 +1,14 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <string>
+
+enum class cmDepfileFormat
+{
+  GccDepfile,
+  VsTlog,
+};
+
+bool cmTransformDepfile(cmDepfileFormat format, const std::string& prefix,
+                        const std::string& infile, const std::string& outfile);

+ 18 - 0
Source/cmcmd.cxx

@@ -19,6 +19,7 @@
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmTransformDepfile.h"
 #include "cmUVProcessChain.h"
 #include "cmUtils.hxx"
 #include "cmVersion.h"
@@ -1426,6 +1427,23 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args)
       return cmcmd::WindowsCEEnvironment("9.0", args[2]);
     }
 #endif
+
+    // Internal depfile transformation
+    if (args[1] == "cmake_transform_depfile" && args.size() == 6) {
+      auto format = cmDepfileFormat::GccDepfile;
+      if (args[2] == "gccdepfile") {
+        format = cmDepfileFormat::GccDepfile;
+      } else if (args[2] == "vstlog") {
+        format = cmDepfileFormat::VsTlog;
+      } else {
+        return 1;
+      }
+      std::string prefix = args[3];
+      if (prefix == "./") {
+        prefix.clear();
+      }
+      return cmTransformDepfile(format, prefix, args[4], args[5]) ? 0 : 1;
+    }
   }
 
   ::CMakeCommandUsage(args[0].c_str());

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -771,6 +771,7 @@ add_RunCMake_test(PrecompileHeaders -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
 
 add_RunCMake_test("UnityBuild")
 add_RunCMake_test(CMakePresets)
+add_RunCMake_test(TransformDepfile)
 
 if(WIN32)
   add_RunCMake_test(Win32GenEx)

+ 21 - 0
Tests/RunCMake/TransformDepfile/RunCMakeTest.cmake

@@ -0,0 +1,21 @@
+include(RunCMake)
+
+function(run_transform_depfile name)
+  set(RunCMake-check-file gccdepfile.cmake)
+  run_cmake_command(${name}-gcc
+    ${CMAKE_COMMAND} -E cmake_transform_depfile gccdepfile ../ ${CMAKE_CURRENT_LIST_DIR}/${name}.d out.d
+    )
+  set(RunCMake-check-file vstlog.cmake)
+  run_cmake_command(${name}-tlog
+    ${CMAKE_COMMAND} -E cmake_transform_depfile vstlog ../ ${CMAKE_CURRENT_LIST_DIR}/${name}.d out.tlog
+    )
+endfunction()
+
+if(WIN32)
+  run_transform_depfile(deps-windows)
+else()
+  run_transform_depfile(deps-unix)
+endif()
+run_transform_depfile(noexist)
+run_transform_depfile(empty)
+run_transform_depfile(invalid)

+ 6 - 0
Tests/RunCMake/TransformDepfile/deps-unix.d

@@ -0,0 +1,6 @@
+out1 /home/build/out2: in1 /home/build/in2
+
+out3 \
+  /home/build/out4: \
+  in3 \
+  /home/build/in4

+ 8 - 0
Tests/RunCMake/TransformDepfile/deps-unix.d.txt

@@ -0,0 +1,8 @@
+../out1 \
+  /home/build/out2: \
+  ../in1 \
+  /home/build/in2
+../out3 \
+  /home/build/out4: \
+  ../in3 \
+  /home/build/in4

+ 6 - 0
Tests/RunCMake/TransformDepfile/deps-unix.tlog.txt

@@ -0,0 +1,6 @@
+^../out1|/home/build/out2
+../in1
+/home/build/in2
+^../out3|/home/build/out4
+../in3
+/home/build/in4

+ 6 - 0
Tests/RunCMake/TransformDepfile/deps-windows.d

@@ -0,0 +1,6 @@
+out1 C:/build/out2: in1 C:/build/in2
+
+out3 \
+  C:/build/out4: \
+  in3 \
+  C:/build/in4

+ 8 - 0
Tests/RunCMake/TransformDepfile/deps-windows.d.txt

@@ -0,0 +1,8 @@
+../out1 \
+  C:/build/out2: \
+  ../in1 \
+  C:/build/in2
+../out3 \
+  C:/build/out4: \
+  ../in3 \
+  C:/build/in4

+ 6 - 0
Tests/RunCMake/TransformDepfile/deps-windows.tlog.txt

@@ -0,0 +1,6 @@
+^..\out1|C:\build\out2
+..\in1
+C:\build\in2
+^..\out3|C:\build\out4
+..\in3
+C:\build\in4

+ 0 - 0
Tests/RunCMake/TransformDepfile/empty.d


+ 0 - 0
Tests/RunCMake/TransformDepfile/empty.d.txt


+ 0 - 0
Tests/RunCMake/TransformDepfile/empty.tlog.txt


+ 16 - 0
Tests/RunCMake/TransformDepfile/gccdepfile.cmake

@@ -0,0 +1,16 @@
+if(EXISTS "${RunCMake_SOURCE_DIR}/${name}.d.txt")
+  file(READ "${RunCMake_SOURCE_DIR}/${name}.d.txt" expected_contents)
+
+  if(EXISTS "${RunCMake_TEST_BINARY_DIR}/out.d")
+    file(READ "${RunCMake_TEST_BINARY_DIR}/out.d" actual_contents)
+    if(NOT actual_contents STREQUAL expected_contents)
+      string(REPLACE "\n" "\n  " p_expected_contents "${expected_contents}")
+      string(REPLACE "\n" "\n  " p_actual_contents "${actual_contents}")
+      string(APPEND RunCMake_TEST_FAILED "Expected contents of ${RunCMake_TEST_BINARY_DIR}/out.d:\n  ${p_expected_contents}\nActual contents:\n  ${p_actual_contents}")
+    endif()
+  else()
+    string(APPEND RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/out.d should exist\n")
+  endif()
+elseif(EXISTS "${RunCMake_TEST_BINARY_DIR}/out.d")
+  string(APPEND RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/out.d should not exist\n")
+endif()

+ 1 - 0
Tests/RunCMake/TransformDepfile/invalid-gcc-result.txt

@@ -0,0 +1 @@
+1

+ 1 - 0
Tests/RunCMake/TransformDepfile/invalid-tlog-result.txt

@@ -0,0 +1 @@
+1

+ 1 - 0
Tests/RunCMake/TransformDepfile/invalid.d

@@ -0,0 +1 @@
+invalid

+ 0 - 0
Tests/RunCMake/TransformDepfile/noexist.d.txt


+ 0 - 0
Tests/RunCMake/TransformDepfile/noexist.tlog.txt


+ 16 - 0
Tests/RunCMake/TransformDepfile/vstlog.cmake

@@ -0,0 +1,16 @@
+if(EXISTS "${RunCMake_SOURCE_DIR}/${name}.tlog.txt")
+  file(READ "${RunCMake_SOURCE_DIR}/${name}.tlog.txt" expected_contents)
+
+  if(EXISTS "${RunCMake_TEST_BINARY_DIR}/out.tlog")
+    file(READ "${RunCMake_TEST_BINARY_DIR}/out.tlog" actual_contents)
+    if(NOT actual_contents STREQUAL expected_contents)
+      string(REPLACE "\n" "\n  " p_expected_contents "${expected_contents}")
+      string(REPLACE "\n" "\n  " p_actual_contents "${actual_contents}")
+      string(APPEND RunCMake_TEST_FAILED "Expected contents of ${RunCMake_TEST_BINARY_DIR}/out.tlog:\n  ${p_expected_contents}\nActual contents:\n  ${p_actual_contents}")
+    endif()
+  else()
+    string(APPEND RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/out.tlog should exist\n")
+  endif()
+elseif(EXISTS "${RunCMake_TEST_BINARY_DIR}/out.tlog")
+  string(APPEND RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/out.tlog should not exist\n")
+endif()

+ 4 - 0
bootstrap

@@ -412,6 +412,8 @@ CMAKE_CXX_SOURCES="\
   cmProjectCommand \
   cmPropertyDefinition \
   cmPropertyMap \
+  cmGccDepfileLexerHelper \
+  cmGccDepfileReader \
   cmReturnCommand \
   cmRulePlaceholderExpander \
   cmRuntimeDependencyArchive \
@@ -452,6 +454,7 @@ CMAKE_CXX_SOURCES="\
   cmTest \
   cmTestGenerator \
   cmTimestamp \
+  cmTransformDepfile \
   cmTryCompileCommand \
   cmTryRunCommand \
   cmUnsetCommand \
@@ -491,6 +494,7 @@ LexerParser_CXX_SOURCES="\
   cmCommandArgumentParser \
   cmExprLexer \
   cmExprParser \
+  cmGccDepfileLexer \
 "
 
 LexerParser_C_SOURCES="\