ฉันเคยเห็นโพสต์ (เก่า ๆ ) สองสามโพสต์ใน 'net เกี่ยวกับการแฮ็กด้วยกันการสนับสนุนบางส่วนสำหรับส่วนหัวที่รวบรวมไว้ล่วงหน้าใน CMake พวกเขาทั้งหมดดูเหมือนจะทั่วสถานที่และทุกคนมีวิธีการของตัวเอง วิธีที่ดีที่สุดในการทำปัจจุบันคืออะไร?
ฉันเคยเห็นโพสต์ (เก่า ๆ ) สองสามโพสต์ใน 'net เกี่ยวกับการแฮ็กด้วยกันการสนับสนุนบางส่วนสำหรับส่วนหัวที่รวบรวมไว้ล่วงหน้าใน CMake พวกเขาทั้งหมดดูเหมือนจะทั่วสถานที่และทุกคนมีวิธีการของตัวเอง วิธีที่ดีที่สุดในการทำปัจจุบันคืออะไร?
คำตอบ:
มีโมดูล CMake ของบุคคลที่สามชื่อ 'Cotire'ซึ่งทำให้การใช้ส่วนหัวที่คอมไพล์ไว้ล่วงหน้าสำหรับระบบการสร้างที่ใช้ CMake โดยอัตโนมัติและยังสนับสนุนการสร้างเอกภาพ
ฉันใช้มาโครต่อไปนี้เพื่อสร้างและใช้ส่วนหัวที่คอมไพล์ไว้ล่วงหน้า:
MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecompiledHeader PrecompiledSource SourcesVar)
IF(MSVC)
GET_FILENAME_COMPONENT(PrecompiledBasename ${PrecompiledHeader} NAME_WE)
SET(PrecompiledBinary "${CMAKE_CURRENT_BINARY_DIR}/${PrecompiledBasename}.pch")
SET(Sources ${${SourcesVar}})
SET_SOURCE_FILES_PROPERTIES(${PrecompiledSource}
PROPERTIES COMPILE_FLAGS "/Yc\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_OUTPUTS "${PrecompiledBinary}")
SET_SOURCE_FILES_PROPERTIES(${Sources}
PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledHeader}\" /FI\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_DEPENDS "${PrecompiledBinary}")
# Add precompiled header to SourcesVar
LIST(APPEND ${SourcesVar} ${PrecompiledSource})
ENDIF(MSVC)
ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER)
สมมติว่าคุณมีตัวแปร $ {MySources} พร้อมไฟล์ต้นฉบับทั้งหมดของคุณรหัสที่คุณต้องการใช้จะเป็นเพียง
ADD_MSVC_PRECOMPILED_HEADER("precompiled.h" "precompiled.cpp" MySources)
ADD_LIBRARY(MyLibrary ${MySources})
รหัสจะยังคงทำงานได้ดีบนแพลตฟอร์มที่ไม่ใช่ MSVC ด้วย สวยเนี๊ยบ :)
list( APPEND ... )
ส่วนปิดออกendif()
ออก ดูรหัสทั้งหมดที่นี่: pastebin.com/84dm5rXZ
/Yu
และ/FI
ข้อโต้แย้งที่พวกเขาควรจะได้และไม่${PrecompiledHeader}
${PrecompiledBinary}
/YuC:/foo/bar.h
จะบังคับให้คุณส่ง/FpC:/foo/bar.h
แฟล็กหรือวางไว้#include <C:/foo/bar.h>
ที่ด้านบนของไฟล์. cpp ทั้งหมดของคุณเป็นคำสั่ง include แรก MSVC ทำการเปรียบเทียบสตริงของ#include
อาร์กิวเมนต์โดยไม่ได้ตรวจสอบว่าชี้ไปที่ไฟล์เดียวกันกับสิ่งที่กำหนดให้/Yu
หรือไม่ Ergo #include <bar.h>
จะไม่ทำงานและแสดงข้อผิดพลาด C2857
CMake เพิ่งได้รับการสนับสนุนสำหรับ PCH ซึ่งควรจะพร้อมใช้งานในรุ่น 3.16 ที่กำลังจะมาถึงในวันที่ 2019-10-01:
https://gitlab.kitware.com/cmake/cmake/merge_requests/3553
target_precompile_headers(<target>
<INTERFACE|PUBLIC|PRIVATE> [header1...]
[<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])
มีการอภิปรายอย่างต่อเนื่องเกี่ยวกับการสนับสนุนการแบ่งปัน PCH ระหว่างเป้าหมาย: https://gitlab.kitware.com/cmake/cmake/issues/19659
มีบริบทเพิ่มเติม (แรงจูงใจตัวเลข) อยู่ที่ https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/
นี่คือข้อมูลโค้ดเพื่อให้คุณใช้ส่วนหัวที่คอมไพล์ไว้ล่วงหน้าสำหรับโปรเจ็กต์ของคุณ เพิ่มสิ่งต่อไปนี้ในการแทนที่ CMakeLists.txt ของคุณmyprecompiledheaders
และmyproject_SOURCE_FILES
ตามความเหมาะสม:
if (MSVC)
set_source_files_properties(myprecompiledheaders.cpp
PROPERTIES
COMPILE_FLAGS "/Ycmyprecompiledheaders.h"
)
foreach( src_file ${myproject_SOURCE_FILES} )
set_source_files_properties(
${src_file}
PROPERTIES
COMPILE_FLAGS "/Yumyprecompiledheaders.h"
)
endforeach( src_file ${myproject_SOURCE_FILES} )
list(APPEND myproject_SOURCE_FILES myprecompiledheaders.cpp)
endif (MSVC)
with set( CMAKE_AUTOMOC ON )
ได้
myprecompiledheader.cpp
คอมไพล์ก่อนหรือไม่? จากตัวอย่างข้อมูลนี้ดูเหมือนว่าจะถูกรวบรวมเป็นครั้งสุดท้ายดังนั้นบางทีนั่นอาจเป็นสาเหตุของความล่าช้า myprecompiledheader.h
มีเฉพาะส่วนหัว STL ทั่วไปที่รหัสของฉันใช้
ฉันลงเอยด้วยการใช้มาโคร larsm เวอร์ชันดัดแปลง การใช้ $ (IntDir) สำหรับพา ธ pch จะเก็บส่วนหัวที่คอมไพล์ไว้ล่วงหน้าสำหรับ debug และ release build แยกกัน
MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecompiledHeader PrecompiledSource SourcesVar)
IF(MSVC)
GET_FILENAME_COMPONENT(PrecompiledBasename ${PrecompiledHeader} NAME_WE)
SET(PrecompiledBinary "$(IntDir)/${PrecompiledBasename}.pch")
SET(Sources ${${SourcesVar}})
SET_SOURCE_FILES_PROPERTIES(${PrecompiledSource}
PROPERTIES COMPILE_FLAGS "/Yc\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_OUTPUTS "${PrecompiledBinary}")
SET_SOURCE_FILES_PROPERTIES(${Sources}
PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledHeader}\" /FI\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_DEPENDS "${PrecompiledBinary}")
# Add precompiled header to SourcesVar
LIST(APPEND ${SourcesVar} ${PrecompiledSource})
ENDIF(MSVC)
ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER)
ADD_MSVC_PRECOMPILED_HEADER("stdafx.h" "stdafx.cpp" MY_SRCS)
ADD_EXECUTABLE(MyApp ${MY_SRCS})
ดัดแปลงมาจาก Dave แต่มีประสิทธิภาพมากกว่า (ตั้งค่าคุณสมบัติเป้าหมายไม่ใช่สำหรับแต่ละไฟล์):
if (MSVC)
set_target_properties(abc PROPERTIES COMPILE_FLAGS "/Yustd.h")
set_source_files_properties(std.cpp PROPERTIES COMPILE_FLAGS "/Ycstd.h")
endif(MSVC)
abc
ในตัวอย่างของคุณหรือไม่
ถ้าคุณไม่อยากบูรณาการล้อเพียงใช้อย่างใดอย่างหนึ่ง Cotire เป็นคำตอบด้านบนแสดงให้เห็นหรือง่ายหนึ่ง - CMake-precompiled ส่วนหัว ที่นี่ หากต้องการใช้เพียงแค่รวมโมดูลและโทร:
include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
CMake 3.16 แนะนำการสนับสนุนสำหรับส่วนหัวที่คอมไพล์ไว้ล่วงหน้า มีคำสั่ง CMake ใหม่target_precompile_headers
ที่ทำทุกสิ่งที่คุณต้องการภายใต้ประทุน ดูเอกสารประกอบสำหรับรายละเอียดเพิ่มเติม: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html
"stdafx.h", "stdafx.cpp" - ชื่อส่วนหัวที่คอมไพล์ไว้ล่วงหน้า
ใส่สิ่งต่อไปนี้ด้านล่างในไฟล์ root cmake
if (MSVC)
# For precompiled header.
# Set
# "Precompiled Header" to "Use (/Yu)"
# "Precompiled Header File" to "stdafx.h"
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Yustdafx.h /FIstdafx.h")
endif()
ใส่สิ่งต่อไปนี้ด้านล่างในไฟล์โครงการ cmake
"src" - โฟลเดอร์ที่มีไฟล์ต้นฉบับ
set_source_files_properties(src/stdafx.cpp
PROPERTIES
COMPILE_FLAGS "/Ycstdafx.h"
)
IMHO วิธีที่ดีที่สุดคือการตั้งค่า PCH สำหรับทั้งโครงการตามที่ martjno แนะนำรวมกับความสามารถในการละเว้น PCH สำหรับแหล่งข้อมูลบางแหล่งหากจำเป็น (เช่นแหล่งที่มาที่สร้างขึ้น):
# set PCH for VS project
function(SET_TARGET_PRECOMPILED_HEADER Target PrecompiledHeader PrecompiledSource)
if(MSVC)
SET_TARGET_PROPERTIES(${Target} PROPERTIES COMPILE_FLAGS "/Yu${PrecompiledHeader}")
set_source_files_properties(${PrecompiledSource} PROPERTIES COMPILE_FLAGS "/Yc${PrecompiledHeader}")
endif(MSVC)
endfunction(SET_TARGET_PRECOMPILED_HEADER)
# ignore PCH for a specified list of files
function(IGNORE_PRECOMPILED_HEADER SourcesVar)
if(MSVC)
set_source_files_properties(${${SourcesVar}} PROPERTIES COMPILE_FLAGS "/Y-")
endif(MSVC)
endfunction(IGNORE_PRECOMPILED_HEADER)
ดังนั้นหากคุณมี MY_TARGET เป้าหมายและรายชื่อแหล่งที่มาที่สร้างขึ้น IGNORE_PCH_SRC_LIST คุณจะทำดังนี้
SET_TARGET_PRECOMPILED_HEADER(MY_TARGET stdafx.h stdafx.cpp)
IGNORE_PRECOMPILED_HEADER(IGNORE_PCH_SRC_LIST)
aproach นี้ผ่านการทดสอบและทำงานได้อย่างสมบูรณ์
เมื่อสร้างใช้เวลา 10+ นาทีบนเครื่องควอดคอร์ทุกครั้งที่คุณเปลี่ยนบรรทัดเดียวในไฟล์โปรเจ็กต์ใด ๆ มันจะบอกเวลาในการเพิ่มส่วนหัวที่คอมไพล์ล่วงหน้าสำหรับ windows ใน * nux ฉันจะใช้ ccache และไม่ต้องกังวลกับเรื่องนั้น
ฉันได้ติดตั้งในแอปพลิเคชันหลักและไลบรารีบางส่วนที่ใช้ มันใช้งานได้ดีในจุดนี้ สิ่งหนึ่งที่จำเป็นเช่นกันคือคุณต้องสร้างไฟล์ซอร์สและส่วนหัว pch และในไฟล์ต้นฉบับรวมส่วนหัวทั้งหมดที่คุณต้องการคอมไพล์ไว้ล่วงหน้า ฉันทำสิ่งนี้มา 12 ปีกับ MFC แต่ฉันใช้เวลาไม่กี่นาทีในการจำได้ว่า ..
วิธีที่สะอาดที่สุดคือเพิ่มตัวเลือกที่คอมไพล์ไว้ล่วงหน้าเป็นตัวเลือกส่วนกลาง ในไฟล์ vcxproj สิ่งนี้จะแสดงเป็นไฟล์<PrecompiledHeader>Use</PrecompiledHeader>
และไม่ทำกับทุกไฟล์
จากนั้นคุณต้องเพิ่มCreate
ตัวเลือกให้กับ StdAfx.cpp ต่อไปนี้เป็นวิธีที่ฉันใช้:
MACRO(ADD_MSVC_PRECOMPILED_HEADER SourcesVar)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /YuStdAfx.h")
set_source_files_properties(StdAfx.cpp
PROPERTIES
COMPILE_FLAGS "/YcStdAfx.h"
)
list(APPEND ${${SourcesVar}} StdAfx.cpp)
ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER)
file(GLOB_RECURSE MYDLL_SRC
"*.h"
"*.cpp"
"*.rc")
ADD_MSVC_PRECOMPILED_HEADER(MYDLL_SRC)
add_library(MyDll SHARED ${MYDLL_SRC})
สิ่งนี้ได้รับการทดสอบและใช้งานได้กับ MSVC 2010 และจะสร้างไฟล์ MyDll.pch ฉันไม่ได้ใส่ใจว่าใช้ชื่อไฟล์อะไรดังนั้นฉันจึงไม่ได้พยายามระบุใด ๆ
เนื่องจากตัวเลือกส่วนหัวที่คอมไพล์ไว้ล่วงหน้าไม่สามารถใช้งานได้กับไฟล์ rc ฉันจึงจำเป็นต้องปรับมาโครที่จัดทำโดย jari
#######################################################################
# Makro for precompiled header
#######################################################################
MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecompiledHeader PrecompiledSource SourcesVar)
IF(MSVC)
GET_FILENAME_COMPONENT(PrecompiledBasename ${PrecompiledHeader} NAME_WE)
SET(PrecompiledBinary "$(IntDir)/${PrecompiledBasename}.pch")
SET(Sources ${${SourcesVar}})
# generate the precompiled header
SET_SOURCE_FILES_PROPERTIES(${PrecompiledSource}
PROPERTIES COMPILE_FLAGS "/Zm500 /Yc\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_OUTPUTS "${PrecompiledBinary}")
# set the usage of this header only to the other files than rc
FOREACH(fname ${Sources})
IF ( NOT ${fname} MATCHES ".*rc$" )
SET_SOURCE_FILES_PROPERTIES(${fname}
PROPERTIES COMPILE_FLAGS "/Zm500 /Yu\"${PrecompiledHeader}\" /FI\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_DEPENDS "${PrecompiledBinary}")
ENDIF( NOT ${fname} MATCHES ".*rc$" )
ENDFOREACH(fname)
# Add precompiled header to SourcesVar
LIST(APPEND ${SourcesVar} ${PrecompiledSource})
ENDIF(MSVC)
ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER)
แก้ไข: การใช้ส่วนหัวที่คอมไพล์ล่วงหน้านี้ช่วยลดเวลาในการสร้างโดยรวมของโปรเจ็กต์หลักของฉันจาก 4 นาที 30 วินาทีเหลือ 1 นาที 40 วินาที นี่เป็นสิ่งที่ดีสำหรับฉันจริงๆ ในส่วนหัวพรีคอมไพล์มีเฉพาะส่วนหัวเช่น boost / stl / Windows / mfc
อย่าไปที่นั่นด้วยซ้ำ ส่วนหัวที่คอมไพล์ไว้ล่วงหน้าหมายความว่าเมื่อใดก็ตามที่มีการเปลี่ยนแปลงส่วนหัวคุณจะต้องสร้างใหม่ทั้งหมดทุกอย่างคุณโชคดีถ้าคุณมีระบบสร้างที่ตระหนักถึงสิ่งนี้ บ่อยครั้งที่การสร้างของคุณจะล้มเหลวจนกว่าคุณจะรู้ว่าคุณได้เปลี่ยนแปลงบางสิ่งที่กำลังถูกคอมไพล์ไว้ล่วงหน้าดังนั้นคุณต้องทำการสร้างใหม่ทั้งหมด คุณสามารถหลีกเลี่ยงสิ่งนี้ได้เป็นส่วนใหญ่โดยการรวมส่วนหัวไว้ล่วงหน้าว่าคุณมีค่าบวกอย่างแน่นอนจะไม่เปลี่ยนแปลง แต่คุณก็ยอมแพ้ส่วนใหญ่ของการเพิ่มความเร็วเช่นกัน
ปัญหาอื่น ๆ คือเนมสเปซของคุณเต็มไปด้วยสัญลักษณ์ทุกชนิดที่คุณไม่รู้จักหรือสนใจในหลาย ๆ ที่ที่คุณใช้ส่วนหัวที่คอมไพล์ไว้ล่วงหน้า