Commit acb95110 authored by Cristian Adam's avatar Cristian Adam Committed by Brad King

Precompile headers: Treat headers as relative to current source directory

Teach `target_precompile_headers` to treat relative paths the
same way as `target_sources`.

Fixes: #19733
parent ebb93464
......@@ -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
^^^^^^^^
......
......@@ -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;
}
......@@ -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
......@@ -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\"
)
......
......@@ -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()
......@@ -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)
......
#include "foo.h"
#include "foo2.h"
int foo()
{
return 0;
}
int foo2()
{
return 0;
}
#include "bar.h"
#include "foo.h"
#include "foo2.h"
int main()
{
return foo() + bar();
return foo() + foo2() + bar();
}
#ifndef foo2_h
#define foo2_h
int foo2(void);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment