Skip to content

Commit 4c8d812

Browse files
committed
MOC_LVL_0, arguments cleanup, out of source builds, etc.
Added support for MOC_LVL_0 Added support for basic/shadow builds (and modified out of source builds) Modified default path of UNITY_TMP_DIR, it is now in the debug or shadow_build_debug. That being said, in regular (non shadow) mode, this modifies QtCreator display behaviour (2 entries are displayed, one for debug and one for release) Improved cli arguments parsing Added strategy configuration (UNITY_STRATEGY) Removed one process step from qmake_unity.pri (see build_pass). Pragma once were added in test projects to test MOC_LVL_2) (using guard2once)
1 parent b69d42e commit 4c8d812

File tree

445 files changed

+573
-1978
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

445 files changed

+573
-1978
lines changed

.gitignore

+8-3
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333

3434
# Qt
3535
*_plugin_import.cpp
36-
/.qmake.cache
37-
/.qmake.stash
36+
.qmake.cache
37+
.qmake.stash
3838
*.pro.user
3939
*.pro.user.*
4040
*.qbs.user
@@ -48,6 +48,10 @@ ui_*.h
4848
*.jsc
4949
Makefile*
5050
*build-*
51+
debug/
52+
release/
53+
object_script.*.Debug
54+
object_script.*.Release
5155

5256
# Python
5357
__pycache__/
@@ -56,4 +60,5 @@ __pycache__/
5660

5761
# Perso
5862

59-
perso.txt
63+
perso.txt
64+
tmp/

README.md

+19-8
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,16 @@ include(path/to/qmakeUnity/qmake_unity.pri)
4141
You might want to adjust the following variables in your .pro file before including qmake_unity.pri :
4242

4343
- `UNITY_TMP_DIR`
44-
Choose a path where cpp/moc/obj files generated by qmake-unity will be stored
45-
Default value : `qmake-unity.pri's folder/../tmp/Build_Qt/unity`
44+
Choose a path where temporary cpp/h/pri files generated by qmake-unity will be stored.
45+
Default value : `BuildPwd/unity/`
46+
47+
Please read `qmake_unity.pri` if you want to put move generated/object files out of source.
48+
49+
- `UNITY_STRATEGY`
50+
There are 3 possible strategies to group cpp files together :
51+
- `incremental` is the default mode. The group size depends on the project's number of cpp files. This option is recommended for big projects.
52+
- `per-processor` generates one CPP per processor. You can set your number of processors in `unity_config.py`.
53+
- `single-compilation-unit` : generated a single CPP file for your entire project.
4654

4755
- `UNITY_MOC_MODE`
4856
`MOC_LVL_1` : group the compilation of moc_file1.cpp and moc_file2.cpp
@@ -109,7 +117,7 @@ b.cpp
109117
QString status(const Screen &screen, const Display &display) { return display.status(screen); }
110118
```
111119

112-
**MACRO with side effects**
120+
**One macro has a side effect that triggers compile errors**
113121
```
114122
a.h
115123
#define Status(XXX, YYY) XXX+YYY-2
@@ -118,16 +126,18 @@ b.cpp (doesn't include a.h)
118126
void displayStatus(const Screen &screen, const Display &display) {}
119127
```
120128
a.cpp may be grouped with b.cpp though they are not related. The macro `Status` has a very short and common name that will match `displayStatus` and replace it with something irrelevant.
129+
Please choose a longer and unique macro name
121130

122131
**Header files that must be included in a certain order ; or that may cause conflicts.**
123132

124133
That is not a good practice, but sometimes things are like that.
125134
For example, some windows SDK headers must be included in a certain order.
126135
In that case, you might remove the header from unity build by adding `//@NO_UNITY` to the including header.
127136

128-
**There is an error related with unity builds, but I don't know which file causes it**
137+
**There is an error related with unity builds, but I don't know which file causes it.**
138+
129139
If you don't know which files causes an error, you can either :
130-
- Edit the `tmp/unity/XXX/unity_[0123].cpp` files and comment entries until the error disappears. Then you'll know which file raises the problem.
140+
- Edit the `tmp/unity/unity_[0123].cpp` files and comment entries until the error disappears. Then you'll know which file raises the problem.
131141
- Edit your .pro file to include qmake_unity.pro when the `SOURCES` and `HEADERS` variables are just partially filled, then run qmake.
132142

133143
**There is an error on my build server/my colleague's workstation but it builds fine on my computer. How can I fix it ?**
@@ -136,7 +146,7 @@ The sources files are probably not be grouped identically on your computer and t
136146
For example, it happens when you add and remove files from a project.
137147
Procedure to reproduce :
138148
- build the project locally
139-
- copy and paste the contents of your colleague's `tmp/unity/XXX` directory in you own tmp directory
149+
- copy and paste the contents of your colleague's `tmp/unity/` directory in you own tmp directory
140150
- build the project without calling qmake (make only) ; the same error should happen.
141151

142152
**The `__FILE__` macro doesn't return the filename anymore, it return a relative path !**
@@ -163,13 +173,14 @@ QString unityExtractFileNameFromPath(const char * iFilePath)
163173
* `MOC_LVL_2` works only with pragma once; there is no workaround for now
164174
* Visual studio IDE is not supported : the source files are be displayed twice.
165175
Either build in QtCreator or from command line.
176+
* When running qmake, qmake-unity logs appear twice. This is because qmake processes every pro file 3 times, one for Makefile, one for Makefile.debug and another one for Makefile.release
166177

167178
## What else can I do to speedup my build
168179

169180
**Pure C++ techniques :**
170181
- Forward declare headers (incremental build)
171182
- Write less templates
172-
- Use precompiled headers
183+
- Use precompiled headers (PCH)
173184

174185
**Qt :**
175186
- If you build in VisualStudio, install Qt Visual Studio Tools <= 2.2; which now supports parallel calls of moc.
@@ -187,7 +198,7 @@ Either build in QtCreator or from command line.
187198

188199
**Use better tools :**
189200
- Zapcc is a fast alternative to Clang
190-
- gold linker replaces ld (linux)
201+
- Gold linker replaces ld (linux)
191202
- Replace Make with Ninja (best combo with CMake)
192203
- Add COTIRE to CMake to handle unity builds and precompiled headers
193204
- Install Incredybuild on Windows

qmakeUnity/qmake_unity.pri

+50-20
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,57 @@
1-
UNITY_SCRIPTS_DIR = $$clean_path($${PWD})
1+
!equals(UNITY_BUILD_DISABLE, "1") {
22

3-
!defined(PROJECTS_ROOT, var) {
4-
PROJECTS_ROOT=$$clean_path($${PWD}/../)
5-
}
6-
!defined(UNITY_TMP_DIR, var) {
7-
UNITY_TMP_DIR=$$clean_path($$PROJECTS_ROOT/tmp/Build_Qt/unity/)
8-
}
9-
UNITY_TMP_DIR_SUFFIX = $$relative_path($$_PRO_FILE_PWD_, $$PROJECTS_ROOT)
10-
UNITY_DIR = $$UNITY_TMP_DIR/$$UNITY_TMP_DIR_SUFFIX/
3+
build_pass { # do this action once for debug and once for release
114

5+
UNITY_SCRIPTS_DIR = $$clean_path($${PWD})
126

13-
!defined(UNITY_MOC_MODE, var) {
14-
UNITY_MOC_MODE = MOC_LVL_2
15-
}
7+
!defined(UNITY_DIR, var) {
8+
# This config makes QtCreator display both release and debug unity directory in "project view" > "source tree"
9+
# when shadow build are disabled.
10+
# If this annoys you, you can enable out of source builds, please see bellow)
11+
CONFIG(debug, debug|release) {
12+
UNITY_DIR = $$OUT_PWD/debug/unity/
13+
} else {
14+
UNITY_DIR = $$OUT_PWD/release/unity/
15+
}
16+
}
17+
18+
# If you have many projects, you might want to put unity and other tmp files outside of the hierarchy. (out of source builds)
19+
# To do this, uncomment the following lines
20+
#
21+
# PROJECTS_ROOT is the path to the "src" directory
22+
# PROJECTS_ROOT = $$clean_path($${PWD}/../)
23+
# PROJECT_NAME = $$relative_path($$_PRO_FILE_PWD_, $$PROJECTS_ROOT)
24+
# OUTPUT_BASE_PATH = $$PROJECTS_ROOT/tmp/Build_Qt/$$PROJECT_NAME
25+
#
26+
# CONFIG( debug, debug|release ) {
27+
# OUTPUT_BASE_PATH = $$OUTPUT_BASE_PATH/debug
28+
# } else {
29+
# OUTPUT_BASE_PATH = $$OUTPUT_BASE_PATH/release
30+
# }
31+
# UNITY_TMP_DIR = $$OUTPUT_BASE_PATH/unity/
32+
#
33+
# To also put the object files outside, uncomment the following lines
34+
#
35+
# OBJECTS_DIR = $$OUTPUT_BASE_PATH/obj/
36+
# MOC_DIR = $$OUTPUT_BASE_PATH/moc/
37+
# UI_DIR = $$OUTPUT_BASE_PATH/ui/
38+
# RCC_DIR = $$OUTPUT_BASE_PATH/rcc/
39+
# PRECOMPILED_DIR = $$OUTPUT_BASE_PATH/pch/
40+
41+
!defined(UNITY_MOC_MODE, var) {
42+
UNITY_MOC_MODE = MOC_LVL_2
43+
}
44+
45+
!defined(UNITY_STRATEGY, var) {
46+
UNITY_STRATEGY = incremental
47+
}
1648

17-
#!build_pass { # do this action only once if config = debug + release
18-
!equals(UNITY_BUILD_DISABLE, "1") {
1949
defined(UNITY_BUILD, var) {
2050
!count(SOURCES, 1) {
2151
mkpath($$UNITY_DIR)
2252
UNITY_SOURCES_FILE_LOCALVAR = $${UNITY_DIR}unitySources.txt
2353
write_file($$UNITY_SOURCES_FILE_LOCALVAR, SOURCES)
24-
system(cd $$_PRO_FILE_PWD_ & python $$UNITY_SCRIPTS_DIR/qmake_unity.py update incremental "$$UNITY_DIR" "$$UNITY_SOURCES_FILE_LOCALVAR")
54+
system(cd $$_PRO_FILE_PWD_ & python $$UNITY_SCRIPTS_DIR/qmake_unity.py --mode update --strategy $$UNITY_STRATEGY --mocMode $$UNITY_MOC_MODE --tmpDir "$$UNITY_DIR" --sourceListPath "$$UNITY_SOURCES_FILE_LOCALVAR")
2555
UNITY_GENERATED_PRI_LINES = $$cat($${UNITY_DIR}unity.pri, lines)
2656
for(UNITY_GENERATED_PRI_LINE, UNITY_GENERATED_PRI_LINES) {
2757
eval($$UNITY_GENERATED_PRI_LINE)
@@ -33,7 +63,7 @@ UNITY_DIR = $$UNITY_TMP_DIR/$$UNITY_TMP_DIR_SUFFIX/
3363
mkpath($$UNITY_DIR)
3464
UNITY_HEADERS_FILE_LOCALVAR = $${UNITY_DIR}unityHeaders.txt
3565
write_file($$UNITY_HEADERS_FILE_LOCALVAR, HEADERS)
36-
system(cd $$_PRO_FILE_PWD_ & python $$UNITY_SCRIPTS_DIR/unity_moc_headers.py generate_groups "$$UNITY_HEADERS_FILE_LOCALVAR")
66+
system(cd $$_PRO_FILE_PWD_ & python $$UNITY_SCRIPTS_DIR/unity_moc_headers.py --mode generate_groups --headerListPath "$$UNITY_HEADERS_FILE_LOCALVAR")
3767
UNITY_GENERATED_PRI_LINES = $$cat($${UNITY_DIR}unity_headers.pri, lines)
3868
for(UNITY_GENERATED_PRI_LINE, UNITY_GENERATED_PRI_LINES) {
3969
eval($$UNITY_GENERATED_PRI_LINE)
@@ -42,7 +72,7 @@ UNITY_DIR = $$UNITY_TMP_DIR/$$UNITY_TMP_DIR_SUFFIX/
4272
}
4373
}
4474
}
45-
else {
46-
!build_pass:message("UNITY_BUILD_DISABLE is set.")
47-
}
48-
#}
75+
}
76+
else {
77+
!build_pass:message("UNITY_BUILD_DISABLE is set.")
78+
}

qmakeUnity/qmake_unity.py

+36-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python3
22

3+
import argparse
34
import glob
45
import os
56
import re
@@ -61,7 +62,7 @@ def writeToDisk(self, type, unityDirectory):
6162
fileContent += "\n".join(fileIncludes)
6263
fileContent += "\n"
6364

64-
# on n'écrit que si le fichier a été modifié
65+
# we write only if the content has been modified
6566
fileOldContent = ''
6667
try:
6768
with open(self.file, encoding="utf-8") as file:
@@ -147,7 +148,7 @@ def addNewSourceFiles(groupList, sourceList, type, desiredGroupsSize):
147148
if j % desiredGroupsSize == 0:
148149
g = Group()
149150
g.type = type
150-
# crée une copie de la liste
151+
# creates a copy of the list
151152
g.sources = list(sources)
152153
groupList.append(g)
153154
sources.clear()
@@ -254,6 +255,7 @@ def writePriFile(groupList, unityPriFile, type, priWriteMode):
254255
groupFiles = [group.file for group in groupList]
255256
print("SOURCES += " + " ".join(groupFiles), file=file)
256257

258+
# To remove a cpp file from qmake, you must type the exact same path as qmake passed it to this script through unitySources.txt
257259
if type == 'cpp':
258260
sourceFiles = [source.pathFromProject for group in groupList for source in group.sources]
259261
print("SOURCES -= " + " ".join(sourceFiles), file=file)
@@ -343,9 +345,9 @@ def removeIncompatibleSourcesFromList(cppList):
343345

344346

345347
def argumentsCheck(mode, unityDirectory, strategy, cppList):
346-
assert mode in ["update","clear","removeOldSources","qmakeListRemovedSources","qmakeListAddedSources"], "Incorrect mode : " + mode
348+
assert mode in ["update","clear"], "Incorrect mode : " + mode
347349
if (mode != "clear"):
348-
assert strategy in ["incremental","per-processor","single"], "Stratégie incorrecte : " + strategy
350+
assert strategy in ["incremental","per-processor","single-compilation-unit"], "Unknown strategy : " + strategy
349351
assert "unity" in unityDirectory, "The folder " + unityDirectory + " must contain the keyword 'unity'"
350352
assert os.path.exists(unityDirectory), "The folder " + unityDirectory + " doesn't exists. This error is probably the consequence of another error"
351353
cd = os.getcwd()
@@ -354,27 +356,32 @@ def argumentsCheck(mode, unityDirectory, strategy, cppList):
354356
for cpp in cppList:
355357
assert cpp.pathFromProject.endswith(".cpp") or cpp.pathFromProject.endswith('.c'), "Error : this file is not a CPP : " + cpp
356358

359+
def buildArgsParser():
360+
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
361+
parser.add_argument('--mode', type=str, choices=('update', 'clear'))
362+
parser.add_argument('--strategy', default='incremental', choices=('incremental', 'per-processor', 'single-compilation-unit'),
363+
help='Strategy for grouping files together')
364+
parser.add_argument('--tmpDir', type=str)
365+
parser.add_argument('--mocMode', type=str, choices=('MOC_LVL_0', 'MOC_LVL_1', 'MOC_LVL_2'), default='MOC_LVL_1')
366+
parser.add_argument('--sourceListPath', type=str)
357367

368+
return parser
369+
358370
def main():
359-
if len(sys.argv) <= 3:
360-
print("""Usage : python qmake-unity.py mode strategy path_to_unity.pri [source1.cpp, subdir/source2.cpp ...]
361-
Mode : update/clear/removeOldSources/QMAKE_LIST_REMOVED/QMAKE_LIST_ADDED
362-
Strategy: incremental(little groups)/per-processor(one group per processor)/unity(one group)
363-
""")
364-
return
365-
366371
try:
367-
#print_debug("current dir : " + os.getcwd())
368-
mode = sys.argv[1]
369-
strategy = sys.argv[2]
370-
unityDirectory = os.path.normpath(sys.argv[3])
372+
# Must be executed from the project's source directory (which contains the .pro file)
373+
# print_dev("current dir : " + os.getcwd())
374+
cliArgs = buildArgsParser().parse_args()
375+
376+
mode = cliArgs.mode
377+
strategy = cliArgs.strategy
378+
unityDirectory = os.path.normpath(cliArgs.tmpDir)
371379
unityPriFile = os.path.normpath(unityDirectory + "/unity.pri")
372-
#print_debug(unityDirectory)
373-
380+
mocMode = cliArgs.mocMode
381+
inputProjectCppListFile = cliArgs.sourceListPath
374382

375383
projectCppListPath = []
376-
if len(sys.argv) > 4:
377-
inputProjectCppListFile = sys.argv[4]
384+
if inputProjectCppListFile is not None:
378385
assert os.path.exists(inputProjectCppListFile), "The sources file list doesn't exists : " + inputProjectCppListFile + ". This error is probably the consequence of another error."
379386

380387
with open(inputProjectCppListFile) as file:
@@ -416,9 +423,16 @@ def main():
416423
cppGroupSize = len(cppList)
417424

418425
generateUnityBuildFiles(unityPriFile, unityDirectory, mode, 'cpp', cppGroupList, cppList, cppGroupSize, "w+")
419-
print_debug('todo : decomment only for MOC_LVL_1')
420-
print_debug('todo : comment only for MOC_LVL_0 and MOC_LVL_2')
421-
generateUnityBuildFiles(unityPriFile, unityDirectory, mode, 'moc', mocGroupList, mocList, MOC_GROUPSIZE, "a")
426+
427+
if mocMode == 'MOC_LVL_1':
428+
# LVL_0 = no moc optimization
429+
# LVL_1 = moc files, the group them for CL step
430+
# LVL_2 = group moc calls before the CL step ; see unity_moc_headers
431+
generateUnityBuildFiles(unityPriFile, unityDirectory, mode, 'moc', mocGroupList, mocList, MOC_GROUPSIZE, "a")
432+
else:
433+
for group in mocGroupList:
434+
group.removeFromDisk()
435+
422436

423437
except InlineException as err:
424438
sys.stderr.write(err.message + "\n")

qmakeUnity/unity_common.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def utf8_file_read(file):
2323

2424
def file_remove(filePath):
2525
if SAFE_MODE:
26-
print_debug("SAFE MODE : simulate os.remove(" + filePath + ")", True)
26+
print_debug(f'SAFE MODE - simulate delete file "{filePath}" ({os.path.abspath(filePath)})', True)
2727
else:
2828
os.remove(filePath)
2929

qmakeUnity/unity_config.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
LOG_DEBUG = True
66

77
# List of namespace names that are allowed appear in a "using namespace XXX" expression in a group
8-
NAMESPACE_WHITELIST = ["GoodNamespace"]
8+
# ex : NAMESPACE_WHITELIST = ["good::namespace"]
9+
NAMESPACE_WHITELIST = []
910

1011
# we let 1 more to avoid freezes
1112
NB_PROCESSORS = 7
1213

13-
# nb files that will be mocced together
14+
# nb files that will be mocced together (MOC_LVL_1 or MOC_LVL_2)
1415
MOC_GROUPSIZE = 50

0 commit comments

Comments
 (0)