0

Vulkan SDK hosts a C++ header-only C-wrapped library. Problem is, it is really a heavy header, so it wastes a lot of compilation time.

So I looked into using a make file for my Vulkan project, which I never did before. I decided to programatically generate all the dependencies. Probably I messed up this part as well, making it too difficult to understand.

It does kind of work, probably there is an easier way to do the same thing(s). Here comes the part I can't really work out with:

  1. I generated all the .o (object) files for my project along with the .gch (precompiled headers)
  2. Then I did the same for the vulkan.hpp and the glfw3.h. (Remember, that's where most of my compilation time is going into).

... And here is where I have several problems about this procedure:

  • Can I, somehow, force the g++ compiler to use .gch files? The combination of -I, -include doesn't seem to work at all and I cannot even debug what is going on with -H/-M/-MM because i have no output whatsoever from this commands.

I am using MSYS2 MINGW_64 release, if it makes a difference. I'd be glad if someone could give me a tip or two about make files and, in particular, if there is something I completely misunderstood about the compilation process.

# Methods

define uniq =
  $(eval seen :=)
  $(foreach _,$1,$(if $(filter $_,${seen}),,$(eval seen += $_)))
  ${seen}
endef

# Compilation flags
_COMPILER := g++ -std=c++17
_FLAGS_WARNING := -Wall -Wextra -Wshadow
_FLAGS_COMPILE := -g -O0
_FLAGS_VULKAN := -lglfw3 -lvulkan

# Custom Flags
_FLAGS_DEBUG := -D _DEBUG -D _DEBUG_SEVERITY=0 -D _DEBUG_TYPE=0

# Output
_OUTPUT_NAME := test

# Directories
_TMP_DIR := _tmp
_O_DIR := ${_TMP_DIR}\.o
_GCH_DIR := ${_TMP_DIR}\.gch

_SOURCE_DIR := src
_BUILD_DIR := build
_DEBUG_DIR := ${_BUILD_DIR}\debug
_RELEASE_DIR := ${_BUILD_DIR}\release

# Files
_VULKAN_HPP := C:/VulkanSDK/1.2.148.1/Include/vulkan/vulkan.hpp
_GLFW_HPP := C:/msys64/mingw64/include/GLFW/glfw3.h

# Grouping Files
# .cpp & .o
_CPP_LIST := ${wildcard ${_SOURCE_DIR}/*.cpp ${_SOURCE_DIR}/*/*.cpp}
_CPP_LIST_NAME := ${notdir ${_CPP_LIST}}
_O_LIST := ${addprefix ${_O_DIR}/, ${patsubst %.cpp, %.o, ${_CPP_LIST_NAME}}}

# .hpp & .gch
_HPP_LIST := ${wildcard ${_SOURCE_DIR}/*.hpp ${_SOURCE_DIR}/*/*.hpp} ${_VULKAN_HPP}
_HPP_LIST_NAME := ${notdir ${_HPP_LIST}}
_GCH_LIST := ${addprefix ${_GCH_DIR}/, ${patsubst %.hpp, %.gch, ${_HPP_LIST_NAME}}}

# .h & .gch
_H_LIST := ${_GLFW_HPP}
_H_LIST_NAME := ${notdir ${_H_LIST}}
_C_GCH_LIST := ${addprefix ${_GCH_DIR}/, ${patsubst %.h, %.gch, ${_H_LIST_NAME}}}

_COMPILATION_BUNDLE_CPP = ${_FLAGS_WARNING} ${_FLAGS_COMPILE} ${_FLAGS_VULKAN} ${_FLAGS_DEBUG} -I ${_GCH_DIR} 
_COMPILATION_BUNDLE_HPP = ${_FLAGS_COMPILE} -x c++-header ${_FLAGS_DEBUG} -I ${_GCH_DIR}

${_DEBUG_DIR}/${_OUTPUT_NAME}: ${_GCH_LIST} ${_C_GCH_LIST} ${_O_LIST} 
    ${_COMPILER} ${_COMPILATION_BUNDLE_CPP} ${_O_LIST} ${addprefix -include , ${_GCH_LIST}} ${addprefix -include , ${_C_GCH_LIST}} -o $@ 

vpath %.cpp $(call uniq, ${dir ${_CPP_LIST}})
${_O_DIR}/%.o: %.cpp 
    ${_COMPILER} ${_COMPILATION_BUNDLE_CPP} -c $< -o $@ 

vpath %.hpp $(call uniq, ${dir ${_HPP_LIST}})
${_GCH_DIR}/%.gch: %.hpp 
    ${_COMPILER} ${_COMPILATION_BUNDLE_HPP} $< -o $@

vpath %.h $(call uniq, ${dir ${_H_LIST}})
${_GCH_DIR}/%.gch: %.h
    ${_COMPILER} ${_COMPILATION_BUNDLE_HPP} $< -o $@

The execution on a clean project (thus easier to follow along):

g++ -std=c++17 -g -O0 -x c++-header -D _DEBUG -D _DEBUG_SEVERITY=0 -D _DEBUG_TYPE=0 -I _tmp\.gch src/helper/helper.hpp -o _tmp\.gch/helper.gch
g++ -std=c++17 -g -O0 -x c++-header -D _DEBUG -D _DEBUG_SEVERITY=0 -D _DEBUG_TYPE=0 -I _tmp\.gch src/renderer/renderer.hpp -o _tmp\.gch/renderer.gch
g++ -std=c++17 -g -O0 -x c++-header -D _DEBUG -D _DEBUG_SEVERITY=0 -D _DEBUG_TYPE=0 -I _tmp\.gch C:/VulkanSDK/1.2.148.1/Include/vulkan/vulkan.hpp -o _tmp\.gch/vulkan.gch
g++ -std=c++17 -g -O0 -x c++-header -D _DEBUG -D _DEBUG_SEVERITY=0 -D _DEBUG_TYPE=0 -I _tmp\.gch C:/msys64/mingw64/include/GLFW/glfw3.h -o _tmp\.gch/glfw3.gch 
g++ -std=c++17 -Wall -Wextra -Wshadow -g -O0 -lglfw3 -lvulkan -D _DEBUG -D _DEBUG_SEVERITY=0 -D _DEBUG_TYPE=0 -I _tmp\.gch  -c src/main.cpp -o _tmp\.o/main.o  
g++ -std=c++17 -Wall -Wextra -Wshadow -g -O0 -lglfw3 -lvulkan -D _DEBUG -D _DEBUG_SEVERITY=0 -D _DEBUG_TYPE=0 -I _tmp\.gch  -c src/helper/helper.cpp -o _tmp\.o/helper.o
g++ -std=c++17 -Wall -Wextra -Wshadow -g -O0 -lglfw3 -lvulkan -D _DEBUG -D _DEBUG_SEVERITY=0 -D _DEBUG_TYPE=0 -I _tmp\.gch  -c src/renderer/renderer.cpp -o _tmp\.o/renderer.o
g++ -std=c++17 -Wall -Wextra -Wshadow -g -O0 -lglfw3 -lvulkan -D _DEBUG -D _DEBUG_SEVERITY=0 -D _DEBUG_TYPE=0 -I _tmp\.gch  _tmp\.o/main.o _tmp\.o/helper.o _tmp\.o/renderer.o -include _tmp\.gch/helper.gch -include _tmp\.gch/renderer.gch -include _tmp\.gch/vulkan.gch -include _tmp\.gch/glfw3.gch -o build\debug/test
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
DrKGD
  • 23
  • 4

1 Answers1

1

Using pre-compiled headers is actually fairly straight forward. It might be easiest to break it down into manual steps. You can even retro fit to an existing project pretty easily. The steps could be:

  • Create a PCH file including a bunch of std libs or other headers you want to pre-compile. Note: Compile your PCH with the same flags as your other c++ code is compiled with, otherwise it may not work.
  • Add the include flags for the PCH in your makefile... and that is basically it.

So lets try a practical example:

  1. Start with a source file, src1.cpp and compile it: g++ -I. <other flags> src1.cpp -o src1.o
  2. Create your pch file, call it pch.hpp (or whatever)
  3. Compile your PCH file: g++ -I. <other flags> pch.hpp (same flags as the previous compile). This generates the pch.hpp.gch file.
  4. Now you can use your PCH file in your original compile line: g++ -I. <other flags> src1.cpp -o src1.o -Winvalid-pch -include pch.hpp

Couple of things to note:

  • If you use -Winvalid-pch warning flag this usually gives you enough information to figure out why the pch is not used (like its missing, bad path, bad options etc...).
  • Use -include pch.hpp not -include pch.hpp.gch since g++ will figure that out. You can see that retro fitting it is easy - since you just append the include flags to your compiler lines.
  • If your .gch file is not generated then the code should still compile using the pch.hpp header directly...
code_fodder
  • 15,263
  • 17
  • 90
  • 167
  • So you're suggesting me to write all of them by hand in a .hpp file, then compile this file to its precompiled version, then `-include` during the linkage compilation process? – DrKGD Aug 31 '20 at 13:38
  • 1
    @DrKGD no, not at all. But just to debug in this way. Get it all working fully in a simpler/smaller (perhaps more manual) way first, and then go back and fix your makefile. I am just suggesting you *can* do it this way - you don't even need to change your existing source files. If file `src1.cpp` includes for example, and your PCH also includes and it is precompiled, then when you add the pch.hpp include flags into your gcc/g++ compile command the precompiled version will be used. – code_fodder Aug 31 '20 at 14:28
  • 1
    I have run lots of tests on this to make sure it works - my build times for some example code was something like 4x faster. Though they where simple example source files... – code_fodder Aug 31 '20 at 14:28
  • I will try for a while and I will let you know, thanks for the advices! – DrKGD Aug 31 '20 at 16:17