summaryrefslogtreecommitdiffstats
path: root/mkspecs
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@digia.com>2013-10-08 19:41:16 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-10-25 20:50:51 +0200
commitc760d2dbfdaf743052be620f58c1f123420e9086 (patch)
tree40965f1d1b4a655b81605dd617b391e21e7925eb /mkspecs
parent25650923b10bfd9a5f6b7c05c365eeb602023357 (diff)
Rewrite qmake's exclusive-build feature
We used to compute the default exclusive build directory, eg 'debug', at configure time, and then set OBJECTS_DIR, MOC_DIR, etc to include this hard-coded default exclusive build directory. We then had to run a post- process step where we replaced the 'debug' part with the current actual exclusive build pass, eg 'release', resulting in long-standing bugs such as QTBUG-491 where we end up replacing parts of the build output dirs that were not part of the original exclusive build directory. We now set the OBJECTS_DIR, MOC_DIR, etc defaults in configure like before, but they do not include any exclusive-build information. The exclusive build directory is handled as a separate step in default_post where we adjust all entries in QMAKE_DIR_REPLACE to be exclusive directories. For backwards compatibility the new exclusive build behavior is only enabled for variables named by QMAKE_DIR_REPLACE_SANE, which for Qt itself applies globally to everything but DESTDIR, and for libs and tools also applies to DESTDIR. The reason for leaving out DESTDIR in the general case is because many tests and examples assume the old behavior for DESTDIR. A side effect of including all the other variables for Qt libs and tools is that the PCH output dir will be uniformly set, which has been an issue on Windows in the past. The addExclusiveBuilds function now takes two or more arguments, each argument being the key for an exclusive build, which can be customized eg. using $$key.{name,target,dir_affix}. Passing more than two arguments results in three/four/etc-way exclusive builds, eg debug/release/profile. Exclusive builds can also be combined, eg static/shared + debug/release by making two calls to the function. We also handle individual targets of combined exclusive builds, eg static/shared + debug/release, meaning it is possible to run 'make debug' to build both static-debug and shared-debug. Task-number: QTBUG-491 Change-Id: I02841dbbd065ac07d413dfb45cfcfe4c013674ac Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
Diffstat (limited to 'mkspecs')
-rw-r--r--mkspecs/features/default_post.prf2
-rw-r--r--mkspecs/features/exclusive_builds.prf109
-rw-r--r--mkspecs/features/exclusive_builds_post.prf160
-rw-r--r--mkspecs/features/qt_build_config.prf8
-rw-r--r--mkspecs/features/qt_common.prf3
-rw-r--r--mkspecs/features/resolve_config.prf19
-rw-r--r--mkspecs/features/resolve_target.prf2
7 files changed, 205 insertions, 98 deletions
diff --git a/mkspecs/features/default_post.prf b/mkspecs/features/default_post.prf
index 4501b2a568..8eb7101acc 100644
--- a/mkspecs/features/default_post.prf
+++ b/mkspecs/features/default_post.prf
@@ -6,6 +6,8 @@ contains(TEMPLATE, ".*(lib|app)"):CONFIG += have_target
load(resolve_config)
+exclusive_builds: load(exclusive_builds_post)
+
# If the TARGET looks like a path, split it into DESTDIR and the resulting TARGET
target_dir_part = $$dirname(TARGET)
!isEmpty(target_dir_part) {
diff --git a/mkspecs/features/exclusive_builds.prf b/mkspecs/features/exclusive_builds.prf
index c45ff22c06..4d88dcd15e 100644
--- a/mkspecs/features/exclusive_builds.prf
+++ b/mkspecs/features/exclusive_builds.prf
@@ -1,92 +1,37 @@
-# fixExclusiveOutputDirs(1config, 2config)
-# Change all output paths that references 2config to have the string 1config in them
-defineTest(fixExclusiveOutputDirs) {
- firstBuild = $$1
- secondBuild = $$2
- count(ARGS, 2, greaterThan):isEqual($$list($$lower($$3)), false):appendFirstBuild = false
- else:appendFirstBuild = true
- isEmpty(QMAKE_DIR_REPLACE):QMAKE_DIR_REPLACE += OBJECTS_DIR MOC_DIR RCC_DIR PRECOMPILED_DIR
- lessThan(firstBuild, $$secondBuild):eval($${firstBuild}_and_$${secondBuild}_target:QMAKE_DIR_REPLACE += DESTDIR)
- else:eval($${secondBuild}_and_$${firstBuild}_target:QMAKE_DIR_REPLACE += DESTDIR)
- for(fix, QMAKE_DIR_REPLACE) {
- isEmpty($$fix)|isEqual($$fix, .) {
- eval($$fix = $${firstBuild})
- } else:contains($$list($$first($$fix)), .*$${secondBuild}.*) {
- eval($$fix ~= s/$${secondBuild}/$${firstBuild}/gi)
- } else:isEqual(appendFirstBuild, true):!contains($$list($$first($$fix)), .*$${firstBuild}.*) {
- contains($$list($${first($$fix)}), .*/$):eval($$fix = $${first($$fix)}$${firstBuild})
- else:eval($$fix = $${first($$fix)}-$${firstBuild})
- }
- export($$fix)
- }
- return(true)
-}
-
-# addExclusiveBuilds(1config, 1name, 2config, 2name)
-# Adds two BUILDS which are exclusive to each other.
defineTest(addExclusiveBuilds) {
- firstBuild = $$1
- firstBuildName = $$2
- secondBuild = $$3
- secondBuildName = $$4
+ lessThan(ARGC, 2): \
+ error("addExclusiveBuilds() requires at least two arguments")
- contains(TEMPLATE, subdirs) {
- eval(sub_$${firstBuildName}.target = $$firstBuild)
- export(sub_$${firstBuildName}.target)
- eval(sub_$${firstBuildName}.CONFIG = recursive)
- export(sub_$${firstBuildName}.CONFIG)
- eval(sub_$${secondBuildName}.target = $$secondBuild)
- export(sub_$${secondBuildName}.target)
- eval(sub_$${secondBuildName}.CONFIG = recursive)
- export(sub_$${secondBuildName}.CONFIG)
- QMAKE_EXTRA_TARGETS += sub_$${firstBuildName} sub_$${secondBuildName}
- export(QMAKE_EXTRA_TARGETS)
- } else:!build_pass {
- first_BUILDS =
- second_BUILDS =
- suffix_BUILDS = Build
+ !$$join(ARGS, _and_):!fix_output_dirs: \
+ return(true)
- isEmpty(BUILDS): BUILDPERMUTATIONS = $$suffix_BUILDS
- else: BUILDPERMUTATIONS = $$BUILDS
+ for(build, ARGS) {
+ isEmpty($${build}.name) {
+ $${build}.name = $$title($$build)
+ export($${build}.name)
+ }
+ isEmpty($${build}.target) {
+ $${build}.target = $$lower($$build)
+ export($${build}.target)
+ }
+ isEmpty($${build}.dir_affix) {
+ $${build}.dir_affix = $$lower($$build)
+ export($${build}.dir_affix)
+ }
- for(permutation, BUILDPERMUTATIONS) {
- permutation ~= s/$${suffix_BUILDS}$//
- isEmpty(permutation): permutationName =
- else: permutationName = -$$permutation
- # Makefile target rule
- eval($${firstBuildName}$${permutation}.target = $${firstBuild}$$lower($${permutationName}))
- export($${firstBuildName}$${permutation}.target)
- # IDE name
- eval($${firstBuildName}$${permutation}.name = $${firstBuildName}$${permutationName})
- export($${firstBuildName}$${permutation}.name)
- # prl import CONFIG option
- eval($${firstBuildName}$${permutation}.PRL_CONFIG = $${firstBuild}$${permutation})
- export($${firstBuildName}$${permutation}.PRL_CONFIG)
- # Individual CONFIG option
- eval($${firstBuildName}$${permutation}.CONFIG = $${firstBuild} $${firstBuildName}Build $$eval($${permutation}.CONFIG))
- export($${firstBuildName}$${permutation}.CONFIG)
+ $${build}.exclusive = $$ARGS
+ export($${build}.exclusive)
- eval($${secondBuildName}$${permutation}.target = $${secondBuild}$$lower($${permutationName}))
- export($${secondBuildName}$${permutation}.target)
- eval($${secondBuildName}$${permutation}.name = $${secondBuildName}$${permutationName})
- export($${secondBuildName}$${permutation}.name)
- eval($${secondBuildName}$${permutation}.PRL_CONFIG = $${secondBuild}$${permutation})
- export($${secondBuildName}$${permutation}.PRL_CONFIG)
- eval($${secondBuildName}$${permutation}.CONFIG = $${secondBuild} $${secondBuildName}Build $$eval($${permutation}.CONFIG))
- export($${secondBuildName}$${permutation}.CONFIG)
+ QMAKE_EXCLUSIVE_BUILDS += $$build
+ }
- first_BUILDS += $${firstBuildName}$${permutation}
- second_BUILDS += $${secondBuildName}$${permutation}
- }
+ CONFIG *= exclusive_builds
+ export(CONFIG)
- # A mutual exclusive block.
- CONFIG($${firstBuild}, $${firstBuild}|$${secondBuild}): BUILDS = $$first_BUILDS $$second_BUILDS
- else: BUILDS = $$second_BUILDS $$first_BUILDS
- export(BUILDS)
- } else {
- eval($${firstBuildName}Build:fixExclusiveOutputDirs($$firstBuild, $$secondBuild, false))
- eval($${secondBuildName}Build:fixExclusiveOutputDirs($$secondBuild, $$firstBuild, false))
- }
+ export(QMAKE_EXCLUSIVE_BUILDS)
return(true)
}
+
+# Default directories to process
+QMAKE_DIR_REPLACE = OBJECTS_DIR MOC_DIR RCC_DIR PRECOMPILED_DIR DESTDIR
diff --git a/mkspecs/features/exclusive_builds_post.prf b/mkspecs/features/exclusive_builds_post.prf
new file mode 100644
index 0000000000..936085af0b
--- /dev/null
+++ b/mkspecs/features/exclusive_builds_post.prf
@@ -0,0 +1,160 @@
+
+contains(TEMPLATE, subdirs) {
+ for(build, QMAKE_EXCLUSIVE_BUILDS) {
+ prepareRecursiveTarget($$build)
+ QMAKE_EXTRA_TARGETS += $$build
+ }
+} else {
+ # Recursively combines a list of exclusive builds into combinations
+ # of non-exclusive builds (separated by a ':' character), eg the
+ # list [debug, release, static, shared] will result in the four
+ # combinations [debug:static, debug:shared, release:static,
+ # release:shared].
+ defineReplace(combineExclusiveBuilds) {
+ permutationBuilds = $$1
+ existingBuilds = $$2
+
+ isEmpty(permutationBuilds): \
+ # Exit-condition, no more recursing
+ return($$existingBuilds)
+
+ # Choose the first build of the permutations and use the set of exclusive
+ # builds associated with that build as the list of existing builds. This
+ # partitions the permutations into one set of exclusive builds + the rest
+ # of the unknown permutations.
+ newExistingBuilds = $$eval($$first(permutationBuilds).exclusive)
+ permutationBuilds -= $$newExistingBuilds
+
+ # Recursively compute the combination of these two sets
+ recursiveCombination = $$combineExclusiveBuilds($$permutationBuilds, $$newExistingBuilds)
+
+ isEmpty(existingBuilds): \
+ # No need to combine further
+ return($$recursiveCombination)
+
+ result =
+ for(existingBuild, existingBuilds) {
+ for(combination, recursiveCombination): \
+ result += "$${existingBuild}:$${combination}"
+ }
+ return($$result)
+ }
+
+ buildCombinations = $$combineExclusiveBuilds($$QMAKE_EXCLUSIVE_BUILDS)
+
+ for(combination, buildCombinations) {
+ builds = $$split(combination, :)
+ key =
+ config =
+ target =
+ priority =
+ for(build, builds) {
+ key = $${key}$$eval($${build}.name)
+ config *= $$eval($${build}.CONFIG) $${build} $$eval($${build}.name)Build
+ target += $$eval($${build}.target)
+
+ # If a build has been prioritized through CONFIG we prefer that
+ CONFIG($$build, $$join($${build}.exclusive, |)): \
+ priority += 1
+ }
+
+ $${key}.name = $$key
+ $${key}.target = $$join(target, -)
+ $${key}.CONFIG = $$config
+ $${key}.builds = $$builds
+
+ BUILDS.$$size(priority) += $$key
+
+ # Add makefile targets for each exclusive build that will aggregate all targets
+ # that include the exclusive build. This matches the targets in the SUBDIR files
+ # so that you can recursivly build a single exclusive build.
+ !build_pass:count(builds, 1, >) {
+ for(build, builds) {
+ $${build}.depends += $$eval($${key}.target)
+ QMAKE_EXTRA_TARGETS *= $$build
+ }
+ }
+ }
+
+ BUILDS =
+ priority =
+ for(ever) {
+ # Keep the order in BUILDS matching the priority from CONFIG, so that the first
+ # entry in BUILDS will be the first/default target when not CONFIG(build_all).
+ BUILDS = $$eval(BUILDS.$$size(priority)) $$BUILDS
+ count(BUILDS, $$size(buildCombinations), >=): break()
+ priority += 1
+ }
+
+ build_pass|fix_output_dirs {
+ !build_pass {
+ # The builds are sorted by priority based on the current config
+ # so choosing the first one gives us the most appropriate build.
+ BUILD_PASS = $$first(BUILDS)
+ }
+
+ for(dir, QMAKE_DIR_REPLACE) {
+
+ # Limit builds to ones that should affect the current $$dir
+ builds =
+ for(build, $${BUILD_PASS}.builds) {
+ equals(dir, DESTDIR) {
+ !$$join($${build}.exclusive, _and_)_target: \
+ next()
+ }
+
+ builds += $$build
+ }
+
+ isEmpty(builds): \
+ next()
+
+ affixes =
+ for(build, builds): \
+ affixes += $$eval($${build}.dir_affix)
+ full_dir_affix = $$join(affixes, -)
+
+ isEmpty($$dir)|isEqual($$dir, .) {
+ # Use affix directly
+ $$dir = $$full_dir_affix
+ next()
+ }
+
+ contains(QMAKE_DIR_REPLACE_SANE, $$dir) {
+ # Suffix output dir
+ $$dir = $$clean_path($$eval($$dir)/$$full_dir_affix)
+ } else {
+ # "Compatibility mode" with QTBUG-491
+ for(build, builds) {
+ did_replace = false
+ build_affix = $$eval($${build}.dir_affix)
+ for(exclusive, $${build}.exclusive) {
+ equals(exclusive, $$build): \
+ next()
+
+ exclusive_affix = $$eval($${exclusive}.dir_affix)
+ contains($$dir, .*$${exclusive_affix}.*) {
+ $$dir ~= s/$${exclusive_affix}/$${build_affix}/gi
+ did_replace = true
+ }
+ }
+ $$did_replace: next()
+
+ # Append (as subdir or as suffix)
+ !build_pass {
+ dir_affix = $$eval($${build}.dir_affix)
+ !contains($$dir, .*$${dir_affix}.*) {
+ contains($$dir, .*/$) {
+ # Subdir
+ $$dir = $$eval($$dir)$$dir_affix
+ } else {
+ # Suffix
+ $$dir = $$eval($$dir)-$${dir_affix}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/mkspecs/features/qt_build_config.prf b/mkspecs/features/qt_build_config.prf
index a29e09cbc5..faa7a44f67 100644
--- a/mkspecs/features/qt_build_config.prf
+++ b/mkspecs/features/qt_build_config.prf
@@ -20,6 +20,14 @@
debug(1, "Not loading qmodule.pri twice")
}
+PRECOMPILED_DIR = .pch
+OBJECTS_DIR = .obj
+MOC_DIR = .moc
+RCC_DIR = .rcc
+UI_DIR = .uic
+
+QMAKE_DIR_REPLACE_SANE = PRECOMPILED_DIR OBJECTS_DIR MOC_DIR RCC_DIR UI_DIR
+
# force_independent can be set externally. prefix_build not.
!exists($$[QT_HOST_DATA]/.qmake.cache): \
CONFIG += prefix_build force_independent
diff --git a/mkspecs/features/qt_common.prf b/mkspecs/features/qt_common.prf
index 746e2878a7..22d66e8907 100644
--- a/mkspecs/features/qt_common.prf
+++ b/mkspecs/features/qt_common.prf
@@ -9,6 +9,9 @@
# We mean it.
#
+QMAKE_DIR_REPLACE_SANE += DESTDIR
+CONFIG -= debug_and_release_target
+
contains(QT_CONFIG, c++11): CONFIG += c++11
contains(TEMPLATE, .*lib) {
# module and plugins
diff --git a/mkspecs/features/resolve_config.prf b/mkspecs/features/resolve_config.prf
index 3884598a94..7835fe4f7c 100644
--- a/mkspecs/features/resolve_config.prf
+++ b/mkspecs/features/resolve_config.prf
@@ -22,14 +22,8 @@ CONFIG(static, static|shared) {
contains(TEMPLATE, ".*lib"): CONFIG += dll
}
-static_and_shared {
- !macx-xcode: addExclusiveBuilds(static, Static, shared, Shared)
-} else: fix_output_dirs {
- static: \
- fixExclusiveOutputDirs(static, shared)
- else: \
- fixExclusiveOutputDirs(shared, static)
-}
+!macx-xcode: \
+ addExclusiveBuilds(shared, static)
CONFIG(debug, debug|release): \
CONFIG -= release
@@ -37,14 +31,7 @@ else: \
CONFIG -= debug
!macx-xcode {
- debug_and_release {
- addExclusiveBuilds(debug, Debug, release, Release)
- } else: fix_output_dirs {
- debug: \
- fixExclusiveOutputDirs(debug, release)
- else: \
- fixExclusiveOutputDirs(release, debug)
- }
+ addExclusiveBuilds(debug, release)
} else {
# The Xcode generator always generates project files with
# debug and release configurations, regardless of whether
diff --git a/mkspecs/features/resolve_target.prf b/mkspecs/features/resolve_target.prf
index 22d7722ce3..fe5808940a 100644
--- a/mkspecs/features/resolve_target.prf
+++ b/mkspecs/features/resolve_target.prf
@@ -11,6 +11,8 @@
load(resolve_config)
+exclusive_builds: load(exclusive_builds_post)
+
QMAKE_RESOLVED_TARGET = $$absolute_path($$DESTDIR, $$OUT_PWD)/
win32 {