Skip to content

Commit

Permalink
Include pch header automatically and on-demand (#19)
Browse files Browse the repository at this point in the history
Previously it was needed to include pch header in every source file, but Clang does it automatically making the usage non-uniform. It is also a very noisy process to add pch header to an existing project. Automatic on-demand header inclusion solves both issues.

* pch: msvc source automatic header folder inclusion
* pch test refactoring
* pch test msvc automatic pch source generation
* Include pch header automatically and on-demand
* no more need in gcc pch naming hack
  • Loading branch information
Kojoley authored May 3, 2021
1 parent 9552adf commit 64dbb27
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 52 deletions.
9 changes: 4 additions & 5 deletions doc/src/tasks.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -556,9 +556,7 @@ To use precompiled headers, follow the following steps:
1. Create a header that includes headers used by your project that you
want precompiled. It is better to include only headers that are
sufficiently stable -- like headers from the compiler and external
libraries. Please wrap the header in `#ifdef BOOST_BUILD_PCH_ENABLED`, so
that the potentially expensive inclusion of headers is not done when PCH is
not enabled. Include the new header at the top of your source files.
libraries. B2 will include the header automatically and on-demand.

2. Declare a new B2 target for the precompiled header and add
that precompiled header to the sources of the target whose compilation
Expand All @@ -577,8 +575,6 @@ The `pch` example in B2 distribution can be used as reference.

Please note the following:

* The inclusion of the precompiled header must be the first thing in a
source file, before any code or preprocessor directives.
* The build properties used to compile the source files and the
precompiled header must be the same. Consider using project requirements
to assure this.
Expand All @@ -592,6 +588,9 @@ headers are not supported.
namespaces in precompiled headers, which limits their utility. See the
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29085[bug report] for
details.
* Previosuly B2 had not been automatically inluding the header, a user
was required to include the header at the top of every source file
the precompiled header will be used with.

[[bbv2.reference.generated_headers]]
== Generated headers
Expand Down
26 changes: 5 additions & 21 deletions src/tools/gcc.jam
Original file line number Diff line number Diff line change
Expand Up @@ -527,27 +527,27 @@ rule compile.fortran ( targets * : sources * : properties * )

actions compile.c++ bind PCH_FILE
{
"$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -include"$(FORCE_INCLUDES)" -c -o "$(<:W)" "$(>:W)"
"$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -include"$(PCH_FILE:S=)" -I"$(INCLUDES)" -include"$(FORCE_INCLUDES)" -c -o "$(<:W)" "$(>:W)"
}

actions compile.c bind PCH_FILE
{
"$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -include"$(FORCE_INCLUDES)" -c -o "$(<)" "$(>)"
"$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -include"$(PCH_FILE:S=)" -I"$(INCLUDES)" -include"$(FORCE_INCLUDES)" -c -o "$(<)" "$(>)"
}

actions compile.c++.preprocess bind PCH_FILE
{
"$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -include"$(FORCE_INCLUDES)" "$(>:W)" -E >"$(<:W)"
"$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -include"$(PCH_FILE:S=)" -I"$(INCLUDES)" -include"$(FORCE_INCLUDES)" "$(>:W)" -E >"$(<:W)"
}

actions compile.c.preprocess bind PCH_FILE
{
"$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -include"$(FORCE_INCLUDES)" "$(>)" -E >$(<)
"$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -include"$(PCH_FILE:S=)" -I"$(INCLUDES)" -include"$(FORCE_INCLUDES)" "$(>)" -E >$(<)
}

actions compile.fortran
{
"$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(PCH_FILE:D)" -I"$(INCLUDES)" -c -o "$(<)" "$(>)"
"$(CONFIG_COMMAND)" $(LANG) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -include"$(PCH_FILE:S=)" -I"$(INCLUDES)" -c -o "$(<)" "$(>)"
}

rule compile.asm ( targets * : sources * : properties * )
Expand Down Expand Up @@ -591,12 +591,6 @@ class gcc-pch-generator : pch-generator
}
}

local path-prefix = [ path.join $(name)
[ feature.get-values location-prefix
: $(property-set).raw ] ] ;
property-set = [ $(property-set).add-raw
<location-prefix>$(path-prefix) ] ;

local pch-file = [ generator.run $(project) $(name) : $(property-set)
: $(header) ] ;

Expand All @@ -607,16 +601,6 @@ class gcc-pch-generator : pch-generator
$(pch-file[2-])
;
}

# Calls the base version specifying source's name as the name of the created
# target. As a result, the PCH will be named whatever.hpp.gch, and not
# whatever.gch.
rule generated-targets ( sources + : property-set : project name ? )
{
name = [ $(sources[1]).name ] ;
return [ generator.generated-targets $(sources)
: $(property-set) : $(project) $(name) ] ;
}
}

# Note: the 'H' source type will catch both '.h' header and '.hpp' header. The
Expand Down
12 changes: 6 additions & 6 deletions src/tools/msvc.jam
Original file line number Diff line number Diff line change
Expand Up @@ -735,14 +735,14 @@ toolset.flags msvc YLOPTION : "-Yl" ;
# for each source file by rule archive, as in this case compiler must be used
# to create a single PDB for our library.
#
actions compile-c-c++ bind PDB_NAME
actions compile-c-c++ bind PDB_NAME PCH_HEADER
{
$(.SETUP) $(.CC) @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>[1]:W)" -Fo"$(<[1]:W)" $(PDB_CFLAG)"$(PDB_NAME)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE)) $(.CC.FILTER)
$(.SETUP) $(.CC) @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>[1]:W)" -Fo"$(<[1]:W)" $(PDB_CFLAG)"$(PDB_NAME)" -FI"$(PCH_HEADER)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE)) $(.CC.FILTER)
}

actions preprocess-c-c++ bind PDB_NAME
actions preprocess-c-c++ bind PDB_NAME PCH_HEADER
{
$(.SETUP) $(.CC) @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>[1]:W)" -E $(PDB_CFLAG)"$(PDB_NAME)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE))" >"$(<[1]:W)"
$(.SETUP) $(.CC) @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>[1]:W)" -E $(PDB_CFLAG)"$(PDB_NAME)" -FI"$(PCH_HEADER)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE))" >"$(<[1]:W)"
}

rule compile-c-c++ ( targets + : sources * )
Expand Down Expand Up @@ -996,14 +996,14 @@ class msvc-pch-generator : pch-generator

# If we do not have the PCH source - that is fine. We will just create a
# temporary .cpp file in the action.

local pch-header-dir = [ $(pch-header).path ] ;
local generated = [ generator.run $(project) $(name)
: [ property-set.create
# Passing of <pch-source> is a dirty trick, needed because
# non-composing generators with multiple inputs are subtly
# broken. For more detailed information see:
# https://zigzag.cs.msu.su:7813/boost.build/ticket/111
<pch-source>$(pch-source)
<pch-source>$(pch-source) "<include>$(pch-header-dir)"
[ $(property-set).raw ] ]
: $(pch-header) ] ;

Expand Down
12 changes: 7 additions & 5 deletions test/BoostBuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,11 @@ def write(self, file, content, wait=True):
f.close()
self.__ensure_newer_than_last_build(nfile)

def rename(self, src, dst):
src_name = self.native_file_name(src)
dst_name = self.native_file_name(dst)
os.rename(src_name, dst_name)

def copy(self, src, dst):
try:
self.write(dst, self.read(src, binary=True))
Expand All @@ -364,15 +369,12 @@ def copy(self, src, dst):
def copy_timestamp(self, src, dst):
src_name = self.native_file_name(src)
dst_name = self.native_file_name(dst)
stats = os.stat(src_name)
os.utime(dst_name, (stats.st_atime, stats.st_mtime))
shutil.copystat(src_name, dst_name)

def copy_preserving_timestamp(self, src, dst):
src_name = self.native_file_name(src)
dst_name = self.native_file_name(dst)
stats = os.stat(src_name)
self.write(dst, self.__read(src, binary=True))
os.utime(dst_name, (stats.st_atime, stats.st_mtime))
shutil.copy2(src_name, dst_name)

def touch(self, names, wait=True):
if isstr(names):
Expand Down
29 changes: 14 additions & 15 deletions test/pch.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
t.write("jamroot.jam", """
import pch ;
project : requirements <warnings-as-errors>on ;
cpp-pch pch : pch.hpp : <toolset>msvc:<source>pch.cpp <include>. ;
cpp-pch pch-afx : pch.hpp : <define>HELLO <toolset>msvc:<source>pch.cpp <include>. ;
exe hello : hello.cpp pch : <include>. ;
exe hello-afx : hello-afx.cpp pch-afx : <define>HELLO <include>. ;
cpp-pch pch : pch.hpp ;
cpp-pch pch-afx : pch.hpp : <define>HELLO ;
cpp-pch pch-msvc-source : pch.hpp : <toolset>msvc:<source>pch.cpp ;
exe hello : hello.cpp pch ;
exe hello-afx : hello-afx.cpp pch-afx : <define>HELLO ;
exe hello-msvc-source : hello-msvc-source.cpp pch-msvc-source ;
""")

pch_content = """\
Expand All @@ -35,36 +37,33 @@ class TestClass
""")

toolset = BoostBuild.get_toolset()
# Clang with posix interface always include everything in source files
if not toolset.startswith('clang') or toolset.startswith('clang-win'):
include = '#include <pch.hpp>'
else:
include = ''
for name in ("hello.cpp", "hello-afx.cpp"):
t.write(name, include + """
int main() { TestClass c(1, 2); }
for name in ("hello.cpp", "hello-afx.cpp", "hello-msvc-source.cpp"):
t.write(name, """int main() { TestClass c(1, 2); }
""")

t.run_build_system()
t.expect_addition("bin/$toolset/debug*/hello.exe")
t.expect_addition("bin/$toolset/debug*/hello-afx.exe")
t.expect_addition("bin/$toolset/debug*/hello-msvc-source.exe")


# Now make the header unusable, replace its content with some garbage, but
# preserve the size and timestamp to fool the compiler. If everything is OK,
# B2 will not recreate PCH, and compiler will happily use pre-compiled
# header, not noticing that the real header is bad.

t.rename("pch.hpp", "pch.hpp.orig")
s = "THIS WILL NOT COMPILE. "
t.write("pch.hpp.bad", s + (len(pch_content) - len(s)) * 'x')
t.copy_timestamp("pch.hpp", "pch.hpp.bad")
t.copy_preserving_timestamp("pch.hpp.bad", "pch.hpp")
t.write("pch.hpp", s + (len(pch_content) - len(s)) * 'x')
t.copy_timestamp("pch.hpp.orig", "pch.hpp")

t.rm("bin/$toolset/debug*/hello.obj")
t.rm("bin/$toolset/debug*/hello-afx.obj")
t.rm("bin/$toolset/debug*/hello-msvc-source.obj")

t.run_build_system()
t.expect_addition("bin/$toolset/debug*/hello.obj")
t.expect_addition("bin/$toolset/debug*/hello-afx.obj")
t.expect_addition("bin/$toolset/debug*/hello-msvc-source.obj")

t.cleanup()

0 comments on commit 64dbb27

Please sign in to comment.