Browse Source

Merge topic 'pch-relative-includes'

acb9511044 Precompile headers: Treat headers as relative to current source directory

Acked-by: Kitware Robot <[email protected]>
Merge-request: !3840
Brad King 6 years ago
parent
commit
dcc117b944

+ 18 - 3
Help/command/target_precompile_headers.rst

@@ -48,17 +48,32 @@ See the :manual:`cmake-generator-expressions(7)` manual for available
 expressions.  See the :manual:`cmake-compile-features(7)` manual for
 information on compile features and a list of supported compilers.
 
+Usage
+^^^^^
+
 .. code-block:: cmake
 
   target_precompile_headers(<target>
     PUBLIC
-      "project_header.h"
+      project_header.h
     PRIVATE
+      [["other_header.h"]]
       <unordered_map>
   )
 
-Header files will be double quoted if they are not specified with double
-quotes or angle brackets.
+The list of header files is used to generate a header file named
+``cmake_pch.h|xx`` which is used to generate the precompiled header file
+(``.pch``, ``.gch``, ``.pchi``) artifact.  The ``cmake_pch.h|xx`` header
+file will be force included (``-include`` for GCC, ``/FI`` for MSVC) to
+all source files, so sources do not need to have ``#include "pch.h"``.
+
+Header file names specified with angle brackets (e.g. ``<unordered_map>``) or
+explicit double quotes (escaped for the :manual:`cmake-language(7)`,
+e.g. ``[["other_header.h"]]``) will be treated as is, and include directories
+must be available for the compiler to find them.  Other header file names
+(e.g. ``project_header.h``) are interpreted as being relative to the current
+source directory (e.g. :variable:`CMAKE_CURRENT_SOURCE_DIR`) and will be
+included by absolute path.
 
 See Also
 ^^^^^^^^

+ 40 - 1
Source/cmTargetPrecompileHeadersCommand.cxx

@@ -2,17 +2,29 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetPrecompileHeadersCommand.h"
 
+#include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 #include "cmTarget.h"
 
+#include <utility>
+
 bool cmTargetPrecompileHeadersCommand::InitialPass(
   std::vector<std::string> const& args, cmExecutionStatus&)
 {
   return this->HandleArguments(args, "PRECOMPILE_HEADERS", PROCESS_REUSE_FROM);
 }
 
+void cmTargetPrecompileHeadersCommand::HandleInterfaceContent(
+  cmTarget* tgt, const std::vector<std::string>& content, bool prepend,
+  bool system)
+{
+  cmTargetPropCommandBase::HandleInterfaceContent(
+    tgt, ConvertToAbsoluteContent(tgt, content, true), prepend, system);
+}
+
 void cmTargetPrecompileHeadersCommand::HandleMissingTarget(
   const std::string& name)
 {
@@ -31,6 +43,33 @@ std::string cmTargetPrecompileHeadersCommand::Join(
 bool cmTargetPrecompileHeadersCommand::HandleDirectContent(
   cmTarget* tgt, const std::vector<std::string>& content, bool, bool)
 {
-  tgt->AppendProperty("PRECOMPILE_HEADERS", this->Join(content).c_str());
+  tgt->AppendProperty(
+    "PRECOMPILE_HEADERS",
+    this->Join(ConvertToAbsoluteContent(tgt, content, false)).c_str());
   return true;
 }
+
+std::vector<std::string>
+cmTargetPrecompileHeadersCommand::ConvertToAbsoluteContent(
+  cmTarget* /*tgt*/, const std::vector<std::string>& content,
+  bool /*isInterfaceContent*/)
+{
+  std::vector<std::string> absoluteContent;
+  absoluteContent.reserve(content.size());
+  for (std::string const& src : content) {
+    std::string absoluteSrc;
+    // Use '<foo.h>' and '"foo.h"' includes and absolute paths as-is.
+    // Interpret relative paths with respect to the source directory.
+    // If the path starts in a generator expression, assume it is absolute.
+    if (cmHasLiteralPrefix(src, "<") || cmHasLiteralPrefix(src, "\"") ||
+        cmSystemTools::FileIsFullPath(src) ||
+        cmGeneratorExpression::Find(src) == 0) {
+      absoluteSrc = src;
+    } else {
+      absoluteSrc =
+        cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', src);
+    }
+    absoluteContent.emplace_back(std::move(absoluteSrc));
+  }
+  return absoluteContent;
+}

+ 9 - 0
Source/cmTargetPrecompileHeadersCommand.h

@@ -28,6 +28,11 @@ public:
   bool InitialPass(std::vector<std::string> const& args,
                    cmExecutionStatus& status) override;
 
+protected:
+  void HandleInterfaceContent(cmTarget* tgt,
+                              const std::vector<std::string>& content,
+                              bool prepend, bool system) override;
+
 private:
   void HandleMissingTarget(const std::string& name) override;
 
@@ -36,6 +41,10 @@ private:
                            bool prepend, bool system) override;
 
   std::string Join(const std::vector<std::string>& content) override;
+
+  std::vector<std::string> ConvertToAbsoluteContent(
+    cmTarget* tgt, const std::vector<std::string>& content,
+    bool isInterfaceContent);
 };
 
 #endif

+ 1 - 1
Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake

@@ -4,7 +4,7 @@ project(DisabledPch C)
 add_library(foo foo.c)
 target_include_directories(foo PUBLIC include)
 target_precompile_headers(foo PUBLIC
-  foo.h
+  include/foo.h
   <stdio.h>
   \"string.h\"
 )

+ 4 - 9
Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake

@@ -18,19 +18,14 @@ endif()
 
 file(STRINGS ${foo_pch_header} foo_pch_header_strings)
 
-if (NOT "#include \"foo.h\"" IN_LIST foo_pch_header_strings OR
-    NOT "#include <stdio.h>" IN_LIST foo_pch_header_strings OR
-    NOT "#include \"string.h\"" IN_LIST foo_pch_header_strings)
-  set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} has bad content")
+if (NOT foo_pch_header_strings MATCHES ";#include \"[^\"]*PrecompileHeaders/include/foo.h\";#include \"foo2.h\";#include <stdio.h>;#include \"string.h\"(;|$)")
+  set(RunCMake_TEST_FAILED "Generated foo pch header\n  ${foo_pch_header}\nhas bad content:\n  ${foo_pch_header_strings}")
   return()
 endif()
 
 file(STRINGS ${foobar_pch_header} foobar_pch_header_strings)
 
-if (NOT "#include \"foo.h\"" IN_LIST foobar_pch_header_strings OR
-    NOT "#include <stdio.h>" IN_LIST foobar_pch_header_strings OR
-    NOT "#include \"string.h\"" IN_LIST foobar_pch_header_strings OR
-    NOT "#include \"bar.h\"" IN_LIST foobar_pch_header_strings)
-  set(RunCMake_TEST_FAILED "Generated foobar pch header ${foobar_pch_header} has bad content")
+if (NOT foobar_pch_header_strings MATCHES ";#include \"[^\"]*PrecompileHeaders/include/foo.h\";#include \"foo2.h\";#include <stdio.h>;#include \"string.h\";#include \"[^\"]*PrecompileHeaders/include/bar.h\"(;|$)")
+  set(RunCMake_TEST_FAILED "Generated foobar pch header\n  ${foobar_pch_header}\nhas bad content:\n  ${foobar_pch_header_strings}")
   return()
 endif()

+ 3 - 2
Tests/RunCMake/PrecompileHeaders/PchInterface.cmake

@@ -4,14 +4,15 @@ project(PchInterface C)
 add_library(foo foo.c)
 target_include_directories(foo PUBLIC include)
 target_precompile_headers(foo PUBLIC
-  foo.h
+  include/foo.h
+  \"foo2.h\"
   <stdio.h>
   \"string.h\"
 )
 
 add_library(bar INTERFACE)
 target_include_directories(bar INTERFACE include)
-target_precompile_headers(bar INTERFACE bar.h)
+target_precompile_headers(bar INTERFACE include/bar.h)
 
 add_executable(foobar foobar.c)
 target_link_libraries(foobar foo bar)

+ 6 - 0
Tests/RunCMake/PrecompileHeaders/foo.c

@@ -1,6 +1,12 @@
 #include "foo.h"
+#include "foo2.h"
 
 int foo()
 {
   return 0;
 }
+
+int foo2()
+{
+  return 0;
+}

+ 2 - 1
Tests/RunCMake/PrecompileHeaders/foobar.c

@@ -1,7 +1,8 @@
 #include "bar.h"
 #include "foo.h"
+#include "foo2.h"
 
 int main()
 {
-  return foo() + bar();
+  return foo() + foo2() + bar();
 }

+ 6 - 0
Tests/RunCMake/PrecompileHeaders/include/foo2.h

@@ -0,0 +1,6 @@
+#ifndef foo2_h
+#define foo2_h
+
+int foo2(void);
+
+#endif