qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

149
qmake/CMakeLists.txt Normal file
View File

@ -0,0 +1,149 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
# Generated from qmake.pro.
#####################################################################
## qmake Tool:
#####################################################################
# The common object library, that should be available regardless of the presence of qmake.
qt_add_library(QtLibraryInfo OBJECT
library/proitems.cpp library/proitems.h
library/qmake_global.h
property.cpp property.h
propertyprinter.cpp propertyprinter.h
qmakelibraryinfo.cpp qmakelibraryinfo.h
)
qt_internal_add_sync_header_dependencies(QtLibraryInfo Core)
set_target_properties(QtLibraryInfo PROPERTIES
COMPILE_OPTIONS $<TARGET_PROPERTY:Qt::Core,INTERFACE_COMPILE_OPTIONS>
COMPILE_DEFINITIONS $<TARGET_PROPERTY:Qt::Core,INTERFACE_COMPILE_DEFINITIONS>
INCLUDE_DIRECTORIES $<TARGET_PROPERTY:Qt::Core,INTERFACE_INCLUDE_DIRECTORIES>
INCLUDE_DIRECTORIES $<TARGET_PROPERTY:Qt::CorePrivate,INTERFACE_INCLUDE_DIRECTORIES>
)
target_include_directories(QtLibraryInfo PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/library"
)
# Chop off the "/mkspecs" part of INSTALL_MKSPECSDIR
get_filename_component(hostdatadir "${INSTALL_MKSPECSDIR}" DIRECTORY)
if("${hostdatadir}" STREQUAL "")
set(hostdatadir ".")
endif()
qt_internal_library_deprecation_level(deprecation_define)
target_compile_definitions(QtLibraryInfo PUBLIC
PROEVALUATOR_FULL
QT_BUILD_QMAKE
QT_USE_QSTRINGBUILDER
QT_NO_FOREACH
QT_VERSION_STR="${PROJECT_VERSION}"
QT_HOST_MKSPEC="${QT_QMAKE_HOST_MKSPEC}"
QT_TARGET_MKSPEC="${QT_QMAKE_TARGET_MKSPEC}"
QT_HOST_DATADIR="${hostdatadir}"
${deprecation_define}
)
if(NOT QT_FEATURE_qmake)
return()
endif()
qt_get_tool_target_name(target_name qmake)
qt_internal_add_tool(${target_name}
TOOLS_TARGET Core # special case
USER_FACING
NO_UNITY_BUILD
INSTALL_VERSIONED_LINK
SOURCES
cachekeys.h
generators/mac/pbuilder_pbx.cpp generators/mac/pbuilder_pbx.h
generators/makefile.cpp generators/makefile.h
generators/makefiledeps.cpp generators/makefiledeps.h
generators/metamakefile.cpp generators/metamakefile.h
generators/projectgenerator.cpp generators/projectgenerator.h
generators/unix/unixmake.cpp generators/unix/unixmake.h
generators/unix/unixmake2.cpp
generators/win32/mingw_make.cpp generators/win32/mingw_make.h
generators/win32/msbuild_objectmodel.cpp generators/win32/msbuild_objectmodel.h
generators/win32/msvc_nmake.cpp generators/win32/msvc_nmake.h
generators/win32/msvc_objectmodel.cpp generators/win32/msvc_objectmodel.h
generators/win32/msvc_vcproj.cpp generators/win32/msvc_vcproj.h
generators/win32/msvc_vcxproj.cpp generators/win32/msvc_vcxproj.h
generators/win32/winmakefile.cpp generators/win32/winmakefile.h
generators/xmloutput.cpp generators/xmloutput.h
library/ioutils.cpp library/ioutils.h
library/qmakebuiltins.cpp
library/qmakeevaluator.cpp library/qmakeevaluator.h library/qmakeevaluator_p.h
library/qmakeglobals.cpp library/qmakeglobals.h
library/qmakeparser.cpp library/qmakeparser.h
library/qmakevfs.cpp library/qmakevfs.h
main.cpp
meta.cpp meta.h
option.cpp option.h
project.cpp project.h
DEFINES
HAVE_CONFIG_H
INCLUDE_DIRECTORIES
generators
generators/mac
generators/unix
generators/win32
library
PRECOMPILED_HEADER
"qmake_pch.h"
LIBRARIES
Qt::CorePrivate
QtLibraryInfo
)
qt_internal_return_unless_building_tools()
# Add QMAKE_VERSION_STR only if qmake is part of the build.
target_compile_definitions(QtLibraryInfo PUBLIC
QMAKE_VERSION_STR="3.1"
)
#### Keys ignored in scope 1:.:.:qmake.pro:<TRUE>:
# _OPTION = "host_build"
## Scopes:
#####################################################################
qt_internal_extend_target(${target_name} CONDITION MACOS
COMPILE_OPTIONS
"-fconstant-cfstrings"
)
# special case big
qt_internal_extend_target(${target_name} CONDITION WIN32
SOURCES
library/registry.cpp
DEFINES
_SCL_SECURE_NO_WARNINGS
)
qt_internal_extend_target(${target_name} CONDITION CLANG AND WIN32
COMPILE_OPTIONS
"-fms-compatibility-version=19.00.23506"
"-Wno-microsoft-enum-value"
)
# special case:
set_target_properties(${target_name} PROPERTIES
AUTOMOC OFF
AUTORCC OFF
AUTOUIC OFF
)
qt_internal_apply_gc_binaries(${target_name} PRIVATE) # special case
qt_skip_warnings_are_errors(${target_name}) # special case
# special case begin
qt_internal_add_docs(${target_name}
doc/qmake.qdocconf
)
# special case end

95
qmake/cachekeys.h Normal file
View File

@ -0,0 +1,95 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CACHEKEYS_H
#define CACHEKEYS_H
#include "option.h"
#include "project.h"
#include <qstring.h>
#include <qstringlist.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qhash.h>
QT_BEGIN_NAMESPACE
// -------------------------------------------------------------------------------------------------
struct FixStringCacheKey
{
mutable size_t hash;
QString string, pwd;
uchar flags;
FixStringCacheKey(const QString &s, uchar f)
{
hash = 0;
pwd = qmake_getpwd();
string = s;
flags = f;
}
bool operator==(const FixStringCacheKey &f) const
{
return (hashCode() == f.hashCode() &&
f.flags == flags &&
f.string == string &&
f.pwd == pwd);
}
inline size_t hashCode() const {
if(!hash)
hash = qHash(string) ^ qHash(flags) /*^ qHash(pwd)*/;
return hash;
}
};
inline size_t qHash(const FixStringCacheKey &f) { return f.hashCode(); }
// -------------------------------------------------------------------------------------------------
struct FileInfoCacheKey
{
mutable size_t hash;
QString file, pwd;
FileInfoCacheKey(const QString &f)
{
hash = 0;
if(isRelativePath(f))
pwd = qmake_getpwd();
file = f;
}
bool operator==(const FileInfoCacheKey &f) const
{
return (hashCode() == f.hashCode() && f.file == file &&
f.pwd == pwd);
}
inline size_t hashCode() const {
if(!hash)
hash = qHash(file) /*^ qHash(pwd)*/;
return hash;
}
inline bool isRelativePath(const QString &file) {
int length = file.size();
if (!length)
return true;
const QChar c0 = file.at(0);
const QChar c1 = length >= 2 ? file.at(1) : QChar::Null;
return !(c0 == QLatin1Char('/')
|| c0 == QLatin1Char('\\')
|| (c0.isLetter() && c1 == QLatin1Char(':'))
|| (c0 == QLatin1Char('/') && c1 == QLatin1Char('/'))
|| (c0 == QLatin1Char('\\') && c1 == QLatin1Char('\\')));
}
};
inline size_t qHash(const FileInfoCacheKey &f) { return f.hashCode(); }
// -------------------------------------------------------------------------------------------------
template <typename T>
inline void qmakeDeleteCacheClear(void *i) { delete reinterpret_cast<T*>(i); }
inline void qmakeFreeCacheClear(void *i) { free(i); }
typedef void (*qmakeCacheClearFunc)(void *);
void qmakeAddCacheClear(qmakeCacheClearFunc func, void **);
void qmakeClearCaches();
QT_END_NAMESPACE
#endif // CACHEKEYS_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

64
qmake/doc/qmake.qdocconf Normal file
View File

@ -0,0 +1,64 @@
include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
project = QMake
moduleheader = qmake_pch.h
description = QMake Manual
qhp.projects = qmake
qhp.qmake.file = qmake.qhp
qhp.qmake.namespace = org.qt-project.qmake.$QT_VERSION_TAG
qhp.qmake.virtualFolder = qmake
qhp.qmake.indexTitle = QMake Manual
qhp.qmake.subprojects = manual
qhp.qmake.subprojects.manual.title = Manual
qhp.qmake.subprojects.manual.indexTitle = qmake Manual
qhp.qmake.subprojects.manual.selectors = fake:page
language = Cpp
sources = src/qmake-manual.qdoc
imagedirs = images
exampledirs = snippets
tagfile = qmake.tags
macro.qtbug = "\\l{https://bugreports.qt.io/browse/\1}{\1}"
depends += \
activeqt \
qt3d \
qtassistant \
qtbluetooth \
qtconcurrent \
qtcore \
qtdbus \
qtdoc \
qtdesigner \
qtgui \
qthelp \
qtimageformats \
qtlinguist \
qtlocation \
qtmultimedia \
qtnetwork \
qtopengl \
qtprintsupport \
qtqml qtqmltest \
qtquick \
qtquickcontrols \
qtquickdialogs \
qtsensors \
qtserialport \
qtsql \
qtsvg \
qttestlib \
qtuitools \
qtwidgets \
qtxml
navigation.landingpage = "qmake Manual"
# Fail the documentation build if there are more warnings than the limit
warninglimit = 0

View File

@ -0,0 +1,21 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [104]
// Add C includes here
#if defined __cplusplus
// Add C++ includes here
#include <stdlib>
#include <iostream>
#include <vector>
#include <QApplication> // Qt includes
#include <QPushButton>
#include <QLabel>
#include "thirdparty/include/libmain.h"
#include "my_stable_class.h"
...
#endif
//! [104]

View File

@ -0,0 +1,963 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#! [0]
make all
#! [0]
#! [1]
CONFIG += qt debug
#! [1]
#! [2]
QT += network xml
#! [2]
#! [3]
QT = network xml # This will omit the core and gui modules.
#! [3]
#! [4]
QT -= gui # Only the core module is used.
#! [4]
#! [5]
CONFIG += link_pkgconfig
PKGCONFIG += ogg dbus-1
#! [5]
#! [6]
LIBS += -L/usr/local/lib -lmath
#! [6]
#! [7]
INCLUDEPATH = c:/msdev/include d:/stl/include
#! [7]
#! [8]
qmake [mode] [options] files
#! [8]
#! [9]
qmake -makefile [options] files
#! [9]
#! [10]
qmake -makefile -o Makefile "CONFIG+=test" test.pro
#! [10]
#! [11]
qmake "CONFIG+=test" test.pro
#! [11]
#! [12]
qmake -project [options] files
#! [12]
#! [13]
qmake -spec macx-g++
#! [13]
#! [14]
LIBS += -F/path/to/framework/directory/
#! [14]
#! [15]
LIBS += -framework TheFramework
#! [15]
#! [16]
TEMPLATE = lib
CONFIG += lib_bundle
#! [16]
#! [17]
FRAMEWORK_HEADERS.version = Versions
FRAMEWORK_HEADERS.files = path/to/header_one.h path/to/header_two.h
FRAMEWORK_HEADERS.path = Headers
QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS
#! [17]
#! [19]
qmake -spec macx-xcode project.pro
#! [19]
#! [20]
qmake -tp vc
#! [20]
#! [21]
qmake -tp vc -r
#! [21]
#! [22]
CONFIG -= embed_manifest_exe
#! [22]
#! [23]
CONFIG -= embed_manifest_dll
#! [23]
#! [24]
make all
#! [24]
#! [25]
build_pass:CONFIG(debug, debug|release) {
unix: TARGET = $$join(TARGET,,,_debug)
else: TARGET = $$join(TARGET,,,d)
}
#! [25]
#! [26]
CONFIG += console newstuff
...
newstuff {
SOURCES += new.cpp
HEADERS += new.h
}
#! [26]
#! [27]
DEFINES += USE_MY_STUFF
#! [27]
#! [30]
DESTDIR = ../../lib
#! [30]
#! [31]
DISTFILES += ../program.txt
#! [31]
#! [32]
FORMS = mydialog.ui \
mywidget.ui \
myconfig.ui
#! [32]
#! [34]
HEADERS = myclass.h \
login.h \
mainwindow.h
#! [34]
#! [35]
INCLUDEPATH = c:/msdev/include d:/stl/include
#! [35]
#! [36]
target.path += $$[QT_INSTALL_PLUGINS]/imageformats
INSTALLS += target
#! [36]
#! [37]
LEXSOURCES = lexer.l
#! [37]
#! [38]
unix:LIBS += -L/usr/local/lib -lmath
win32:LIBS += c:/mylibs/math.lib
#! [38]
#! [39]
CONFIG += no_lflags_merge
#! [39]
#! [40]
unix:MOC_DIR = ../myproject/tmp
win32:MOC_DIR = c:/myproject/tmp
#! [40]
#! [41]
unix:OBJECTS_DIR = ../myproject/tmp
win32:OBJECTS_DIR = c:/myproject/tmp
#! [41]
#! [43]
FRAMEWORK_HEADERS.version = Versions
FRAMEWORK_HEADERS.files = path/to/header_one.h path/to/header_two.h
FRAMEWORK_HEADERS.path = Headers
QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS
#! [43]
#! [44]
QMAKE_BUNDLE_EXTENSION = .myframework
#! [44]
#! [45]
QMAKE_RESOURCE_FLAGS += -threshold 0 -compress 9
#! [45]
#! [47]
QT -= gui # Only the core module is used.
#! [47]
#! [48]
unix:RCC_DIR = ../myproject/resources
win32:RCC_DIR = c:/myproject/resources
#! [48]
#! [49]
SOURCES = myclass.cpp \
login.cpp \
mainwindow.cpp
#! [49]
#! [50]
SUBDIRS = kernel \
tools \
myapp
#! [50]
#! [51]
SUBDIRS += my_executable my_library tests doc
my_executable.depends = my_library
tests.depends = my_executable
#! [51]
#! [52]
TEMPLATE = app
TARGET = myapp
SOURCES = main.cpp
#! [52]
#! [53]
TEMPLATE = lib
SOURCES = main.cpp
TARGET = mylib
#! [53]
#! [54]
unix:UI_DIR = ../myproject/ui
win32:UI_DIR = c:/myproject/ui
#! [54]
#! [57]
win32:VERSION = 1.2.3.4 # major.minor.patch.build
else:VERSION = 1.2.3 # major.minor.patch
#! [57]
#! [58]
YACCSOURCES = moc.y
#! [58]
#! [59]
FILE = /etc/passwd
FILENAME = $$basename(FILE) #passwd
#! [59]
#! [60]
CONFIG = debug
CONFIG += release
CONFIG(release, debug|release):message(Release build!) #will print
CONFIG(debug, debug|release):message(Debug build!) #no print
#! [60]
#! [61]
contains( drivers, network ) {
# drivers contains 'network'
message( "Configuring for network build..." )
HEADERS += network.h
SOURCES += network.cpp
}
#! [61]
#! [62]
error(An error has occurred in the configuration process.)
#! [62]
#! [63]
exists( $(QTDIR)/lib/libqt-mt* ) {
message( "Configuring for multi-threaded Qt..." )
CONFIG += thread
}
#! [63]
#! [64]
MY_VAR = one two three four
MY_VAR2 = $$join(MY_VAR, " -L", -L) -Lfive
MY_VAR3 = $$member(MY_VAR, 2) $$find(MY_VAR, t.*)
#! [64]
#! [65]
LIST = 1 2 3
for(a, LIST):exists(file.$${a}):message(I see a file.$${a}!)
#! [65]
#! [66]
include( shared.pri )
OPTIONS = standard custom
!include( options.pri ) {
message( "No custom build options specified" )
OPTIONS -= custom
}
#! [66]
#! [67]
isEmpty( CONFIG ) {
CONFIG += warn_on debug
}
#! [67]
#! [68]
message( "This is a message" )
#! [68]
#! [69]
!build_pass:message( "This is a message" )
#! [69]
#! [70]
This is a test.
#! [70]
#! [71]
system("ls /bin"): HAS_BIN = TRUE
#! [71]
#! [72]
UNAME = $$system(uname -s)
contains( UNAME, [lL]inux ):message( This looks like Linux ($$UNAME) to me )
#! [72]
#! [73]
ARGS = 1 2 3 2 5 1
ARGS = $$unique(ARGS) #1 2 3 5
#! [73]
#! [74]
qmake -set PROPERTY VALUE
#! [74]
#! [75]
qmake -query PROPERTY
qmake -query #queries all current PROPERTY/VALUE pairs
#! [75]
#! [77]
qmake -query "QT_INSTALL_PREFIX"
#! [77]
#! [78]
QMAKE_VERS = $$[QMAKE_VERSION]
#! [78]
#! [79]
documentation.path = /usr/local/program/doc
documentation.files = docs/*
#! [79]
#! [80]
INSTALLS += documentation
#! [80]
#! [81]
unix:documentation.extra = create_docs; mv master.doc toc.doc
#! [81]
#! [82]
target.path = /usr/local/myprogram
INSTALLS += target
#! [82]
#! [83]
CONFIG += create_prl
#! [83]
#! [84]
CONFIG += link_prl
#! [84]
#! [85]
QMAKE_EXT_MOC = .mymoc
#! [85]
#! [86]
mytarget.target = .buildfile
mytarget.commands = touch $$mytarget.target
mytarget.depends = mytarget2
mytarget2.commands = @echo Building $$mytarget.target
#! [86]
#! [87]
QMAKE_EXTRA_TARGETS += mytarget mytarget2
#! [87]
#! [88]
new_moc.output = moc_${QMAKE_FILE_BASE}.cpp
new_moc.commands = moc ${QMAKE_FILE_NAME} -o ${QMAKE_FILE_OUT}
new_moc.depend_command = g++ -E -M ${QMAKE_FILE_NAME} | sed "s,^.*: ,,"
new_moc.input = NEW_HEADERS
QMAKE_EXTRA_COMPILERS += new_moc
#! [88]
#! [89]
TARGET = myapp
#! [89]
#! [90]
DEFINES += USE_MY_STUFF
#! [90]
#! [91]
DEFINES -= USE_MY_STUFF
#! [91]
#! [92]
DEFINES *= USE_MY_STUFF
#! [92]
#! [93]
DEFINES ~= s/QT_[DT].+/QT
#! [93]
#! [94]
EVERYTHING = $$SOURCES $$HEADERS
message("The project contains the following files:")
message($$EVERYTHING)
#! [94]
#! [95]
win32:DEFINES += USE_MY_STUFF
#! [95]
#! [96]
win32:xml {
message(Building for Windows)
SOURCES += xmlhandler_win.cpp
} else:xml {
SOURCES += xmlhandler.cpp
} else {
message("Unknown configuration")
}
#! [96]
#! [97]
MY_VARIABLE = value
#! [97]
#! [98]
MY_DEFINES = $$DEFINES
#! [98]
#! [99]
MY_DEFINES = $${DEFINES}
#! [99]
#! [100]
TARGET = myproject_$${TEMPLATE}
#! [100]
#! [101]
target.path = $$[QT_INSTALL_PLUGINS]/designer
INSTALLS += target
#! [101]
#! [102]
defineReplace(functionName){
#function code
}
#! [102]
#! [103]
CONFIG += myfeatures
#! [103]
#! [105]
PRECOMPILED_HEADER = stable.h
#! [105]
#! [106]
precompile_header:!isEmpty(PRECOMPILED_HEADER) {
DEFINES += USING_PCH
}
#! [106]
#! [107]
PRECOMPILED_HEADER = window.h
SOURCES = window.cpp
#! [107]
#! [108]
SOURCES += hello.cpp
#! [108]
#! [109]
SOURCES += hello.cpp
SOURCES += main.cpp
#! [109]
#! [110]
SOURCES = hello.cpp \
main.cpp
#! [110]
#! [111]
HEADERS += hello.h
SOURCES += hello.cpp
SOURCES += main.cpp
#! [111]
#! [112]
TARGET = helloworld
#! [112]
#! [113]
HEADERS += hello.h
SOURCES += hello.cpp
SOURCES += main.cpp
#! [113]
#! [114]
qmake -o Makefile hello.pro
#! [114]
#! [115]
qmake -tp vc hello.pro
#! [115]
#! [116]
CONFIG += debug
HEADERS += hello.h
SOURCES += hello.cpp
SOURCES += main.cpp
#! [116]
#! [117]
win32 {
SOURCES += hellowin.cpp
}
#! [117]
#! [118]
CONFIG += debug
HEADERS += hello.h
SOURCES += hello.cpp
SOURCES += main.cpp
win32 {
SOURCES += hellowin.cpp
}
unix {
SOURCES += hellounix.cpp
}
#! [118]
#! [119]
!exists( main.cpp ) {
error( "No main.cpp file found" )
}
#! [119]
#! [120]
CONFIG += debug
HEADERS += hello.h
SOURCES += hello.cpp
SOURCES += main.cpp
win32 {
SOURCES += hellowin.cpp
}
unix {
SOURCES += hellounix.cpp
}
!exists( main.cpp ) {
error( "No main.cpp file found" )
}
#! [120]
#! [121]
win32 {
debug {
CONFIG += console
}
}
#! [121]
#! [122]
CONFIG += debug
HEADERS += hello.h
SOURCES += hello.cpp
SOURCES += main.cpp
win32 {
SOURCES += hellowin.cpp
}
unix {
SOURCES += hellounix.cpp
}
!exists( main.cpp ) {
error( "No main.cpp file found" )
}
win32:debug {
CONFIG += console
}
#! [122]
#! [123]
TEMPLATE = app
DESTDIR = c:/helloapp
HEADERS += hello.h
SOURCES += hello.cpp
SOURCES += main.cpp
DEFINES += USE_MY_STUFF
CONFIG += release
#! [123]
#! [124]
make all
#! [124]
#! [125]
make
#! [125]
#! [126]
make install
#! [126]
#! [127]
CONFIG(debug, debug|release) {
mac: TARGET = $$join(TARGET,,,_debug)
win32: TARGET = $$join(TARGET,,d)
}
#! [127]
#! [149]
SUBDIRS += my_executable my_library
my_executable.subdir = app
my_executable.depends = my_library
my_library.subdir = lib
#! [149]
#! [157]
packagesExist(sqlite3 QtNetwork QtDeclarative) {
DEFINES += USE_FANCY_UI
}
#! [157]
#! [158]
#ifdef USE_FANCY_UI
// Use the fancy UI, as we have extra packages available
#endif
#! [158]
#! [159]
message($$absolute_path("readme.txt", "/home/johndoe/myproject"))
#! [159]
#! [160]
TARGET = helloworld
equals(TARGET, "helloworld") {
message("The target assignment was successful.")
}
#! [160]
#! [161]
CONTACT = firstname middlename surname phone
message($$first(CONTACT))
#! [161]
#! [162]
CONTACT = firstname middlename surname phone
message($$last(CONTACT))
#! [162]
#! [163]
message($$format_number(BAD, ibase=16 width=6 zeropad))
#! [163]
#! [164]
ANSWER = 42
greaterThan(ANSWER, 1) {
message("The answer might be correct.")
}
#! [164]
#! [165]
ANSWER = 42
lessThan(ANSWER, 1) {
message("The answer might be wrong.")
}
#! [165]
#! [166]
if(linux-g++*|macx-g++*):CONFIG(debug, debug|release) {
message("We are on Linux or Mac OS, and we are in debug mode.")
}
#! [166]
#! [167]
CONTACT = firstname:middlename:surname:phone
message($$section(CONTACT, :, 2, 2))
#! [167]
#! [168]
CONTACT = firstname:middlename:surname:phone
message($$split(CONTACT, :))
#! [168]
#! [169]
NARF = zort
unset(NARF)
!defined(NARF, var) {
message("NARF is not defined.")
}
#! [169]
#! [170]
for(var, $$list(foo bar baz)) {
...
}
#! [170]
#! [171]
values = foo bar baz
for(var, values) {
...
}
#! [171]
#! [172]
VALUE = 123
TMP_VALUE = x$$VALUE
greaterThan(TMP_VALUE, x456): message("Condition may be true.")
#! [172]
#! [173]
message("First line$$escape_expand(\\n)Second line")
#! [173]
#! [174]
TEMPLATE = subdirs
SUBDIRS = one two three
prepareRecursiveTarget(check)
#! [174]
#! [175]
two.CONFIG += no_check_target
#! [175]
#! [176]
QMAKE_EXTRA_TARGETS += check
#! [176]
#! [177]
# <project root>/features/mycheck.prf
equals(TEMPLATE, subdirs) {
prepareRecursiveTarget(check)
} else {
check.commands = echo hello user
}
QMAKE_EXTRA_TARGETS += check
#! [177]
#! [178]
# <project root>/.qmake.conf
CONFIG += mycheck
#! [178]
#! [179]
# <project root>/project.pro
load(configure)
#! [179]
#! [180]
# <project root>/config.tests/test/test.pro
SOURCES = main.cpp
LIBS += -ltheFeature
# Note that the test project is built without Qt by default.
#! [180]
#! [181]
// <project root>/config.tests/test/main.cpp
#include <TheFeature/MainHeader.h>
int main() { return featureFunction(); }
#! [181]
#! [182]
# <project root>/project.pro
qtCompileTest(test)
#! [182]
#! [183]
# <project root>/project.pro
QMAKE_SONAME_PREFIX = @rpath
#! [183]
#! [184]
# <project root>/project.pro
QMAKE_SONAME_PREFIX = @executable_path/../Frameworks
QMAKE_SONAME_PREFIX = @loader_path/Frameworks
QMAKE_SONAME_PREFIX = /Library/Frameworks
#! [184]
#! [185]
VERSION_PE_HEADER = 1.2
#! [185]
#! [186]
RC_DEFINES += USE_MY_STUFF
#! [186]
#! [187]
win32-g++:contains(QMAKE_HOST.arch, x86_64):{
message("Host is 64bit")
...
}
#! [187]
#! [188]
translations_en.files = $$PWD/en.lproj/InfoPlist.strings
translations_en.path = en.lproj
QMAKE_BUNDLE_DATA += translations_en
#! [188]
#! [189]
# Approach 1
translations_en.files = $$PWD/InfoPlist.strings
translations_en.path = en.lproj
# Approach 2
translations_de.files = $$PWD/de.lproj/InfoPlist.strings
QMAKE_BUNDLE_DATA += translations_en translations_de
#! [189]

View File

@ -0,0 +1,10 @@
#! [0]
# Comments usually start at the beginning of a line, but they
# can also follow other content on the same line.
#! [0]
#! [1]
# To include a literal hash character, use the $$LITERAL_HASH variable:
urlPieces = http://doc.qt.io/qt-5/qtextdocument.html pageCount
message($$join(urlPieces, $$LITERAL_HASH))
#! [1]

View File

@ -0,0 +1,23 @@
SOURCES = main.cpp
#! [0]
CONFIG += opengl
#! [0]
#! [1]
opengl {
TARGET = application-gl
} else {
#! [1] #! [2]
TARGET = application
#! [2] #! [3]
}
#! [3]
#! [4]
CONFIG(opengl) {
message(Building with OpenGL support.)
} else {
#! [4] #! [5]
message(OpenGL support is not available.)
}
#! [5]

View File

@ -0,0 +1,14 @@
#! [0]
CONFIG += debug_and_release
CONFIG(debug, debug|release) {
TARGET = debug_binary
} else {
#! [0] #! [1]
TARGET = release_binary
}
#! [1]
#! [2]
CONFIG += build_all
#! [2]

View File

@ -0,0 +1,3 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

View File

@ -0,0 +1,5 @@
#! [0]
TEMP_SOURCES = $$SOURCES
#! [0]
# Do something with the SOURCES variable then restore its old value.
SOURCES = $$TEMP_SOURCES

View File

@ -0,0 +1,2 @@
SOURCES = main.cpp
DESTDIR = output

View File

@ -0,0 +1,6 @@
#! [0]
FILE = /etc/X11R6/XF86Config
DIRNAME = $$dirname(FILE) #/etc/X11R6
#! [0]
message($$FILE)
message($$DIRNAME)

View File

@ -0,0 +1,9 @@
#! [0] #! [1]
DESTDIR = $$(PWD)
message(The project will be installed in $$DESTDIR)
#! [0]
DESTDIR = $(PWD)
message(The project will be installed in the value of PWD)
message(when the Makefile is processed.)
#! [1]

View File

@ -0,0 +1,34 @@
#! [0]
EXTRAS = handlers tests docs
for(dir, EXTRAS) {
exists($$dir) {
SUBDIRS += $$dir
}
}
#! [0]
SOURCES = paintwidget_mac.cpp paintwidget_unix.cpp paintwidget_win.cpp
macx {
SOURCES = $$find(SOURCES, "_mac")
}
#! [1]
HEADERS = model.h
HEADERS += $$OTHER_HEADERS
HEADERS = $$unique(HEADERS)
#! [1]
CONFIG += debug
#! [2]
options = $$find(CONFIG, "debug") $$find(CONFIG, "release")
#! [3]
count(options, 2) {
message(Both release and debug specified.)
}
#! [2] #! [3]
#! [4]
eval(TARGET = myapp) {
message($$TARGET)
}
#! [4]

View File

@ -0,0 +1,3 @@
#! [0]
include(other.pro)
#! [0]

View File

@ -0,0 +1,3 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

View File

@ -0,0 +1,3 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

View File

@ -0,0 +1,3 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

View File

View File

@ -0,0 +1,3 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

View File

@ -0,0 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
int main(int argc, char *argv[])
{
return 0;
}

View File

@ -0,0 +1,3 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

View File

@ -0,0 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
/* Add C includes here */
#if defined __cplusplus
/* Add C++ includes here */
# include <iostream>
# include <QApplication>
# include <QPushButton>
# include <QLabel>
#endif
//! [0]

View File

@ -0,0 +1,6 @@
#! [project file]
message($$_PRO_FILE_)
#! [project file]
#! [project file directory]
message($$_PRO_FILE_PWD_)
#! [project file directory]

View File

@ -0,0 +1,18 @@
# Show information about the Qt installation.
#! [0]
message(Qt version: $$[QT_VERSION])
message(Qt is installed in $$[QT_INSTALL_PREFIX])
message(Qt resources can be found in the following locations:)
message(Documentation: $$[QT_INSTALL_DOCS])
message(Header files: $$[QT_INSTALL_HEADERS])
message(Libraries: $$[QT_INSTALL_LIBS])
message(Binary files (executables): $$[QT_INSTALL_BINS])
message(Plugins: $$[QT_INSTALL_PLUGINS])
message(Data files: $$[QT_INSTALL_DATA])
message(Translation files: $$[QT_INSTALL_TRANSLATIONS])
message(Settings: $$[QT_INSTALL_CONFIGURATION])
message(Examples: $$[QT_INSTALL_EXAMPLES])
#! [0]
# Show configuration information.
message(CONFIG = $$CONFIG)

View File

@ -0,0 +1,8 @@
#! [0]
DEST = "Program Files"
#! [0]
count(DEST, 1) {
message(Only one item found in DEST.)
} else {
message(More than one item found in DEST.)
}

View File

@ -0,0 +1,4 @@
#! [0]
MESSAGE = This is a tent.
message($$replace(MESSAGE, tent, test))
#! [0]

View File

@ -0,0 +1,46 @@
#! [0]
defineReplace(headersAndSources) {
variable = $$1
names = $$eval($$variable)
headers =
sources =
for(name, names) {
header = $${name}.h
exists($$header) {
headers += $$header
}
source = $${name}.cpp
exists($$source) {
sources += $$source
}
}
return($$headers $$sources)
}
#! [0]
defineReplace(matchingFiles) {
names = $$ARGS
files =
for(name, names) {
header = $${name}.h
source = $${name}.cpp
exists($$header):exists($$source) {
files += $$header
files += $$source
}
}
return($$files)
}
names = delegate model view main
message(Finding all headers and sources from the following list of names:)
message($$names)
allFiles = $$headersAndSources(names)
message(Found: $$allFiles)
message(Finding only matching headers and sources from the following list of names:)
message($$names)
matching = $$matchingFiles($$names)
message(Found: $$matching)

View File

@ -0,0 +1,60 @@
#! [syntax]
<condition> {
<command or definition>
...
}
#! [syntax]
#! [0]
win32 {
SOURCES += paintwidget_win.cpp
}
#! [0]
#! [1]
!win32 {
SOURCES -= paintwidget_win.cpp
}
#! [1]
unix {
SOURCES += paintwidget_unix.cpp
}
#! [2]
macx {
CONFIG(debug, debug|release) {
HEADERS += debugging.h
}
}
#! [2]
#! [3]
macx:CONFIG(debug, debug|release) {
HEADERS += debugging.h
}
#! [3]
#! [4]
win32|macx {
HEADERS += debugging.h
}
#! [4]
#! [5]
if(win32|macos):CONFIG(debug, debug|release) {
# Do something on Windows and macOS,
# but only for the debug configuration.
}
win32|if(macos:CONFIG(debug, debug|release)) {
# Do something on Windows (regardless of debug or release)
# and on macOS (only for debug).
}
#! [5]
#! [6]
win32-* {
# Matches every mkspec starting with "win32-"
SOURCES += win32_specific.cpp
}
#! [6]

View File

@ -0,0 +1,8 @@
TEMPLIBS = $$[QT_INSTALL_LIBS] libQtGui.prl
include($$join(TEMPLIBS, "/"))
contains(QMAKE_PRL_CONFIG, shared) {
message(Shared Qt)
} else {
message(Static Qt)
}

View File

@ -0,0 +1,9 @@
#! [quoting library paths with spaces]
win32:LIBS += "C:/mylibs/extra libs/extra.lib"
unix:LIBS += "-L/home/user/extra libs" -lextra
#! [quoting library paths with spaces]
#! [quoting include paths with spaces]
win32:INCLUDEPATH += "C:/mylibs/extra headers"
unix:INCLUDEPATH += "/home/user/extra headers"
#! [quoting include paths with spaces]

View File

@ -0,0 +1,7 @@
#! [0]
message($$QMAKESPEC)
linux-g++ {
message(Linux)
}
#! [0]

View File

@ -0,0 +1,20 @@
#! [0]
defineTest(allFiles) {
files = $$ARGS
for(file, files) {
!exists($$file) {
return(false)
}
}
return(true)
}
#! [0]
files = delegate.h model.h view.h
allFiles($$files) {
message(All files are present: $$files)
} else {
message(Not all files are present: $$files)
}

View File

@ -0,0 +1,7 @@
#! [0]
HEADERS = mainwindow.h paintwidget.h
#! [0] #! [1]
SOURCES = main.cpp mainwindow.cpp \
paintwidget.cpp
CONFIG += console
#! [1]

View File

@ -0,0 +1,3 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PBUILDER_PBX_H
#define PBUILDER_PBX_H
#include "unixmake.h"
QT_BEGIN_NAMESPACE
class ProjectBuilderMakefileGenerator : public UnixMakefileGenerator
{
bool writingUnixMakefileGenerator;
mutable QString pbx_dir;
int pbuilderVersion() const;
bool writeSubDirs(QTextStream &);
bool writeMakeParts(QTextStream &);
bool writeMakefile(QTextStream &) override;
bool replaceLibrarySuffix(const QString &lib_file, const ProString &opt, QString &name,
QString &library);
QString pbxbuild();
QHash<QString, QString> keys;
QString keyFor(const QString &file);
QString findProgram(const ProString &prog);
QString fixForOutput(const QString &file);
ProStringList fixListForOutput(const char *where);
ProStringList fixListForOutput(const ProStringList &list);
int reftypeForFile(const QString &where);
QString projectSuffix() const;
enum { SettingsAsList=0x01, SettingsNoQuote=0x02 };
inline QString writeSettings(const QString &var, const char *val, int flags=0, int indent_level=0)
{ return writeSettings(var, ProString(val), flags, indent_level); }
inline QString writeSettings(const QString &var, const ProString &val, int flags=0, int indent_level=0)
{ return writeSettings(var, ProStringList(val), flags, indent_level); }
QString writeSettings(const QString &var, const ProStringList &vals, int flags=0, int indent_level=0);
public:
bool supportsMetaBuild() override { return false; }
bool openOutput(QFile &, const QString &) const override;
protected:
bool doPrecompiledHeaders() const override { return false; }
bool doDepends() const override { return writingUnixMakefileGenerator && UnixMakefileGenerator::doDepends(); }
bool inhibitMakeDirOutPath(const ProKey &path) const override;
};
QT_END_NAMESPACE
#endif // PBUILDER_PBX_H

File diff suppressed because it is too large Load Diff

314
qmake/generators/makefile.h Normal file
View File

@ -0,0 +1,314 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MAKEFILE_H
#define MAKEFILE_H
#include "option.h"
#include "project.h"
#include "makefiledeps.h"
#include <qtextstream.h>
#include <qlist.h>
#include <qhash.h>
#include <qfileinfo.h>
#include <functional>
QT_BEGIN_NAMESPACE
#ifdef Q_OS_WIN32
#define QT_POPEN _popen
#define QT_POPEN_READ "rb"
#define QT_PCLOSE _pclose
#else
#define QT_POPEN popen
#define QT_POPEN_READ "r"
#define QT_PCLOSE pclose
#endif
struct ReplaceExtraCompilerCacheKey;
class MakefileGenerator : protected QMakeSourceFileInfo
{
QString spec;
bool no_io = false;
bool resolveDependenciesInFrameworks = false;
QHash<QString, bool> init_compiler_already;
QString makedir, chkexists;
QString fullBuildArgs();
//internal caches
mutable QHash<QString, QMakeLocalFileName> depHeuristicsCache;
mutable QHash<QString, QStringList> dependsCache;
mutable QHash<ReplaceExtraCompilerCacheKey, QString> extraCompilerVariablesCache;
public:
// We can't make it visible to VCFilter in VS2008 except by making it public or directly friending it.
enum ReplaceFor { NoShell, LocalShell, TargetShell };
protected:
enum TARG_MODE { TARG_UNIX_MODE, TARG_MAC_MODE, TARG_WIN_MODE } target_mode;
ProStringList createObjectList(const ProStringList &sources);
//makefile style generator functions
void writeObj(QTextStream &, const char *src);
void writeInstalls(QTextStream &t, bool noBuild=false);
void writeHeader(QTextStream &t);
void writeSubDirs(QTextStream &t);
void writeMakeQmake(QTextStream &t, bool noDummyQmakeAll = false);
void writeExportedVariables(QTextStream &t);
void writeExtraVariables(QTextStream &t);
void writeExtraTargets(QTextStream &t);
QString resolveDependency(const QDir &outDir, const QString &file);
void callExtraCompilerDependCommand(const ProString &extraCompiler,
const QString &tmp_dep_cmd, const QString &inpf,
const QString &tmp_out, bool dep_lines, QStringList *deps,
bool existingDepsOnly,
bool checkCommandAvailability = false);
void writeExtraCompilerTargets(QTextStream &t);
void writeExtraCompilerVariables(QTextStream &t);
bool writeDummyMakefile(QTextStream &t);
virtual bool writeMakefile(QTextStream &t);
virtual void writeDefaultVariables(QTextStream &t);
QString pkgConfigPrefix() const;
QString pkgConfigFileName(bool fixify=true);
QString pkgConfigFixPath(QString) const;
void writePkgConfigFile(); // for pkg-config
//generating subtarget makefiles
struct SubTarget
{
QString name;
QString in_directory, out_directory;
QString profile, target, makefile;
ProStringList depends;
};
enum SubTargetFlags {
SubTargetInstalls=0x01,
SubTargetOrdered=0x02,
SubTargetSkipDefaultVariables=0x04,
SubTargetSkipDefaultTargets=0x08,
SubTargetsNoFlags=0x00
};
QList<MakefileGenerator::SubTarget*> findSubDirsSubTargets() const;
void writeSubTargetCall(QTextStream &t,
const QString &in_directory, const QString &in, const QString &out_directory, const QString &out,
const QString &out_directory_cdin, const QString &makefilein);
virtual void suppressBuiltinRules(QTextStream &t) const;
virtual void writeSubMakeCall(QTextStream &t, const QString &outDirectory_cdin,
const QString &makeFileIn);
virtual void writeSubTargets(QTextStream &t, QList<SubTarget*> subtargets, int flags);
virtual ProStringList extraSubTargetDependencies() { return {}; }
//extra compiler interface
bool verifyExtraCompiler(const ProString &c, const QString &f);
virtual QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &, ReplaceFor forShell);
inline QString replaceExtraCompilerVariables(const QString &val, const QString &in, const QString &out, ReplaceFor forShell)
{ return replaceExtraCompilerVariables(val, QStringList(in), QStringList(out), forShell); }
//interface to the source file info
QMakeLocalFileName fixPathForFile(const QMakeLocalFileName &, bool) override;
QMakeLocalFileName findFileForDep(const QMakeLocalFileName &, const QMakeLocalFileName &) override;
QFileInfo findFileInfo(const QMakeLocalFileName &) override;
QMakeProject *project = nullptr;
//escape
virtual QString escapeFilePath(const QString &path) const = 0;
ProString escapeFilePath(const ProString &path) const;
template<typename A, typename B>
QString escapeFilePath(const QStringBuilder<A, B> &path) const
{ return escapeFilePath(QString(path)); }
QStringList escapeFilePaths(const QStringList &paths) const;
ProStringList escapeFilePaths(const ProStringList &paths) const;
virtual QString escapeDependencyPath(const QString &path) const;
ProString escapeDependencyPath(const ProString &path) const;
template<typename A, typename B>
QString escapeDependencyPath(const QStringBuilder<A, B> &path) const
{ return escapeDependencyPath(QString(path)); }
QStringList escapeDependencyPaths(const QStringList &paths) const;
ProStringList escapeDependencyPaths(const ProStringList &paths) const;
QStringList finalizeDependencyPaths(const QStringList &paths) const;
//initialization
void verifyCompilers();
virtual void init();
void initOutPaths();
virtual bool inhibitMakeDirOutPath(const ProKey &path) const;
struct Compiler
{
QString variable_in;
enum CompilerFlag {
CompilerNoFlags = 0x00,
CompilerBuiltin = 0x01,
CompilerNoCheckDeps = 0x02,
CompilerRemoveNoExist = 0x04,
CompilerWarnNoExist = 0x08,
CompilerAddInputsAsMakefileDeps = 0x10
};
uint flags, type;
};
friend class QTypeInfo<Compiler>;
void initCompiler(const Compiler &comp);
enum VPATHFlag {
VPATH_NoFlag = 0x00,
VPATH_WarnMissingFiles = 0x01,
VPATH_RemoveMissingFiles = 0x02,
VPATH_NoFixify = 0x04
};
ProStringList findFilesInVPATH(ProStringList l, uchar flags, const QString &var="");
inline int findExecutable(const QStringList &cmdline)
{ int ret; canExecute(cmdline, &ret); return ret; }
bool canExecute(const QStringList &cmdline, int *argv0) const;
inline bool canExecute(const QString &cmdline) const
{ return canExecute(cmdline.split(' '), nullptr); }
bool mkdir(const QString &dir) const;
QString mkdir_p_asstring(const QString &dir, bool escape=true) const;
QString specdir();
//subclasses can use these to query information about how the generator was "run"
QString buildArgs(bool withExtra);
virtual QStringList &findDependencies(const QString &file);
virtual bool doDepends() const { return Option::mkfile::do_deps; }
void filterIncludedFiles(const char *);
void processSources();
//for installs
virtual QString defaultInstall(const QString &);
virtual QString installRoot() const;
//for prl
QString prlFileName(bool fixify=true);
void writePrlFile();
bool processPrlFile(QString &, bool baseOnly);
virtual void writePrlFile(QTextStream &);
//make sure libraries are found
virtual bool findLibraries(bool linkPrl, bool mergeLflags);
//for retrieving values and lists of values
virtual QString var(const ProKey &var) const;
QString varGlue(const ProKey &var, const QString &before, const QString &glue, const QString &after) const;
QString varList(const ProKey &var) const;
QString fixFileVarGlue(const ProKey &var, const QString &before, const QString &glue, const QString &after) const;
QString fileVarList(const ProKey &var) const;
QString fileVarGlue(const ProKey &var, const QString &before, const QString &glue, const QString &after) const;
QString fileVar(const ProKey &var) const;
QString depVar(const ProKey &var) const;
QString val(const ProStringList &varList) const;
QString val(const QStringList &varList) const;
QString valGlue(const QStringList &varList, const QString &before, const QString &glue, const QString &after) const;
QString valGlue(const ProStringList &varList, const QString &before, const QString &glue, const QString &after) const;
QString valList(const QStringList &varList) const;
QString valList(const ProStringList &varList) const;
QString filePrefixRoot(const QString &, const QString &);
enum LibFlagType { LibFlagLib, LibFlagPath, LibFlagFile, LibFlagOther };
virtual LibFlagType parseLibFlag(const ProString &flag, ProString *arg);
ProStringList fixLibFlags(const ProKey &var);
virtual ProString fixLibFlag(const ProString &lib);
public:
//file fixification to unify all file names into a single pattern
enum FileFixifyType {
FileFixifyFromIndir = 0,
FileFixifyFromOutdir = 1,
FileFixifyToOutDir = 0,
FileFixifyToIndir = 2,
FileFixifyBackwards = FileFixifyFromOutdir | FileFixifyToIndir,
FileFixifyDefault = 0,
FileFixifyAbsolute = 4,
FileFixifyRelative = 8
};
Q_DECLARE_FLAGS(FileFixifyTypes, FileFixifyType)
protected:
QString fileFixify(const QString &file, FileFixifyTypes fix = FileFixifyDefault, bool canon = true) const;
QStringList fileFixify(const QStringList &files, FileFixifyTypes fix = FileFixifyDefault, bool canon = true) const;
QString createSedArgs(const ProKey &replace_rule, const QString &file_type = QString()) const;
QString installMetaFile(const ProKey &replace_rule, const QString &src,
const QString &dst) const;
virtual bool processPrlFileBase(QString &origFile, QStringView origName,
QStringView fixedBase, int slashOff);
bool processPrlFileCore(QString &origFile, QStringView origName,
const QString &fixedFile);
QString createResponseFile(const QString &baseName,
const ProStringList &objList,
const QString &prefix = QString()) const;
struct LinkerResponseFileInfo
{
QString filePath;
bool onlyObjects;
bool isValid() const { return !filePath.isEmpty(); }
};
LinkerResponseFileInfo maybeCreateLinkerResponseFile() const;
public:
QMakeProject *projectFile() const;
void setProjectFile(QMakeProject *p);
void setNoIO(bool o);
bool noIO() const;
inline bool exists(QString file) const { return fileInfo(file).exists(); }
QFileInfo fileInfo(QString file) const;
static MakefileGenerator *create(QMakeProject *);
virtual bool write();
virtual bool writeProjectMakefile();
virtual bool supportsMetaBuild() { return true; }
virtual bool supportsMergedBuilds() { return false; }
virtual bool mergeBuildProject(MakefileGenerator * /*other*/) { return false; }
virtual bool openOutput(QFile &, const QString &build) const;
bool isWindowsShell() const { return Option::dir_sep == QLatin1String("\\"); }
QString shellQuote(const QString &str) const;
virtual ProKey fullTargetVariable() const;
};
Q_DECLARE_TYPEINFO(MakefileGenerator::Compiler, Q_RELOCATABLE_TYPE);
Q_DECLARE_OPERATORS_FOR_FLAGS(MakefileGenerator::FileFixifyTypes)
inline void MakefileGenerator::setNoIO(bool o)
{ no_io = o; }
inline bool MakefileGenerator::noIO() const
{ return no_io; }
inline QString MakefileGenerator::defaultInstall(const QString &)
{ return QString(""); }
inline QString MakefileGenerator::installRoot() const
{ return QStringLiteral("$(INSTALL_ROOT)"); }
inline bool MakefileGenerator::findLibraries(bool, bool)
{ return true; }
struct ReplaceExtraCompilerCacheKey
{
mutable size_t hash;
QString var, in, out, pwd;
MakefileGenerator::ReplaceFor forShell;
ReplaceExtraCompilerCacheKey(const QString &v, const QStringList &i, const QStringList &o, MakefileGenerator::ReplaceFor s);
bool operator==(const ReplaceExtraCompilerCacheKey &f) const;
inline size_t hashCode() const {
if (!hash)
hash = (size_t)forShell ^ qHash(var) ^ qHash(in) ^ qHash(out) /*^ qHash(pwd)*/;
return hash;
}
};
inline size_t qHash(const ReplaceExtraCompilerCacheKey &f) { return f.hashCode(); }
QT_END_NAMESPACE
#endif // MAKEFILE_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MAKEFILEDEPS_H
#define MAKEFILEDEPS_H
#include <proitems.h>
#include <qfileinfo.h>
#include <qlist.h>
#include <qstringlist.h>
QT_BEGIN_NAMESPACE
struct SourceFile;
struct SourceDependChildren;
class SourceFiles;
class QMakeLocalFileName
{
QString real_name;
mutable QString local_name;
public:
QMakeLocalFileName() = default;
QMakeLocalFileName(const QString &);
bool isNull() const { return real_name.isNull(); }
inline const QString &real() const { return real_name; }
const QString &local() const;
bool operator==(const QMakeLocalFileName &other) const {
return (this->real_name == other.real_name);
}
bool operator!=(const QMakeLocalFileName &other) const {
return !(*this == other);
}
};
class QMakeSourceFileInfo
{
private:
//quick project lookups
SourceFiles *files, *includes;
bool files_changed;
QList<QMakeLocalFileName> depdirs;
QStringList systemIncludes;
//sleezy buffer code
char *spare_buffer;
int spare_buffer_size;
char *getBuffer(int s);
//actual guts
bool findMocs(SourceFile *);
bool findDeps(SourceFile *);
void dependTreeWalker(SourceFile *, SourceDependChildren *);
protected:
virtual QMakeLocalFileName fixPathForFile(const QMakeLocalFileName &, bool forOpen=false);
virtual QMakeLocalFileName findFileForDep(const QMakeLocalFileName &, const QMakeLocalFileName &);
virtual QFileInfo findFileInfo(const QMakeLocalFileName &);
public:
QMakeSourceFileInfo();
virtual ~QMakeSourceFileInfo();
QList<QMakeLocalFileName> dependencyPaths() const { return depdirs; }
void setDependencyPaths(const QList<QMakeLocalFileName> &);
enum DependencyMode { Recursive, NonRecursive };
inline void setDependencyMode(DependencyMode mode) { dep_mode = mode; }
inline DependencyMode dependencyMode() const { return dep_mode; }
void setSystemIncludes(const ProStringList &list)
{ systemIncludes = list.toQStringList(); }
enum SourceFileType { TYPE_UNKNOWN, TYPE_C, TYPE_UI, TYPE_QRC };
enum SourceFileSeek { SEEK_DEPS=0x01, SEEK_MOCS=0x02 };
void addSourceFiles(const ProStringList &, uchar seek, SourceFileType type=TYPE_C);
void addSourceFile(const QString &, uchar seek, SourceFileType type=TYPE_C);
bool containsSourceFile(const QString &, SourceFileType type=TYPE_C);
bool isSystemInclude(const QString &);
int included(const QString &file);
QStringList dependencies(const QString &file);
bool mocable(const QString &file);
private:
DependencyMode dep_mode;
};
QT_END_NAMESPACE
#endif // MAKEFILEDEPS_H

View File

@ -0,0 +1,501 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "metamakefile.h"
#include "qdir.h"
#include "qdebug.h"
#include "makefile.h"
#include "project.h"
#include "cachekeys.h"
#include <algorithm>
#include <iterator>
#include <utility>
#define BUILDSMETATYPE 1
#define SUBDIRSMETATYPE 2
QT_BEGIN_NAMESPACE
MetaMakefileGenerator::~MetaMakefileGenerator()
{
if(own_project)
delete project;
}
#ifndef QT_QMAKE_PARSER_ONLY
class BuildsMetaMakefileGenerator : public MetaMakefileGenerator
{
private:
bool init_flag;
struct Build {
QString name, build;
MakefileGenerator *makefile;
};
QList<Build *> makefiles;
void clearBuilds();
MakefileGenerator *processBuild(const ProString &);
void accumulateVariableFromBuilds(const ProKey &name, Build *build) const;
void checkForConflictingTargets() const;
public:
BuildsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
~BuildsMetaMakefileGenerator() { clearBuilds(); }
bool init() override;
int type() const override { return BUILDSMETATYPE; }
bool write() override;
};
void
BuildsMetaMakefileGenerator::clearBuilds()
{
for(int i = 0; i < makefiles.size(); i++) {
Build *build = makefiles[i];
if(QMakeProject *p = build->makefile->projectFile()) {
if(p != project)
delete p;
}
delete build->makefile;
delete build;
}
makefiles.clear();
}
bool
BuildsMetaMakefileGenerator::init()
{
if(init_flag)
return false;
init_flag = true;
const ProStringList &builds = project->values("BUILDS");
bool use_single_build = builds.isEmpty();
if(builds.size() > 1 && Option::output.fileName() == "-") {
use_single_build = true;
warn_msg(WarnLogic, "Cannot direct to stdout when using multiple BUILDS.");
}
if(!use_single_build) {
for(int i = 0; i < builds.size(); i++) {
ProString build = builds[i];
MakefileGenerator *makefile = processBuild(build);
if(!makefile)
return false;
if(!makefile->supportsMetaBuild()) {
warn_msg(WarnLogic, "QMAKESPEC does not support multiple BUILDS.");
clearBuilds();
use_single_build = true;
break;
} else {
Build *b = new Build;
b->name = name;
if(builds.size() != 1)
b->build = build.toQString();
b->makefile = makefile;
makefiles += b;
}
}
}
if(use_single_build) {
Build *build = new Build;
build->name = name;
build->makefile = createMakefileGenerator(project, false);
if (build->makefile){
makefiles += build;
}else {
delete build;
return false;
}
}
return true;
}
bool
BuildsMetaMakefileGenerator::write()
{
Build *glue = nullptr;
if(!makefiles.isEmpty() && !makefiles.first()->build.isNull()
&& Option::qmake_mode != Option::QMAKE_GENERATE_PRL) {
glue = new Build;
glue->name = name;
glue->makefile = createMakefileGenerator(project, true);
makefiles += glue;
}
bool ret = true;
const QString &output_name = Option::output.fileName();
for(int i = 0; ret && i < makefiles.size(); i++) {
Option::output.setFileName(output_name);
Build *build = makefiles[i];
bool using_stdout = false;
if(build->makefile && (Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT)
&& (!build->makefile->supportsMergedBuilds()
|| (build->makefile->supportsMergedBuilds() && (!glue || build == glue)))) {
//open output
if(!(Option::output.isOpen())) {
if(Option::output.fileName() == "-") {
Option::output.setFileName("");
Option::output_dir = qmake_getpwd();
Option::output.open(stdout, QIODevice::WriteOnly | QIODevice::Text);
using_stdout = true;
} else {
if(Option::output.fileName().isEmpty() &&
Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE)
Option::output.setFileName(project->first("QMAKE_MAKEFILE").toQString());
QString build_name = build->name;
if(!build->build.isEmpty()) {
if(!build_name.isEmpty())
build_name += ".";
build_name += build->build;
}
if(!build->makefile->openOutput(Option::output, build_name)) {
fprintf(stderr, "Failure to open file: %s\n",
Option::output.fileName().isEmpty() ? "(stdout)" :
Option::output.fileName().toLatin1().constData());
return false;
}
}
}
} else {
using_stdout = true; //kind of..
}
if(!build->makefile) {
ret = false;
} else if(build == glue) {
checkForConflictingTargets();
accumulateVariableFromBuilds("QMAKE_INTERNAL_INCLUDED_FILES", build);
ret = build->makefile->writeProjectMakefile();
} else {
ret = build->makefile->write();
if (glue && glue->makefile->supportsMergedBuilds())
ret = glue->makefile->mergeBuildProject(build->makefile);
}
if(!using_stdout) {
Option::output.close();
if(!ret)
Option::output.remove();
}
}
return ret;
}
MakefileGenerator
*BuildsMetaMakefileGenerator::processBuild(const ProString &build)
{
if(project) {
debug_msg(1, "Meta Generator: Parsing '%s' for build [%s].",
project->projectFile().toLatin1().constData(),build.toLatin1().constData());
//initialize the base
ProValueMap basevars;
ProStringList basecfgs = project->values(ProKey(build + ".CONFIG"));
basecfgs += build;
basecfgs += "build_pass";
basevars["BUILD_PASS"] = ProStringList(build);
ProStringList buildname = project->values(ProKey(build + ".name"));
basevars["BUILD_NAME"] = (buildname.isEmpty() ? ProStringList(build) : buildname);
//create project
QMakeProject *build_proj = new QMakeProject;
build_proj->setExtraVars(basevars);
build_proj->setExtraConfigs(basecfgs);
if (build_proj->read(project->projectFile()))
return createMakefileGenerator(build_proj);
}
return nullptr;
}
void BuildsMetaMakefileGenerator::accumulateVariableFromBuilds(const ProKey &name, Build *dst) const
{
ProStringList &values = dst->makefile->projectFile()->values(name);
for (auto build : makefiles) {
if (build != dst)
values += build->makefile->projectFile()->values(name);
}
values.removeDuplicates();
}
void BuildsMetaMakefileGenerator::checkForConflictingTargets() const
{
if (makefiles.size() < 3) {
// Checking for conflicts only makes sense if we have more than one BUILD,
// and the last entry in makefiles is the "glue" Build.
return;
}
if (!project->isActiveConfig("build_all")) {
// Only complain if we're about to build all configurations.
return;
}
using TargetInfo = std::pair<Build *, ProString>;
QList<TargetInfo> targets;
const int last = makefiles.size() - 1;
targets.resize(last);
for (int i = 0; i < last; ++i) {
Build *b = makefiles.at(i);
auto mkf = b->makefile;
auto prj = mkf->projectFile();
targets[i] = std::make_pair(b, prj->first(mkf->fullTargetVariable()));
}
std::stable_sort(targets.begin(), targets.end(),
[](const TargetInfo &lhs, const TargetInfo &rhs)
{
return lhs.second < rhs.second;
});
for (auto prev = targets.begin(), it = std::next(prev); it != targets.end(); ++prev, ++it) {
if (prev->second == it->second) {
warn_msg(WarnLogic, "Targets of builds '%s' and '%s' conflict: %s.",
qPrintable(prev->first->build),
qPrintable(it->first->build),
qPrintable(prev->second.toQString()));
break;
}
}
}
class SubdirsMetaMakefileGenerator : public MetaMakefileGenerator
{
protected:
bool init_flag;
struct Subdir {
Subdir() : makefile(nullptr), indent(0) { }
~Subdir() { delete makefile; }
QString input_dir;
QString output_dir, output_file;
MetaMakefileGenerator *makefile;
int indent;
};
QList<Subdir *> subs;
MakefileGenerator *processBuild(const QString &);
public:
SubdirsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { }
~SubdirsMetaMakefileGenerator();
bool init() override;
int type() const override { return SUBDIRSMETATYPE; }
bool write() override;
};
bool
SubdirsMetaMakefileGenerator::init()
{
if(init_flag)
return false;
init_flag = true;
bool hasError = false;
bool recurse = Option::recursive;
if (recurse && project->isActiveConfig("dont_recurse"))
recurse = false;
if(recurse) {
QString old_output_dir = Option::output_dir;
QString old_output = Option::output.fileName();
QString oldpwd = qmake_getpwd();
QString thispwd = oldpwd;
if(!thispwd.endsWith('/'))
thispwd += '/';
const ProStringList &subdirs = project->values("SUBDIRS");
Q_CONSTINIT static int recurseDepth = -1;
++recurseDepth;
for(int i = 0; i < subdirs.size(); ++i) {
Subdir *sub = new Subdir;
sub->indent = recurseDepth;
QFileInfo subdir(subdirs.at(i).toQString());
const ProKey fkey(subdirs.at(i) + ".file");
if (!project->isEmpty(fkey)) {
subdir = QFileInfo(project->first(fkey).toQString());
} else {
const ProKey skey(subdirs.at(i) + ".subdir");
if (!project->isEmpty(skey))
subdir = QFileInfo(project->first(skey).toQString());
}
QString sub_name;
if(subdir.isDir())
subdir = QFileInfo(subdir.filePath() + "/" + subdir.fileName() + Option::pro_ext);
else
sub_name = subdir.baseName();
if(!subdir.isRelative()) { //we can try to make it relative
QString subdir_path = subdir.filePath();
if(subdir_path.startsWith(thispwd))
subdir = QFileInfo(subdir_path.mid(thispwd.size()));
}
//handle sub project
QMakeProject *sub_proj = new QMakeProject;
for (int ind = 0; ind < sub->indent; ++ind)
printf(" ");
sub->input_dir = subdir.absolutePath();
if(subdir.isRelative() && old_output_dir != oldpwd) {
sub->output_dir = old_output_dir + (subdir.path() != "." ? "/" + subdir.path() : QString());
printf("Reading %s [%s]\n", subdir.absoluteFilePath().toLatin1().constData(), sub->output_dir.toLatin1().constData());
} else { //what about shadow builds?
sub->output_dir = sub->input_dir;
printf("Reading %s\n", subdir.absoluteFilePath().toLatin1().constData());
}
qmake_setpwd(sub->input_dir);
Option::output_dir = sub->output_dir;
bool tmpError = !sub_proj->read(subdir.fileName());
if (!sub_proj->isEmpty("QMAKE_FAILED_REQUIREMENTS")) {
fprintf(stderr, "Project file(%s) not recursed because all requirements not met:\n\t%s\n",
subdir.fileName().toLatin1().constData(),
sub_proj->values("QMAKE_FAILED_REQUIREMENTS").join(' ').toLatin1().constData());
delete sub;
delete sub_proj;
Option::output_dir = old_output_dir;
qmake_setpwd(oldpwd);
continue;
} else {
hasError |= tmpError;
}
sub->makefile = MetaMakefileGenerator::createMetaGenerator(sub_proj, sub_name);
const QString output_name = Option::output.fileName();
Option::output.setFileName(sub->output_file);
hasError |= !sub->makefile->write();
delete sub;
qmakeClearCaches();
sub = nullptr;
Option::output.setFileName(output_name);
Option::output_dir = old_output_dir;
qmake_setpwd(oldpwd);
}
--recurseDepth;
Option::output.setFileName(old_output);
Option::output_dir = old_output_dir;
qmake_setpwd(oldpwd);
}
Subdir *self = new Subdir;
self->input_dir = qmake_getpwd();
self->output_dir = Option::output_dir;
if(!recurse || (!Option::output.fileName().endsWith(Option::dir_sep) && !QFileInfo(Option::output).isDir()))
self->output_file = Option::output.fileName();
self->makefile = new BuildsMetaMakefileGenerator(project, name, false);
self->makefile->init();
subs.append(self);
return !hasError;
}
bool
SubdirsMetaMakefileGenerator::write()
{
bool ret = true;
const QString &pwd = qmake_getpwd();
const QString &output_dir = Option::output_dir;
const QString &output_name = Option::output.fileName();
for(int i = 0; ret && i < subs.size(); i++) {
const Subdir *sub = subs.at(i);
qmake_setpwd(sub->input_dir);
Option::output_dir = QFileInfo(sub->output_dir).absoluteFilePath();
Option::output.setFileName(sub->output_file);
if(i != subs.size()-1) {
for (int ind = 0; ind < sub->indent; ++ind)
printf(" ");
printf("Writing %s\n", QDir::cleanPath(Option::output_dir+"/"+
Option::output.fileName()).toLatin1().constData());
}
if (!(ret = sub->makefile->write()))
break;
//restore because I'm paranoid
qmake_setpwd(pwd);
Option::output.setFileName(output_name);
Option::output_dir = output_dir;
}
return ret;
}
SubdirsMetaMakefileGenerator::~SubdirsMetaMakefileGenerator()
{
for(int i = 0; i < subs.size(); i++)
delete subs[i];
subs.clear();
}
//Factory things
QT_BEGIN_INCLUDE_NAMESPACE
#include "unixmake.h"
#include "mingw_make.h"
#include "projectgenerator.h"
#include "pbuilder_pbx.h"
#include "msvc_nmake.h"
#include "msvc_vcproj.h"
#include "msvc_vcxproj.h"
QT_END_INCLUDE_NAMESPACE
MakefileGenerator *
MetaMakefileGenerator::createMakefileGenerator(QMakeProject *proj, bool noIO)
{
Option::postProcessProject(proj);
MakefileGenerator *mkfile = nullptr;
if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
mkfile = new ProjectGenerator;
mkfile->setProjectFile(proj);
return mkfile;
}
ProString gen = proj->first("MAKEFILE_GENERATOR");
if(gen.isEmpty()) {
fprintf(stderr, "MAKEFILE_GENERATOR variable not set as a result of parsing : %s. Possibly qmake was not able to find files included using \"include(..)\" - enable qmake debugging to investigate more.\n",
proj->projectFile().toLatin1().constData());
} else if(gen == "UNIX") {
mkfile = new UnixMakefileGenerator;
} else if(gen == "MINGW") {
mkfile = new MingwMakefileGenerator;
} else if(gen == "PROJECTBUILDER" || gen == "XCODE") {
#ifdef Q_CC_MSVC
fprintf(stderr, "Generating Xcode projects is not supported with an MSVC build of Qt.\n");
#else
mkfile = new ProjectBuilderMakefileGenerator;
#endif
} else if(gen == "MSVC.NET") {
if (proj->first("TEMPLATE").startsWith("vc"))
mkfile = new VcprojGenerator;
else
mkfile = new NmakeMakefileGenerator;
} else if(gen == "MSBUILD") {
// Visual Studio >= v11.0
if (proj->first("TEMPLATE").startsWith("vc"))
mkfile = new VcxprojGenerator;
else
mkfile = new NmakeMakefileGenerator;
} else {
fprintf(stderr, "Unknown generator specified: %s\n", gen.toLatin1().constData());
}
if (mkfile) {
mkfile->setNoIO(noIO);
mkfile->setProjectFile(proj);
}
return mkfile;
}
MetaMakefileGenerator *
MetaMakefileGenerator::createMetaGenerator(QMakeProject *proj, const QString &name, bool op, bool *success)
{
Option::postProcessProject(proj);
MetaMakefileGenerator *ret = nullptr;
if ((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
Option::qmake_mode == Option::QMAKE_GENERATE_PRL)) {
if (proj->first("TEMPLATE").endsWith("subdirs"))
ret = new SubdirsMetaMakefileGenerator(proj, name, op);
}
if (!ret)
ret = new BuildsMetaMakefileGenerator(proj, name, op);
bool res = ret->init();
if (success)
*success = res;
return ret;
}
#endif // QT_QMAKE_PARSER_ONLY
QT_END_NAMESPACE

View File

@ -0,0 +1,39 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef METAMAKEFILE_H
#define METAMAKEFILE_H
#include <qlist.h>
#include <qstring.h>
QT_BEGIN_NAMESPACE
class QMakeProject;
class MakefileGenerator;
class MetaMakefileGenerator
{
protected:
MetaMakefileGenerator(QMakeProject *p, const QString &n, bool op=true) : project(p), own_project(op), name(n) { }
QMakeProject *project;
bool own_project;
QString name;
public:
virtual ~MetaMakefileGenerator();
static MetaMakefileGenerator *createMetaGenerator(QMakeProject *proj, const QString &name, bool op=true, bool *success = nullptr);
static MakefileGenerator *createMakefileGenerator(QMakeProject *proj, bool noIO = false);
inline QMakeProject *projectFile() const { return project; }
virtual bool init() = 0;
virtual int type() const { return -1; }
virtual bool write() = 0;
};
QT_END_NAMESPACE
#endif // METAMAKEFILE_H

View File

@ -0,0 +1,462 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "projectgenerator.h"
#include "option.h"
#include <qdatetime.h>
#include <qdir.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qregularexpression.h>
QT_BEGIN_NAMESPACE
static QString project_builtin_regx() //calculate the builtin regular expression..
{
QString ret;
QStringList builtin_exts;
builtin_exts << Option::c_ext << Option::ui_ext << Option::yacc_ext << Option::lex_ext << ".ts" << ".xlf" << ".qrc";
builtin_exts += Option::h_ext + Option::cpp_ext;
for(int i = 0; i < builtin_exts.size(); ++i) {
if(!ret.isEmpty())
ret += "; ";
ret += QString("*") + builtin_exts[i];
}
return ret;
}
void
ProjectGenerator::init()
{
int file_count = 0;
verifyCompilers();
project->loadSpec();
project->evaluateFeatureFile("default_pre.prf");
project->evaluateFeatureFile("default_post.prf");
project->evaluateConfigFeatures();
project->values("CONFIG").clear();
Option::postProcessProject(project);
ProValueMap &v = project->variables();
QString templ = Option::globals->user_template.isEmpty() ? QString("app") : Option::globals->user_template;
if (!Option::globals->user_template_prefix.isEmpty())
templ.prepend(Option::globals->user_template_prefix);
v["TEMPLATE_ASSIGN"] += templ;
//the scary stuff
if(project->first("TEMPLATE_ASSIGN") != "subdirs") {
QString builtin_regex = project_builtin_regx();
QStringList dirs = Option::projfile::project_dirs;
if(Option::projfile::do_pwd) {
if(!v["INCLUDEPATH"].contains("."))
v["INCLUDEPATH"] += ".";
dirs.prepend(qmake_getpwd());
}
for(int i = 0; i < dirs.size(); ++i) {
QString dir, regex, pd = dirs.at(i);
bool add_depend = false;
if(exists(pd)) {
QFileInfo fi(fileInfo(pd));
if(fi.isDir()) {
dir = pd;
add_depend = true;
if(dir.right(1) != Option::dir_sep)
dir += Option::dir_sep;
if (Option::recursive) {
QStringList files = QDir(dir).entryList(QDir::Files);
for (int i = 0; i < files.size(); i++)
dirs.append(dir + files[i] + QDir::separator() + builtin_regex);
}
regex = builtin_regex;
} else {
QString file = pd;
int s = file.lastIndexOf(Option::dir_sep);
if(s != -1)
dir = file.left(s+1);
if(addFile(file)) {
add_depend = true;
file_count++;
}
}
} else { //regexp
regex = pd;
}
if(!regex.isEmpty()) {
int s = regex.lastIndexOf(Option::dir_sep);
if(s != -1) {
dir = regex.left(s+1);
regex = regex.right(regex.size() - (s+1));
}
const QDir d(dir);
if (Option::recursive) {
QStringList entries = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
for (int i = 0; i < entries.size(); i++)
dirs.append(dir + entries[i] + QDir::separator() + regex);
}
QStringList files = d.entryList(QDir::nameFiltersFromString(regex));
for(int i = 0; i < (int)files.size(); i++) {
QString file = d.absoluteFilePath(files[i]);
if (addFile(file)) {
add_depend = true;
file_count++;
}
}
}
if(add_depend && !dir.isEmpty() && !v["DEPENDPATH"].contains(dir, Qt::CaseInsensitive)) {
QFileInfo fi(fileInfo(dir));
if(fi.absoluteFilePath() != qmake_getpwd())
v["DEPENDPATH"] += fileFixify(dir);
}
}
}
if(!file_count) { //shall we try a subdir?
QStringList knownDirs = Option::projfile::project_dirs;
if(Option::projfile::do_pwd)
knownDirs.prepend(".");
const QString out_file = fileFixify(Option::output.fileName());
for(int i = 0; i < knownDirs.size(); ++i) {
QString pd = knownDirs.at(i);
if(exists(pd)) {
QString newdir = pd;
QFileInfo fi(fileInfo(newdir));
if(fi.isDir()) {
newdir = fileFixify(newdir, FileFixifyFromOutdir);
ProStringList &subdirs = v["SUBDIRS"];
if(exists(fi.filePath() + QDir::separator() + fi.fileName() + Option::pro_ext) &&
!subdirs.contains(newdir, Qt::CaseInsensitive)) {
subdirs.append(newdir);
} else {
QStringList profiles = QDir(newdir).entryList(QStringList("*" + Option::pro_ext), QDir::Files);
for(int i = 0; i < (int)profiles.size(); i++) {
QString nd = newdir;
if(nd == ".")
nd = "";
else if (!nd.isEmpty() && !nd.endsWith(QDir::separator()))
nd += QDir::separator();
nd += profiles[i];
fileFixify(nd);
if (!subdirs.contains(nd, Qt::CaseInsensitive) && !out_file.endsWith(nd))
subdirs.append(nd);
}
}
if (Option::recursive) {
QStringList dirs = QDir(newdir).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
for(int i = 0; i < (int)dirs.size(); i++) {
QString nd = fileFixify(newdir + QDir::separator() + dirs[i]);
if (!knownDirs.contains(nd, Qt::CaseInsensitive))
knownDirs.append(nd);
}
}
}
} else { //regexp
QString regx = pd, dir;
int s = regx.lastIndexOf(Option::dir_sep);
if(s != -1) {
dir = regx.left(s+1);
regx = regx.right(regx.size() - (s+1));
}
QStringList files = QDir(dir).entryList(QDir::nameFiltersFromString(regx),
QDir::Dirs | QDir::NoDotAndDotDot);
ProStringList &subdirs = v["SUBDIRS"];
for(int i = 0; i < (int)files.size(); i++) {
QString newdir(dir + files[i]);
QFileInfo fi(fileInfo(newdir));
{
newdir = fileFixify(newdir);
if(exists(fi.filePath() + QDir::separator() + fi.fileName() + Option::pro_ext) &&
!subdirs.contains(newdir)) {
subdirs.append(newdir);
} else {
QStringList profiles = QDir(newdir).entryList(QStringList("*" + Option::pro_ext), QDir::Files);
for(int i = 0; i < (int)profiles.size(); i++) {
QString nd = newdir + QDir::separator() + files[i];
fileFixify(nd);
if(files[i] != "." && files[i] != ".." && !subdirs.contains(nd, Qt::CaseInsensitive)) {
if(newdir + files[i] != Option::output_dir + Option::output.fileName())
subdirs.append(nd);
}
}
}
if (Option::recursive && !knownDirs.contains(newdir, Qt::CaseInsensitive))
knownDirs.append(newdir);
}
}
}
}
v["TEMPLATE_ASSIGN"] = ProStringList("subdirs");
return;
}
//setup deplist
QList<QMakeLocalFileName> deplist;
{
const ProStringList &d = v["DEPENDPATH"];
for(int i = 0; i < d.size(); ++i)
deplist.append(QMakeLocalFileName(d[i].toQString()));
}
setDependencyPaths(deplist);
ProStringList &h = v["HEADERS"];
bool no_qt_files = true;
static const char *srcs[] = { "SOURCES", "YACCSOURCES", "LEXSOURCES", "FORMS", nullptr };
for (int i = 0; srcs[i]; i++) {
const ProStringList &l = v[srcs[i]];
QMakeSourceFileInfo::SourceFileType type = QMakeSourceFileInfo::TYPE_C;
QMakeSourceFileInfo::addSourceFiles(l, QMakeSourceFileInfo::SEEK_DEPS, type);
for(int i = 0; i < l.size(); ++i) {
QStringList tmp = QMakeSourceFileInfo::dependencies(l.at(i).toQString());
if(!tmp.isEmpty()) {
for(int dep_it = 0; dep_it < tmp.size(); ++dep_it) {
QString dep = tmp[dep_it];
dep = fixPathToQmake(dep);
QString file_dir = dep.section(Option::dir_sep, 0, -2),
file_no_path = dep.section(Option::dir_sep, -1);
if(!file_dir.isEmpty()) {
for(int inc_it = 0; inc_it < deplist.size(); ++inc_it) {
QMakeLocalFileName inc = deplist[inc_it];
if(inc.local() == file_dir && !v["INCLUDEPATH"].contains(inc.real(), Qt::CaseInsensitive))
v["INCLUDEPATH"] += inc.real();
}
}
if (no_qt_files && file_no_path.contains(QRegularExpression("^q[a-z_0-9].h$")))
no_qt_files = false;
QString h_ext;
for(int hit = 0; hit < Option::h_ext.size(); ++hit) {
if(dep.endsWith(Option::h_ext.at(hit))) {
h_ext = Option::h_ext.at(hit);
break;
}
}
if(!h_ext.isEmpty()) {
for(int cppit = 0; cppit < Option::cpp_ext.size(); ++cppit) {
QString src(dep.left(dep.size() - h_ext.size()) +
Option::cpp_ext.at(cppit));
if(exists(src)) {
ProStringList &srcl = v["SOURCES"];
if(!srcl.contains(src, Qt::CaseInsensitive))
srcl.append(src);
}
}
} else if(dep.endsWith(Option::lex_ext) &&
file_no_path.startsWith(Option::lex_mod)) {
addConfig("lex_included");
}
if(!h.contains(dep, Qt::CaseInsensitive))
h += dep;
}
}
}
}
//strip out files that are actually output from internal compilers (ie temporary files)
const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
QString tmp_out = project->first(ProKey(*it + ".output")).toQString();
if(tmp_out.isEmpty())
continue;
ProStringList var_out = project->values(ProKey(*it + ".variable_out"));
bool defaults = var_out.isEmpty();
for(int i = 0; i < var_out.size(); ++i) {
ProString v = var_out.at(i);
if(v.startsWith("GENERATED_")) {
defaults = true;
break;
}
}
if(defaults) {
var_out << "SOURCES";
var_out << "HEADERS";
var_out << "FORMS";
}
const ProStringList &tmp = project->values(ProKey(*it + ".input"));
for (ProStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) {
ProStringList &inputs = project->values((*it2).toKey());
for (ProStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) {
QString path = replaceExtraCompilerVariables(tmp_out, (*input).toQString(), QString(), NoShell);
path = fixPathToQmake(path).section('/', -1);
for(int i = 0; i < var_out.size(); ++i) {
ProString v = var_out.at(i);
ProStringList &list = project->values(v.toKey());
for(int src = 0; src < list.size(); ) {
if(list[src] == path || list[src].endsWith("/" + path))
list.removeAt(src);
else
++src;
}
}
}
}
}
}
bool
ProjectGenerator::writeMakefile(QTextStream &t)
{
t << "######################################################################" << Qt::endl;
t << "# Automatically generated by qmake (" QMAKE_VERSION_STR ") " << QDateTime::currentDateTime().toString() << Qt::endl;
t << "######################################################################" << Qt::endl << Qt::endl;
if (!Option::globals->extra_cmds[QMakeEvalBefore].isEmpty())
t << Option::globals->extra_cmds[QMakeEvalBefore] << Qt::endl;
t << getWritableVar("TEMPLATE_ASSIGN", false);
if(project->first("TEMPLATE_ASSIGN") == "subdirs") {
t << Qt::endl << "# Directories" << "\n"
<< getWritableVar("SUBDIRS");
} else {
//figure out target
QString ofn = QFileInfo(static_cast<QFile *>(t.device())->fileName()).completeBaseName();
if (ofn.isEmpty() || ofn == "-")
ofn = "unknown";
project->values("TARGET_ASSIGN") = ProStringList(ofn);
t << getWritableVar("TARGET_ASSIGN")
<< getWritableVar("CONFIG", false)
<< getWritableVar("CONFIG_REMOVE", false)
<< getWritableVar("INCLUDEPATH") << Qt::endl;
t << "# You can make your code fail to compile if you use deprecated APIs.\n"
"# In order to do so, uncomment the following line.\n"
"# Please consult the documentation of the deprecated API in order to know\n"
"# how to port your code away from it.\n"
"# You can also select to disable deprecated APIs only up to a certain version of Qt.\n"
"#DEFINES += QT_DISABLE_DEPRECATED_UP_TO=0x060000 # disables all APIs deprecated in Qt 6.0.0 and earlier\n\n";
t << "# Input" << "\n";
t << getWritableVar("HEADERS")
<< getWritableVar("FORMS")
<< getWritableVar("LEXSOURCES")
<< getWritableVar("YACCSOURCES")
<< getWritableVar("SOURCES")
<< getWritableVar("RESOURCES")
<< getWritableVar("TRANSLATIONS");
}
if (!Option::globals->extra_cmds[QMakeEvalAfter].isEmpty())
t << Option::globals->extra_cmds[QMakeEvalAfter] << Qt::endl;
return true;
}
bool
ProjectGenerator::addConfig(const QString &cfg, bool add)
{
ProKey where = "CONFIG";
if(!add)
where = "CONFIG_REMOVE";
if (!project->values(where).contains(cfg)) {
project->values(where) += cfg;
return true;
}
return false;
}
bool
ProjectGenerator::addFile(QString file)
{
file = fileFixify(file, FileFixifyToIndir);
QString dir;
int s = file.lastIndexOf(Option::dir_sep);
if(s != -1)
dir = file.left(s+1);
if(file.mid(dir.size(), Option::h_moc_mod.size()) == Option::h_moc_mod)
return false;
ProKey where;
for(int cppit = 0; cppit < Option::cpp_ext.size(); ++cppit) {
if(file.endsWith(Option::cpp_ext[cppit])) {
where = "SOURCES";
break;
}
}
if(where.isEmpty()) {
for(int hit = 0; hit < Option::h_ext.size(); ++hit)
if(file.endsWith(Option::h_ext.at(hit))) {
where = "HEADERS";
break;
}
}
if(where.isEmpty()) {
for(int cit = 0; cit < Option::c_ext.size(); ++cit) {
if(file.endsWith(Option::c_ext[cit])) {
where = "SOURCES";
break;
}
}
}
if(where.isEmpty()) {
if(file.endsWith(Option::ui_ext))
where = "FORMS";
else if(file.endsWith(Option::lex_ext))
where = "LEXSOURCES";
else if(file.endsWith(Option::yacc_ext))
where = "YACCSOURCES";
else if(file.endsWith(".ts") || file.endsWith(".xlf"))
where = "TRANSLATIONS";
else if(file.endsWith(".qrc"))
where = "RESOURCES";
}
QString newfile = fixPathToQmake(fileFixify(file));
ProStringList &endList = project->values(where);
if(!endList.contains(newfile, Qt::CaseInsensitive)) {
endList += newfile;
return true;
}
return false;
}
QString
ProjectGenerator::getWritableVar(const char *vk, bool)
{
const ProKey v(vk);
ProStringList &vals = project->values(v);
if(vals.isEmpty())
return "";
// If values contain spaces, ensure that they are quoted
for (ProStringList::iterator it = vals.begin(); it != vals.end(); ++it) {
if ((*it).contains(' ') && !(*it).startsWith(' '))
*it = "\"" + *it + "\"";
}
QString ret;
if(v.endsWith("_REMOVE"))
ret = v.left(v.length() - 7) + " -= ";
else if(v.endsWith("_ASSIGN"))
ret = v.left(v.length() - 7) + " = ";
else
ret = v + " += ";
QString join = vals.join(' ');
if(ret.size() + join.size() > 80) {
QString spaces;
for(int i = 0; i < ret.size(); i++)
spaces += " ";
join = vals.join(" \\\n" + spaces);
}
return ret + join + "\n";
}
bool
ProjectGenerator::openOutput(QFile &file, const QString &build) const
{
ProString fileName = file.fileName();
if (!fileName.endsWith(Option::pro_ext)) {
if (fileName.isEmpty())
fileName = fileInfo(Option::output_dir).fileName();
file.setFileName(fileName + Option::pro_ext);
}
return MakefileGenerator::openOutput(file, build);
}
QString
ProjectGenerator::fixPathToQmake(const QString &file)
{
QString ret = file;
if(Option::dir_sep != QLatin1String("/"))
ret.replace(Option::dir_sep, QLatin1String("/"));
return ret;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,30 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PROJECTGENERATOR_H
#define PROJECTGENERATOR_H
#include "makefile.h"
QT_BEGIN_NAMESPACE
class ProjectGenerator : public MakefileGenerator
{
bool addFile(QString);
bool addConfig(const QString &, bool add=true);
QString getWritableVar(const char *, bool fixPath=true);
QString fixPathToQmake(const QString &file);
protected:
void init() override;
bool writeMakefile(QTextStream &) override;
QString escapeFilePath(const QString &) const override { Q_ASSERT(false); return QString(); }
public:
bool supportsMetaBuild() override { return false; }
bool openOutput(QFile &, const QString &) const override;
};
QT_END_NAMESPACE
#endif // PROJECTGENERATOR_H

View File

@ -0,0 +1,750 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "unixmake.h"
#include "option.h"
#include <qfile.h>
#include <qhash.h>
#include <qdir.h>
#include <time.h>
#include <qdebug.h>
QT_BEGIN_NAMESPACE
ProStringList UnixMakefileGenerator::libdirToFlags(const ProKey &key)
{
ProStringList results;
for (const auto &libdir : std::as_const(project->values(key))) {
if (!project->isEmpty("QMAKE_LFLAGS_RPATH") && project->isActiveConfig("rpath_libdirs"))
project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATH") + libdir;
results.append("-L" + escapeFilePath(libdir));
}
return results;
}
void
UnixMakefileGenerator::init()
{
ProStringList &configs = project->values("CONFIG");
if(project->isEmpty("ICON") && !project->isEmpty("RC_FILE"))
project->values("ICON") = project->values("RC_FILE");
if(project->isEmpty("QMAKE_EXTENSION_PLUGIN"))
project->values("QMAKE_EXTENSION_PLUGIN").append(project->first("QMAKE_EXTENSION_SHLIB"));
project->values("QMAKE_ORIG_TARGET") = project->values("TARGET");
//version handling
if (project->isEmpty("VERSION")) {
project->values("VERSION").append(
"1.0." + (project->isEmpty("VER_PAT") ? QString("0") : project->first("VER_PAT")));
}
QStringList l = project->first("VERSION").toQString().split('.');
l << "0" << "0"; //make sure there are three
project->values("VER_MAJ").append(l[0]);
project->values("VER_MIN").append(l[1]);
project->values("VER_PAT").append(l[2]);
QString sroot = project->sourceRoot();
for (const ProString &iif : project->values("QMAKE_INTERNAL_INCLUDED_FILES")) {
if (iif == project->cacheFile())
continue;
if (iif.startsWith(sroot) && iif.at(sroot.size()) == QLatin1Char('/'))
project->values("DISTFILES") += fileFixify(iif.toQString(), FileFixifyRelative);
}
/* this should probably not be here, but I'm using it to wrap the .t files */
if(project->first("TEMPLATE") == "app")
project->values("QMAKE_APP_FLAG").append("1");
else if(project->first("TEMPLATE") == "lib")
project->values("QMAKE_LIB_FLAG").append("1");
else if(project->first("TEMPLATE") == "subdirs") {
MakefileGenerator::init();
if(project->isEmpty("MAKEFILE"))
project->values("MAKEFILE").append("Makefile");
return; /* subdirs is done */
}
project->values("QMAKE_ORIG_DESTDIR") = project->values("DESTDIR");
if((!project->isEmpty("QMAKE_LIB_FLAG") && !project->isActiveConfig("staticlib")) ||
(project->isActiveConfig("qt") && project->isActiveConfig("plugin"))) {
if(configs.indexOf("dll") == -1) configs.append("dll");
} else if(!project->isEmpty("QMAKE_APP_FLAG") || project->isActiveConfig("dll")) {
configs.removeAll("staticlib");
}
if(!project->isEmpty("QMAKE_INCREMENTAL"))
project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_INCREMENTAL");
else if(!project->isEmpty("QMAKE_LFLAGS_PREBIND") &&
!project->values("QMAKE_LIB_FLAG").isEmpty() &&
project->isActiveConfig("dll"))
project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_PREBIND");
project->values("QMAKE_INCDIR") += project->values("QMAKE_INCDIR_POST");
project->values("QMAKE_RPATHDIR") += project->values("QMAKE_RPATHDIR_POST");
project->values("QMAKE_RPATHLINKDIR") += project->values("QMAKE_RPATHLINKDIR_POST");
if(!project->isEmpty("QMAKE_INCDIR"))
project->values("INCLUDEPATH") += project->values("QMAKE_INCDIR");
// The order of the next two lines is relevant due to side effect on QMAKE_LFLAGS.
ProStringList ldadd = project->values("QMAKE_LIBDIR_FLAGS") + libdirToFlags("QMAKE_LIBDIR");
ProStringList ldaddpost = libdirToFlags("QMAKE_LIBDIR_POST");
if (project->isActiveConfig("mac")) {
if (!project->isEmpty("QMAKE_FRAMEWORKPATH")) {
const ProStringList &fwdirs = project->values("QMAKE_FRAMEWORKPATH");
for (int i = 0; i < fwdirs.size(); ++i)
project->values("QMAKE_FRAMEWORKPATH_FLAGS") += "-F" + escapeFilePath(fwdirs[i]);
}
ldadd += project->values("QMAKE_FRAMEWORKPATH_FLAGS");
}
ProStringList &qmklibs = project->values("LIBS");
qmklibs = ldadd + qmklibs;
ProStringList &qmklibspost = project->values("QMAKE_LIBS");
qmklibspost = ldaddpost + qmklibspost;
if (!project->isEmpty("QMAKE_RPATHDIR") && !project->isEmpty("QMAKE_LFLAGS_RPATH")) {
const ProStringList &rpathdirs = project->values("QMAKE_RPATHDIR");
for (int i = 0; i < rpathdirs.size(); ++i) {
QString rpathdir = rpathdirs[i].toQString();
if (rpathdir.size() > 1 && rpathdir.at(0) == '$' && rpathdir.at(1) != '(') {
rpathdir.replace(0, 1, "\\$$"); // Escape from make and the shell
} else if (!rpathdir.startsWith('@') && fileInfo(rpathdir).isRelative()) {
QString rpathbase = project->first("QMAKE_REL_RPATH_BASE").toQString();
if (rpathbase.isEmpty()) {
fprintf(stderr, "Error: This platform does not support relative paths in QMAKE_RPATHDIR (%s)\n",
rpathdir.toLatin1().constData());
continue;
}
if (rpathbase.startsWith('$'))
rpathbase.replace(0, 1, "\\$$"); // Escape from make and the shell
if (rpathdir == ".")
rpathdir = rpathbase;
else
rpathdir.prepend(rpathbase + '/');
project->values("QMAKE_LFLAGS").insertUnique(project->values("QMAKE_LFLAGS_REL_RPATH"));
}
project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATH") + escapeFilePath(rpathdir);
}
}
if (!project->isEmpty("QMAKE_RPATHLINKDIR")) {
const ProStringList &rpathdirs = project->values("QMAKE_RPATHLINKDIR");
for (int i = 0; i < rpathdirs.size(); ++i) {
if (!project->isEmpty("QMAKE_LFLAGS_RPATHLINK"))
project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATHLINK") + escapeFilePath(QFileInfo(rpathdirs[i].toQString()).absoluteFilePath());
}
}
if(project->isActiveConfig("GNUmake") && !project->isEmpty("QMAKE_CFLAGS_DEPS"))
include_deps = true; //do not generate deps
MakefileGenerator::init();
if (project->isActiveConfig("objective_c"))
project->values("QMAKE_BUILTIN_COMPILERS") << "OBJC" << "OBJCXX";
for (const ProString &compiler : project->values("QMAKE_BUILTIN_COMPILERS")) {
QString compile_flag = var("QMAKE_COMPILE_FLAG");
if(compile_flag.isEmpty())
compile_flag = "-c";
if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) {
QString pchFlags = var(ProKey("QMAKE_" + compiler + "FLAGS_USE_PRECOMPILE"));
QString pchBaseName;
if(!project->isEmpty("PRECOMPILED_DIR")) {
pchBaseName = Option::fixPathToTargetOS(project->first("PRECOMPILED_DIR").toQString());
if(!pchBaseName.endsWith(Option::dir_sep))
pchBaseName += Option::dir_sep;
}
pchBaseName += project->first("QMAKE_ORIG_TARGET").toQString();
// replace place holders
pchFlags.replace(QLatin1String("${QMAKE_PCH_INPUT}"),
escapeFilePath(project->first("PRECOMPILED_HEADER").toQString()));
pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT_BASE}"), escapeFilePath(pchBaseName));
if (project->isActiveConfig("icc_pch_style")) {
// icc style
pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT}"),
escapeFilePath(pchBaseName + project->first("QMAKE_PCH_OUTPUT_EXT")));
const ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
for (const ProString &arch : pchArchs) {
QString suffix = project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
suffix.replace(QLatin1String("${QMAKE_PCH_ARCH}"), arch.toQString());
pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT_") + arch + QLatin1Char('}'),
escapeFilePath(pchBaseName + suffix));
}
} else {
// gcc style (including clang_pch_style)
QString headerSuffix;
if (project->isActiveConfig("clang_pch_style"))
headerSuffix = project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
pchBaseName += project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
pchBaseName += Option::dir_sep;
ProString language = project->first(ProKey("QMAKE_LANGUAGE_" + compiler));
if (!language.isEmpty()) {
pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT}"),
escapeFilePath(pchBaseName + language + headerSuffix));
const ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
for (const ProString &arch : pchArchs) {
QString file = pchBaseName + language + headerSuffix;
file.replace(QLatin1String("${QMAKE_PCH_ARCH}"), arch.toQString());
if (project->isActiveConfig("clang_pch_style")
&& (file.endsWith(QLatin1String(".pch"))
|| file.endsWith(QLatin1String(".gch")))) {
file.chop(4); // must omit header suffix for -include to recognize the PCH
}
pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT_") + arch + QLatin1Char('}'),
escapeFilePath(file));
}
}
}
if (!pchFlags.isEmpty())
compile_flag += " " + pchFlags;
}
QString compilerExecutable;
if (compiler == "C" || compiler == "OBJC") {
compilerExecutable = "$(CC)";
compile_flag += " $(CFLAGS)";
} else {
compilerExecutable = "$(CXX)";
compile_flag += " $(CXXFLAGS)";
}
compile_flag += " $(INCPATH)";
ProString compilerVariable = compiler;
if (compilerVariable == "C")
compilerVariable = ProString("CC");
const ProKey runComp("QMAKE_RUN_" + compilerVariable);
if(project->isEmpty(runComp))
project->values(runComp).append(compilerExecutable + " " + compile_flag + " " + var("QMAKE_CC_O_FLAG") + "$obj $src");
const ProKey runCompImp("QMAKE_RUN_" + compilerVariable + "_IMP");
if(project->isEmpty(runCompImp))
project->values(runCompImp).append(compilerExecutable + " " + compile_flag + " " + var("QMAKE_CC_O_FLAG") + "\"$@\" \"$<\"");
}
if (project->isActiveConfig("mac") && !project->isEmpty("TARGET") &&
((project->isActiveConfig("build_pass") || project->isEmpty("BUILDS")))) {
ProString bundle;
if(project->isActiveConfig("bundle") && !project->isEmpty("QMAKE_BUNDLE_EXTENSION")) {
bundle = project->first("TARGET");
if(!project->isEmpty("QMAKE_BUNDLE_NAME"))
bundle = project->first("QMAKE_BUNDLE_NAME");
if(!bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
bundle += project->first("QMAKE_BUNDLE_EXTENSION");
} else if(project->first("TEMPLATE") == "app" && project->isActiveConfig("app_bundle")) {
bundle = project->first("TARGET");
if(!project->isEmpty("QMAKE_APPLICATION_BUNDLE_NAME"))
bundle = project->first("QMAKE_APPLICATION_BUNDLE_NAME");
if(!bundle.endsWith(".app"))
bundle += ".app";
if(project->isEmpty("QMAKE_BUNDLE_LOCATION"))
project->values("QMAKE_BUNDLE_LOCATION").append("Contents/MacOS");
project->values("QMAKE_PKGINFO").append(project->first("DESTDIR") + bundle + "/Contents/PkgInfo");
} else if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") &&
((!project->isActiveConfig("plugin") && project->isActiveConfig("lib_bundle")) ||
(project->isActiveConfig("plugin") && project->isActiveConfig("plugin_bundle")))) {
bundle = project->first("TARGET");
if(project->isActiveConfig("plugin")) {
if(!project->isEmpty("QMAKE_PLUGIN_BUNDLE_NAME"))
bundle = project->first("QMAKE_PLUGIN_BUNDLE_NAME");
if (project->isEmpty("QMAKE_BUNDLE_EXTENSION"))
project->values("QMAKE_BUNDLE_EXTENSION").append(".plugin");
if (!bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
bundle += project->first("QMAKE_BUNDLE_EXTENSION");
if(project->isEmpty("QMAKE_BUNDLE_LOCATION"))
project->values("QMAKE_BUNDLE_LOCATION").append("Contents/MacOS");
} else {
if(!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME"))
bundle = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME");
if (project->isEmpty("QMAKE_BUNDLE_EXTENSION"))
project->values("QMAKE_BUNDLE_EXTENSION").append(".framework");
if (!bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION")))
bundle += project->first("QMAKE_BUNDLE_EXTENSION");
}
}
if(!bundle.isEmpty()) {
project->values("QMAKE_BUNDLE") = ProStringList(bundle);
} else {
project->values("QMAKE_BUNDLE").clear();
project->values("QMAKE_BUNDLE_LOCATION").clear();
}
} else { //no bundling here
project->values("QMAKE_BUNDLE").clear();
project->values("QMAKE_BUNDLE_LOCATION").clear();
}
init2();
ProString target = project->first("TARGET");
int slsh = target.lastIndexOf(Option::dir_sep);
if (slsh != -1)
target.chopFront(slsh + 1);
project->values("LIB_TARGET").prepend(target);
}
QStringList
&UnixMakefileGenerator::findDependencies(const QString &f)
{
QStringList &ret = MakefileGenerator::findDependencies(f);
if (doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) {
ProString file = f;
QString header_prefix;
if(!project->isEmpty("PRECOMPILED_DIR"))
header_prefix = project->first("PRECOMPILED_DIR").toQString();
header_prefix += project->first("QMAKE_ORIG_TARGET").toQString();
header_prefix += project->first("QMAKE_PCH_OUTPUT_EXT").toQString();
if (project->isActiveConfig("icc_pch_style")) {
// icc style
ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
if (pchArchs.isEmpty())
pchArchs << ProString(); // normal single-arch PCH
for (const ProString &arch : std::as_const(pchArchs)) {
auto pfx = header_prefix;
if (!arch.isEmpty())
pfx.replace(QLatin1String("${QMAKE_PCH_ARCH}"), arch.toQString());
for (QStringList::Iterator it = Option::cpp_ext.begin();
it != Option::cpp_ext.end(); ++it) {
if (file.endsWith(*it)) {
ret += pfx;
break;
}
}
}
} else {
// gcc style (including clang_pch_style)
QString header_suffix = project->isActiveConfig("clang_pch_style")
? project->first("QMAKE_PCH_OUTPUT_EXT").toQString() : "";
header_prefix += Option::dir_sep + project->first("QMAKE_PRECOMP_PREFIX");
for (const ProString &compiler : project->values("QMAKE_BUILTIN_COMPILERS")) {
if (project->isEmpty(ProKey("QMAKE_" + compiler + "FLAGS_PRECOMPILE")))
continue;
ProString language = project->first(ProKey("QMAKE_LANGUAGE_" + compiler));
if (language.isEmpty())
continue;
// Unfortunately we were not consistent about the C++ naming
ProString extensionSuffix = compiler;
if (extensionSuffix == "CXX")
extensionSuffix = ProString("CPP");
for (const ProString &extension : project->values(ProKey("QMAKE_EXT_" + extensionSuffix))) {
if (!file.endsWith(extension.toQString()))
continue;
ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS");
if (pchArchs.isEmpty())
pchArchs << ProString(); // normal single-arch PCH
for (const ProString &arch : std::as_const(pchArchs)) {
QString precompiledHeader = header_prefix + language + header_suffix;
if (!arch.isEmpty()) {
precompiledHeader.replace(QLatin1String("${QMAKE_PCH_ARCH}"),
arch.toQString());
}
if (!ret.contains(precompiledHeader))
ret += precompiledHeader;
}
goto foundPrecompiledDependency;
}
}
foundPrecompiledDependency:
; // Hurray!!
}
}
return ret;
}
ProString
UnixMakefileGenerator::fixLibFlag(const ProString &lib)
{
return escapeFilePath(lib);
}
bool
UnixMakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags)
{
QList<QMakeLocalFileName> libdirs, frameworkdirs;
int libidx = 0, fwidx = 0;
for (const ProString &dlib : project->values("QMAKE_DEFAULT_LIBDIRS"))
libdirs.append(QMakeLocalFileName(dlib.toQString()));
frameworkdirs.append(QMakeLocalFileName("/System/Library/Frameworks"));
frameworkdirs.append(QMakeLocalFileName("/Library/Frameworks"));
ProStringList extens;
extens << project->first("QMAKE_EXTENSION_SHLIB") << "a";
static const char * const lflags[] = { "LIBS", "LIBS_PRIVATE",
"QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", nullptr };
for (int i = 0; lflags[i]; i++) {
ProStringList &l = project->values(lflags[i]);
for (ProStringList::Iterator it = l.begin(); it != l.end(); ) {
QString opt = (*it).toQString();
if(opt.startsWith("-")) {
if(opt.startsWith("-L")) {
QString lib = opt.mid(2);
QMakeLocalFileName f(lib);
int idx = libdirs.indexOf(f);
if (idx >= 0 && idx < libidx) {
it = l.erase(it);
continue;
}
libdirs.insert(libidx++, f);
} else if(opt.startsWith("-l")) {
QString lib = opt.mid(2);
for (const QMakeLocalFileName &libdir : std::as_const(libdirs)) {
QString libBase = libdir.local() + '/'
+ project->first("QMAKE_PREFIX_SHLIB") + lib;
if (linkPrl && processPrlFile(libBase, true))
goto found;
for (ProStringList::Iterator extit = extens.begin(); extit != extens.end(); ++extit) {
if (exists(libBase + '.' + (*extit)))
goto found;
}
}
found: ;
} else if (target_mode == TARG_MAC_MODE && opt.startsWith("-F")) {
QMakeLocalFileName f(opt.mid(2));
if (!frameworkdirs.contains(f))
frameworkdirs.insert(fwidx++, f);
} else if (target_mode == TARG_MAC_MODE && opt == "-framework") {
if (linkPrl) {
opt = (*++it).toQString();
static const QChar suffixMarker = ',';
const int suffixPosition = opt.indexOf(suffixMarker);
const bool hasSuffix = suffixPosition >= 0;
QString frameworkName = opt;
if (hasSuffix) {
frameworkName.truncate(suffixPosition);
opt.remove(suffixMarker); // Apply suffix by removing marker
}
for (const QMakeLocalFileName &dir : std::as_const(frameworkdirs)) {
auto processPrlIfFound = [&](QString directory) {
QString suffixedPrl = directory + opt;
if (processPrlFile(suffixedPrl, true))
return true;
if (hasSuffix) {
QString unsuffixedPrl = directory + frameworkName;
if (processPrlFile(unsuffixedPrl, true))
return true;
}
return false;
};
QString frameworkDirectory = dir.local() + "/" + frameworkName + + ".framework/";
if (processPrlIfFound(frameworkDirectory + "Resources/")
|| processPrlIfFound(frameworkDirectory))
break;
}
} else {
if (opt.size() == 10)
++it;
// Skip
}
}
} else if (linkPrl) {
processPrlFile(opt, false);
}
ProStringList &prl_libs = project->values("QMAKE_CURRENT_PRL_LIBS");
for (int prl = 0; prl < prl_libs.size(); ++prl)
it = l.insert(++it, prl_libs.at(prl));
prl_libs.clear();
++it;
}
if (mergeLflags) {
QHash<ProKey, ProStringList> lflags;
for(int lit = 0; lit < l.size(); ++lit) {
ProKey arch("default");
ProString opt = l.at(lit);
if (opt.startsWith('-')) {
if (target_mode == TARG_MAC_MODE && opt.startsWith("-Xarch")) {
if (opt.length() > 7) {
arch = opt.mid(7).toKey();
opt = l.at(++lit);
}
}
if (opt.startsWith("-L")
|| (target_mode == TARG_MAC_MODE && opt.startsWith("-F"))) {
if (!lflags[arch].contains(opt))
lflags[arch].append(opt);
} else if (opt.startsWith("-l") || opt == "-pthread") {
// Make sure we keep the dependency order of libraries
lflags[arch].removeAll(opt);
lflags[arch].append(opt);
} else if (target_mode == TARG_MAC_MODE
&& (opt == "-framework" || opt == "-force_load")) {
// Handle space separated options
ProString dashOpt = opt;
opt = l.at(++lit);
if (opt.startsWith("-Xarch"))
opt = l.at(++lit); // The user has done the right thing and prefixed each part
for(int x = 0; x < lflags[arch].size(); ++x) {
if (lflags[arch].at(x) == dashOpt && lflags[arch].at(++x) == opt) {
lflags[arch].remove(x - 1, 2);
break;
}
}
lflags[arch].append(dashOpt);
lflags[arch].append(opt);
} else {
lflags[arch].append(opt);
}
} else if(!opt.isNull()) {
for (const ProString &ext : extens) {
if (opt.size() > ext.size() && opt.endsWith(ext)
&& opt.at(opt.size() - ext.size() - 1) == '.') {
// Make sure we keep the dependency order of libraries
lflags[arch].removeAll(opt);
lflags[arch].append(opt);
goto found2;
}
}
if(!lflags[arch].contains(opt))
lflags[arch].append(opt);
found2: ;
}
}
l = lflags.take("default");
// Process architecture specific options (Xarch)
QHash<ProKey, ProStringList>::const_iterator archIterator = lflags.constBegin();
while (archIterator != lflags.constEnd()) {
const ProStringList &archOptions = archIterator.value();
for (int i = 0; i < archOptions.size(); ++i) {
l.append(QLatin1String("-Xarch_") + archIterator.key());
l.append(archOptions.at(i));
}
++archIterator;
}
}
}
return false;
}
#ifdef Q_OS_WIN // MinGW x-compiling for QNX
QString UnixMakefileGenerator::installRoot() const
{
/*
We include a magic prefix on the path to bypass mingw-make's "helpful"
intervention in the environment, recognising variables that look like
paths and adding the msys system root as prefix, which we don't want.
Once this hack has smuggled INSTALL_ROOT into make's variable space, we
can trivially strip the magic prefix back off to get the path we meant.
*/
return QStringLiteral("$(INSTALL_ROOT:@msyshack@%=%)");
}
#endif
QString
UnixMakefileGenerator::defaultInstall(const QString &t)
{
if(t != "target" || project->first("TEMPLATE") == "subdirs")
return QString();
enum { NoBundle, SolidBundle, SlicedBundle } bundle = NoBundle;
bool isAux = (project->first("TEMPLATE") == "aux");
const QString root = installRoot();
ProStringList &uninst = project->values(ProKey(t + ".uninstall"));
QString ret, destdir = project->first("DESTDIR").toQString();
if(!destdir.isEmpty() && destdir.right(1) != Option::dir_sep)
destdir += Option::dir_sep;
QString targetdir = fileFixify(project->first("target.path").toQString(), FileFixifyAbsolute);
if(targetdir.right(1) != Option::dir_sep)
targetdir += Option::dir_sep;
ProStringList links;
QString target="$(TARGET)";
const ProStringList &targets = project->values(ProKey(t + ".targets"));
if(!project->isEmpty("QMAKE_BUNDLE")) {
target = project->first("QMAKE_BUNDLE").toQString();
bundle = project->isActiveConfig("sliced_bundle") ? SlicedBundle : SolidBundle;
} else if(project->first("TEMPLATE") == "app") {
target = "$(QMAKE_TARGET)";
} else if(project->first("TEMPLATE") == "lib") {
if (!project->isActiveConfig("staticlib")
&& !project->isActiveConfig("plugin")
&& !project->isActiveConfig("unversioned_libname")) {
if(project->isEmpty("QMAKE_HPUX_SHLIB")) {
links << "$(TARGET0)" << "$(TARGET1)" << "$(TARGET2)";
} else {
links << "$(TARGET0)";
}
}
}
for(int i = 0; i < targets.size(); ++i) {
QString src = targets.at(i).toQString(),
dst = escapeFilePath(filePrefixRoot(root, targetdir + src.section('/', -1)));
if(!ret.isEmpty())
ret += "\n\t";
ret += "$(QINSTALL) " + escapeFilePath(Option::fixPathToTargetOS(src, false)) + ' ' + dst;
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst);
}
{
QString src_targ = target;
if(!destdir.isEmpty())
src_targ = Option::fixPathToTargetOS(destdir + target, false);
QString plain_targ = filePrefixRoot(root, fileFixify(targetdir + target, FileFixifyAbsolute));
QString dst_targ = plain_targ;
plain_targ = escapeFilePath(plain_targ);
if (bundle != NoBundle) {
QString suffix;
if (project->first("TEMPLATE") == "lib") {
if (!project->isActiveConfig("shallow_bundle"))
suffix += "/Versions/" + project->first("QMAKE_FRAMEWORK_VERSION");
suffix += "/$(TARGET)";
} else {
suffix = "/" + project->first("QMAKE_BUNDLE_LOCATION") + "/$(QMAKE_TARGET)";
}
dst_targ += suffix;
if (bundle == SolidBundle) {
if (!ret.isEmpty())
ret += "\n\t";
ret += "$(DEL_FILE) -r " + plain_targ + "\n\t";
} else {
src_targ += suffix;
}
}
src_targ = escapeFilePath(src_targ);
dst_targ = escapeFilePath(dst_targ);
QString copy_cmd;
if (bundle == SolidBundle) {
copy_cmd += "$(QINSTALL) " + src_targ + ' ' + plain_targ;
} else if (project->first("TEMPLATE") == "lib" && project->isActiveConfig("staticlib")) {
copy_cmd += "$(QINSTALL) " + src_targ + ' ' + dst_targ;
} else if (!isAux) {
if (bundle == SlicedBundle) {
if (!ret.isEmpty())
ret += "\n\t";
ret += mkdir_p_asstring("\"`dirname " + dst_targ + "`\"", false);
}
copy_cmd += "$(QINSTALL_PROGRAM) " + src_targ + ' ' + dst_targ;
}
if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib")
&& project->values(ProKey(t + ".CONFIG")).indexOf("fix_rpath") != -1) {
if (!ret.isEmpty())
ret += "\n\t";
if(!project->isEmpty("QMAKE_FIX_RPATH")) {
ret += copy_cmd;
ret += "\n\t-" + var("QMAKE_FIX_RPATH") + ' ' + dst_targ + ' ' + dst_targ;
} else if(!project->isEmpty("QMAKE_LFLAGS_RPATH")) {
ret += "-$(LINK) $(LFLAGS) " + var("QMAKE_LFLAGS_RPATH") + targetdir + " -o " +
dst_targ + " $(OBJECTS) $(LIBS) $(OBJCOMP)";
} else {
ret += copy_cmd;
}
} else if (!copy_cmd.isEmpty()) {
if (!ret.isEmpty())
ret += "\n\t";
ret += copy_cmd;
}
if (isAux) {
} else if (project->first("TEMPLATE") == "lib" && project->isActiveConfig("staticlib")) {
if(!project->isEmpty("QMAKE_RANLIB"))
ret += QString("\n\t$(RANLIB) ") + dst_targ;
} else if (!project->isActiveConfig("debug_info") && !project->isActiveConfig("nostrip")
&& !project->isEmpty("QMAKE_STRIP")) {
ret += "\n\t-$(STRIP)";
if (project->first("TEMPLATE") == "lib") {
if (!project->isEmpty("QMAKE_STRIPFLAGS_LIB"))
ret += " " + var("QMAKE_STRIPFLAGS_LIB");
} else if (project->first("TEMPLATE") == "app") {
if (!project->isEmpty("QMAKE_STRIPFLAGS_APP"))
ret += " " + var("QMAKE_STRIPFLAGS_APP");
}
ret += ' ' + dst_targ;
}
if(!uninst.isEmpty())
uninst.append("\n\t");
if (bundle == SolidBundle)
uninst.append("-$(DEL_FILE) -r " + plain_targ);
else if (!isAux)
uninst.append("-$(DEL_FILE) " + dst_targ);
if (bundle == SlicedBundle) {
int dstlen = project->first("DESTDIR").length();
for (const ProString &src : project->values("QMAKE_BUNDLED_FILES")) {
ProString file = src.mid(dstlen);
QString dst = escapeFilePath(
filePrefixRoot(root, fileFixify(targetdir + file, FileFixifyAbsolute)));
if (!ret.isEmpty())
ret += "\n\t";
ret += mkdir_p_asstring("\"`dirname " + dst + "`\"", false) + "\n\t";
ret += "-$(DEL_FILE) " + dst + "\n\t"; // Can't overwrite symlinks to directories
ret += "$(QINSTALL) " + escapeFilePath(src) + " " + dst;
if (!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst);
}
}
if(!links.isEmpty()) {
for(int i = 0; i < links.size(); ++i) {
if (target_mode == TARG_UNIX_MODE || target_mode == TARG_MAC_MODE) {
QString link = Option::fixPathToTargetOS(destdir + links[i], false);
int lslash = link.lastIndexOf(Option::dir_sep);
if(lslash != -1)
link = link.right(link.size() - (lslash + 1));
QString dst_link = escapeFilePath(
filePrefixRoot(root, fileFixify(targetdir + link, FileFixifyAbsolute)));
ret += "\n\t-$(SYMLINK) $(TARGET) " + dst_link;
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst_link);
}
}
}
}
if (isAux || project->first("TEMPLATE") == "lib") {
QStringList types;
types << "prl" << "libtool" << "pkgconfig";
for(int i = 0; i < types.size(); ++i) {
const QString type = types.at(i);
QString meta;
if(type == "prl" && project->isActiveConfig("create_prl") && !project->isActiveConfig("no_install_prl") &&
!project->isEmpty("QMAKE_INTERNAL_PRL_FILE"))
meta = prlFileName(false);
if (type == "libtool" && project->isActiveConfig("create_libtool"))
meta = libtoolFileName(false);
if(type == "pkgconfig" && project->isActiveConfig("create_pc"))
meta = pkgConfigFileName(false);
if(!meta.isEmpty()) {
QString src_meta = meta;
if(!destdir.isEmpty())
src_meta = Option::fixPathToTargetOS(destdir + meta, false);
QString dst_meta = filePrefixRoot(root, fileFixify(targetdir + meta, FileFixifyAbsolute));
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + escapeFilePath(dst_meta));
const QString dst_meta_dir = fileInfo(dst_meta).path();
if(!dst_meta_dir.isEmpty()) {
if(!ret.isEmpty())
ret += "\n\t";
ret += mkdir_p_asstring(dst_meta_dir, true);
}
if (!ret.isEmpty())
ret += "\n\t";
ret += installMetaFile(ProKey("QMAKE_" + type.toUpper() + "_INSTALL_REPLACE"), src_meta, dst_meta);
}
}
}
return ret;
}
QString
UnixMakefileGenerator::escapeFilePath(const QString &path) const
{
QString ret = path;
if(!ret.isEmpty()) {
ret.replace(QLatin1Char(' '), QLatin1String("\\ "))
.replace(QLatin1Char('\t'), QLatin1String("\\\t"));
debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
}
return ret;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,44 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef UNIXMAKE_H
#define UNIXMAKE_H
#include "makefile.h"
QT_BEGIN_NAMESPACE
class UnixMakefileGenerator : public MakefileGenerator
{
bool include_deps = false;
QString libtoolFileName(bool fixify=true);
void writeLibtoolFile(); // for libtool
void writePrlFile(QTextStream &) override;
protected:
virtual bool doPrecompiledHeaders() const { return project->isActiveConfig("precompile_header"); }
#ifdef Q_OS_WIN // MinGW x-compiling for QNX
QString installRoot() const override;
#endif
QString defaultInstall(const QString &) override;
ProString fixLibFlag(const ProString &lib) override;
bool findLibraries(bool linkPrl, bool mergeLflags) override;
QString escapeFilePath(const QString &path) const override;
using MakefileGenerator::escapeFilePath;
QStringList &findDependencies(const QString &) override;
void init() override;
void writeDefaultVariables(QTextStream &t) override;
void writeSubTargets(QTextStream &t, QList<SubTarget*> subtargets, int flags) override;
void writeMakeParts(QTextStream &);
bool writeMakefile(QTextStream &) override;
bool writeObjectsPart(QTextStream &, bool do_incremental);
private:
void init2();
ProStringList libdirToFlags(const ProKey &key);
};
QT_END_NAMESPACE
#endif // UNIXMAKE_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,326 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "mingw_make.h"
#include "option.h"
#include <proitems.h>
#include <qregularexpression.h>
#include <qdir.h>
#include <stdlib.h>
#include <time.h>
QT_BEGIN_NAMESPACE
QString MingwMakefileGenerator::escapeDependencyPath(const QString &path) const
{
QString ret = path;
ret.replace('\\', "/"); // ### this shouldn't be here
return MakefileGenerator::escapeDependencyPath(ret);
}
ProString MingwMakefileGenerator::fixLibFlag(const ProString &lib)
{
if (lib.startsWith("-l")) // Fallback for unresolved -l libs.
return QLatin1String("-l") + escapeFilePath(lib.mid(2));
if (lib.startsWith("-L")) // Lib search path. Needed only by -l above.
return QLatin1String("-L")
+ escapeFilePath(Option::fixPathToTargetOS(lib.mid(2).toQString(), false));
if (lib.startsWith("lib")) // Fallback for unresolved MSVC-style libs.
return QLatin1String("-l") + escapeFilePath(lib.mid(3).toQString());
return escapeFilePath(Option::fixPathToTargetOS(lib.toQString(), false));
}
MakefileGenerator::LibFlagType
MingwMakefileGenerator::parseLibFlag(const ProString &flag, ProString *arg)
{
// Skip MSVC handling from Win32MakefileGenerator
return MakefileGenerator::parseLibFlag(flag, arg);
}
bool MingwMakefileGenerator::processPrlFileBase(QString &origFile, QStringView origName,
QStringView fixedBase, int slashOff)
{
if (origName.startsWith(u"lib")) {
QString newFixedBase = fixedBase.left(slashOff) + fixedBase.mid(slashOff + 3);
if (Win32MakefileGenerator::processPrlFileBase(origFile, origName,
QStringView(newFixedBase), slashOff)) {
return true;
}
}
return Win32MakefileGenerator::processPrlFileBase(origFile, origName, fixedBase, slashOff);
}
bool MingwMakefileGenerator::writeMakefile(QTextStream &t)
{
writeHeader(t);
if (writeDummyMakefile(t))
return true;
if(project->first("TEMPLATE") == "app" ||
project->first("TEMPLATE") == "lib" ||
project->first("TEMPLATE") == "aux") {
if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib")
writePkgConfigFile();
writeMingwParts(t);
return MakefileGenerator::writeMakefile(t);
}
else if(project->first("TEMPLATE") == "subdirs") {
writeSubDirs(t);
return true;
}
return false;
}
QString MingwMakefileGenerator::installRoot() const
{
/*
We include a magic prefix on the path to bypass mingw-make's "helpful"
intervention in the environment, recognising variables that look like
paths and adding the msys system root as prefix, which we don't want.
Once this hack has smuggled INSTALL_ROOT into make's variable space, we
can trivially strip the magic prefix back off to get the path we meant.
*/
return QStringLiteral("$(INSTALL_ROOT:@msyshack@%=%)");
}
void MingwMakefileGenerator::writeMingwParts(QTextStream &t)
{
writeStandardParts(t);
if (!preCompHeaderOut.isEmpty()) {
QString header = project->first("PRECOMPILED_HEADER").toQString();
QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
t << escapeDependencyPath(cHeader) << ": " << escapeDependencyPath(header) << " "
<< finalizeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
<< "\n\t" << mkdir_p_asstring(preCompHeaderOut)
<< "\n\t$(CC) -x c-header -c $(CFLAGS) $(INCPATH) -o " << escapeFilePath(cHeader)
<< ' ' << escapeFilePath(header) << Qt::endl << Qt::endl;
QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
t << escapeDependencyPath(cppHeader) << ": " << escapeDependencyPath(header) << " "
<< finalizeDependencyPaths(findDependencies(header)).join(" \\\n\t\t")
<< "\n\t" << mkdir_p_asstring(preCompHeaderOut)
<< "\n\t$(CXX) -x c++-header -c $(CXXFLAGS) $(INCPATH) -o " << escapeFilePath(cppHeader)
<< ' ' << escapeFilePath(header) << Qt::endl << Qt::endl;
}
}
void MingwMakefileGenerator::init()
{
/* this should probably not be here, but I'm using it to wrap the .t files */
if(project->first("TEMPLATE") == "app")
project->values("QMAKE_APP_FLAG").append("1");
else if(project->first("TEMPLATE") == "lib")
project->values("QMAKE_LIB_FLAG").append("1");
else if(project->first("TEMPLATE") == "subdirs") {
MakefileGenerator::init();
if(project->values("MAKEFILE").isEmpty())
project->values("MAKEFILE").append("Makefile");
return;
}
processVars();
project->values("LIBS") += project->values("RES_FILE");
if (project->isActiveConfig("dll")) {
QString destDir = "";
if(!project->first("DESTDIR").isEmpty())
destDir = Option::fixPathToTargetOS(project->first("DESTDIR") + Option::dir_sep, false, false);
project->values("MINGW_IMPORT_LIB").prepend(destDir + project->first("LIB_TARGET"));
project->values("QMAKE_LFLAGS").append(QString("-Wl,--out-implib,") + fileVar("MINGW_IMPORT_LIB"));
}
if (!project->values("DEF_FILE").isEmpty()) {
QString defFileName = fileFixify(project->first("DEF_FILE").toQString());
project->values("QMAKE_LFLAGS").append(QString("-Wl,") + escapeFilePath(defFileName));
}
if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib")
project->values("QMAKE_LFLAGS").append("-static");
MakefileGenerator::init();
// precomp
if (!project->first("PRECOMPILED_HEADER").isEmpty()
&& project->isActiveConfig("precompile_header")) {
QString preCompHeader = var("PRECOMPILED_DIR")
+ QFileInfo(project->first("PRECOMPILED_HEADER").toQString()).fileName();
preCompHeaderOut = preCompHeader + ".gch";
project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c");
project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c++");
preCompHeader = escapeFilePath(preCompHeader);
project->values("QMAKE_RUN_CC").clear();
project->values("QMAKE_RUN_CC").append("$(CC) -c -include " + preCompHeader +
" $(CFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$obj $src");
project->values("QMAKE_RUN_CC_IMP").clear();
project->values("QMAKE_RUN_CC_IMP").append("$(CC) -c -include " + preCompHeader +
" $(CFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$@ $<");
project->values("QMAKE_RUN_CXX").clear();
project->values("QMAKE_RUN_CXX").append("$(CXX) -c -include " + preCompHeader +
" $(CXXFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$obj $src");
project->values("QMAKE_RUN_CXX_IMP").clear();
project->values("QMAKE_RUN_CXX_IMP").append("$(CXX) -c -include " + preCompHeader +
" $(CXXFLAGS) $(INCPATH) " + var("QMAKE_CC_O_FLAG") + "$@ $<");
}
if(project->isActiveConfig("dll")) {
project->values("QMAKE_DISTCLEAN").append(project->first("MINGW_IMPORT_LIB"));
}
}
void MingwMakefileGenerator::writeIncPart(QTextStream &t)
{
t << "INCPATH = ";
const ProStringList &incs = project->values("INCLUDEPATH");
QFile responseFile;
QTextStream responseStream;
QChar sep(' ');
int totalLength = std::accumulate(incs.constBegin(), incs.constEnd(), 0,
[](int total, const ProString &inc) {
return total + inc.size() + 2;
});
if (totalLength > project->intValue("QMAKE_RESPONSEFILE_THRESHOLD", 8000)) {
const QString fileName = createResponseFile("incpath", incs, "-I");
if (!fileName.isEmpty()) {
t << '@' + fileName;
t << Qt::endl;
return;
}
}
for (const ProString &incit: std::as_const(incs)) {
QString inc = incit.toQString();
inc.replace(QRegularExpression("\\\\$"), "");
inc.replace('\\', '/');
t << "-I" << escapeFilePath(inc) << sep;
}
t << Qt::endl;
}
void MingwMakefileGenerator::writeLibsPart(QTextStream &t)
{
if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
t << "LIB = " << var("QMAKE_LIB") << Qt::endl;
} else {
t << "LINKER = " << var("QMAKE_LINK") << Qt::endl;
t << "LFLAGS = " << var("QMAKE_LFLAGS") << Qt::endl;
t << "LIBS = "
<< fixLibFlags("LIBS").join(' ') << ' '
<< fixLibFlags("LIBS_PRIVATE").join(' ') << ' '
<< fixLibFlags("QMAKE_LIBS").join(' ') << ' '
<< fixLibFlags("QMAKE_LIBS_PRIVATE").join(' ') << Qt::endl;
}
}
void MingwMakefileGenerator::writeObjectsPart(QTextStream &t)
{
linkerResponseFile = maybeCreateLinkerResponseFile();
if (!linkerResponseFile.isValid()) {
objectsLinkLine = "$(OBJECTS)";
} else if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
// QMAKE_LIB is used for win32, including mingw, whereas QMAKE_AR is used on Unix.
QString ar_cmd = var("QMAKE_LIB");
if (ar_cmd.isEmpty())
ar_cmd = "ar -rc";
objectsLinkLine = ar_cmd + ' ' + var("DEST_TARGET") + " @"
+ escapeFilePath(linkerResponseFile.filePath);
} else {
objectsLinkLine = "@" + escapeFilePath(linkerResponseFile.filePath);
}
Win32MakefileGenerator::writeObjectsPart(t);
}
void MingwMakefileGenerator::writeBuildRulesPart(QTextStream &t)
{
t << "first: all\n";
t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName()))
<< ' ' << depVar("ALL_DEPS") << ' ' << depVar("DEST_TARGET") << "\n\n";
t << depVar("DEST_TARGET") << ": "
<< depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << depVar("POST_TARGETDEPS");
if (project->first("TEMPLATE") == "aux") {
t << "\n\n";
return;
}
if(!project->isEmpty("QMAKE_PRE_LINK"))
t << "\n\t" <<var("QMAKE_PRE_LINK");
if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
t << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET) 2>" << var("QMAKE_SHELL_NULL_DEVICE");
const ProString &objmax = project->first("QMAKE_LINK_OBJECT_MAX");
if (objmax.isEmpty() || project->values("OBJECTS").size() < objmax.toInt()) {
t << "\n\t$(LIB) $(DESTDIR_TARGET) " << objectsLinkLine << " " ;
} else {
t << "\n\t" << objectsLinkLine << " " ;
}
} else {
t << "\n\t$(LINKER) $(LFLAGS) " << var("QMAKE_LINK_O_FLAG") << "$(DESTDIR_TARGET) "
<< objectsLinkLine;
if (!linkerResponseFile.isValid() || linkerResponseFile.onlyObjects)
t << " $(LIBS)";
}
if(!project->isEmpty("QMAKE_POST_LINK"))
t << "\n\t" <<var("QMAKE_POST_LINK");
t << Qt::endl;
}
void MingwMakefileGenerator::writeRcFilePart(QTextStream &t)
{
const QString rc_file = fileFixify(project->first("RC_FILE").toQString());
ProStringList rcIncPaths = project->values("RC_INCLUDEPATH");
rcIncPaths.prepend(fileInfo(rc_file).path());
QString incPathStr;
for (int i = 0; i < rcIncPaths.size(); ++i) {
const ProString &path = rcIncPaths.at(i);
if (path.isEmpty())
continue;
incPathStr += QStringLiteral(" --include-dir=");
if (path != "." && QDir::isRelativePath(path.toQString()))
incPathStr += "./";
incPathStr += escapeFilePath(path);
}
if (!rc_file.isEmpty()) {
ProString defines = varGlue("RC_DEFINES", " -D", " -D", "");
if (defines.isEmpty())
defines = ProString(" $(DEFINES)");
addSourceFile(rc_file, QMakeSourceFileInfo::SEEK_DEPS);
const QStringList rcDeps = QStringList(rc_file) << dependencies(rc_file);
t << escapeDependencyPath(var("RES_FILE")) << ": "
<< escapeDependencyPaths(rcDeps).join(' ') << "\n\t"
<< var("QMAKE_RC") << " -i " << escapeFilePath(rc_file) << " -o " << fileVar("RES_FILE")
<< incPathStr << defines << "\n\n";
}
}
QStringList &MingwMakefileGenerator::findDependencies(const QString &file)
{
QStringList &aList = MakefileGenerator::findDependencies(file);
if (preCompHeaderOut.isEmpty())
return aList;
for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) {
if (file.endsWith(*it)) {
QString cHeader = preCompHeaderOut + Option::dir_sep + "c";
if (!aList.contains(cHeader))
aList += cHeader;
break;
}
}
for (QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) {
if (file.endsWith(*it)) {
QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++";
if (!aList.contains(cppHeader))
aList += cppHeader;
break;
}
}
return aList;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,42 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MINGW_MAKE_H
#define MINGW_MAKE_H
#include "winmakefile.h"
QT_BEGIN_NAMESPACE
class MingwMakefileGenerator : public Win32MakefileGenerator
{
protected:
using MakefileGenerator::escapeDependencyPath;
QString escapeDependencyPath(const QString &path) const override;
ProString fixLibFlag(const ProString &lib) override;
bool processPrlFileBase(QString &origFile, QStringView origName,
QStringView fixedBase, int slashOff) override;
bool writeMakefile(QTextStream &) override;
void init() override;
QString installRoot() const override;
private:
void writeMingwParts(QTextStream &);
void writeIncPart(QTextStream &t) override;
void writeLibsPart(QTextStream &t) override;
void writeObjectsPart(QTextStream &t) override;
void writeBuildRulesPart(QTextStream &t) override;
void writeRcFilePart(QTextStream &t) override;
QStringList &findDependencies(const QString &file) override;
QString preCompHeaderOut;
LibFlagType parseLibFlag(const ProString &flag, ProString *arg) override;
QString objectsLinkLine;
LinkerResponseFileInfo linkerResponseFile;
};
QT_END_NAMESPACE
#endif // MINGW_MAKE_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,168 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MSBUILD_OBJECTMODEL_H
#define MSBUILD_OBJECTMODEL_H
#include "project.h"
#include "xmloutput.h"
#include "msvc_objectmodel.h"
#include <qlist.h>
#include <qstring.h>
#include <qmap.h>
QT_BEGIN_NAMESPACE
// Tree & Flat view of files --------------------------------------------------
class XNode
{
public:
virtual ~XNode() { }
void addElement(const VCFilterFile &file) {
addElement(file.file, file);
}
virtual void addElement(const QString &filepath, const VCFilterFile &allInfo) = 0;
virtual void removeElements()= 0;
virtual void generateXML(XmlOutput &xml, XmlOutput &xmlFilter, const QString &tagName,
VCProject &tool, const QString &filter) = 0;
virtual bool hasElements() = 0;
};
class XTreeNode : public XNode
{
typedef QMap<QString, XTreeNode*> ChildrenMap;
VCFilterFile info;
ChildrenMap children;
public:
virtual ~XTreeNode() { removeElements(); }
int pathIndex(const QString &filepath) {
int Windex = filepath.indexOf("\\");
int Uindex = filepath.indexOf("/");
if (Windex != -1 && Uindex != -1)
return qMin(Windex, Uindex);
else if (Windex != -1)
return Windex;
return Uindex;
}
void addElement(const QString &filepath, const VCFilterFile &allInfo) override {
QString newNodeName(filepath);
int index = pathIndex(filepath);
if (index != -1)
newNodeName = filepath.left(index);
XTreeNode *n = children.value(newNodeName);
if (!n) {
n = new XTreeNode;
n->info = allInfo;
children.insert(newNodeName, n);
}
if (index != -1)
n->addElement(filepath.mid(index+1), allInfo);
}
void removeElements() override {
ChildrenMap::ConstIterator it = children.constBegin();
ChildrenMap::ConstIterator end = children.constEnd();
for( ; it != end; it++) {
(*it)->removeElements();
delete it.value();
}
children.clear();
}
void generateXML(XmlOutput &xml, XmlOutput &xmlFilter, const QString &tagName, VCProject &tool,
const QString &filter) override;
bool hasElements() override {
return children.size() != 0;
}
};
class XFlatNode : public XNode
{
typedef QMap<QString, VCFilterFile> ChildrenMapFlat;
ChildrenMapFlat children;
public:
virtual ~XFlatNode() { removeElements(); }
int pathIndex(const QString &filepath) {
int Windex = filepath.lastIndexOf("\\");
int Uindex = filepath.lastIndexOf("/");
if (Windex != -1 && Uindex != -1)
return qMax(Windex, Uindex);
else if (Windex != -1)
return Windex;
return Uindex;
}
void addElement(const QString &filepath, const VCFilterFile &allInfo) override {
QString newKey(filepath);
int index = pathIndex(filepath);
if (index != -1)
newKey = filepath.mid(index+1);
// Key designed to sort files with same
// name in different paths correctly
children.insert(newKey + "\0" + allInfo.file, allInfo);
}
void removeElements() override {
children.clear();
}
void generateXML(XmlOutput &xml, XmlOutput &xmlFilter, const QString &tagName, VCProject &proj,
const QString &filter) override;
bool hasElements() override {
return children.size() != 0;
}
};
class VCXProjectWriter : public VCProjectWriter
{
public:
void write(XmlOutput &, VCProjectSingleConfig &) override;
void write(XmlOutput &, VCProject &) override;
void write(XmlOutput &, const VCCLCompilerTool &) override;
void write(XmlOutput &, const VCLinkerTool &) override;
void write(XmlOutput &, const VCMIDLTool &) override;
void write(XmlOutput &, const VCCustomBuildTool &) override;
void write(XmlOutput &, const VCLibrarianTool &) override;
void write(XmlOutput &, const VCResourceCompilerTool &) override;
void write(XmlOutput &, const VCEventTool &) override;
void write(XmlOutput &, const VCDeploymentTool &) override;
void write(XmlOutput &, const VCWinDeployQtTool &) override;
void write(XmlOutput &, const VCConfiguration &) override;
void write(XmlOutput &, VCFilter &) override;
private:
struct OutputFilterData
{
VCFilter filter;
VCFilterFile info;
bool inBuild;
};
static void addFilters(VCProject &project, XmlOutput &xmlFilter, const QString &filterName);
static void outputFilter(VCProject &project, XmlOutput &xml, XmlOutput &xmlFilter, const QString &filtername);
static void outputFileConfigs(VCProject &project, XmlOutput &xml, XmlOutput &xmlFilter,
const VCFilterFile &info, const QString &filtername);
static bool outputFileConfig(OutputFilterData *d, XmlOutput &xml, XmlOutput &xmlFilter,
const QString &filename, const QString &fullFilterName,
bool fileAdded, bool hasCustomBuildStep);
static void outputFileConfig(XmlOutput &xml, XmlOutput &xmlFilter, const QString &fileName, const QString &filterName);
static QString generateCondition(const VCConfiguration &config);
static XmlOutput::xml_output attrTagToolsVersion(const VCConfiguration &config);
friend class XTreeNode;
friend class XFlatNode;
};
QT_END_NAMESPACE
#endif // MSVC_OBJECTMODEL_H

View File

@ -0,0 +1,515 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "msvc_nmake.h"
#include "option.h"
#include <qregularexpression.h>
#include <qdir.h>
#include <qdiriterator.h>
#include <qset.h>
#include <time.h>
QT_BEGIN_NAMESPACE
bool
NmakeMakefileGenerator::writeMakefile(QTextStream &t)
{
writeHeader(t);
if (writeDummyMakefile(t))
return true;
if(project->first("TEMPLATE") == "app" ||
project->first("TEMPLATE") == "lib" ||
project->first("TEMPLATE") == "aux") {
writeNmakeParts(t);
return MakefileGenerator::writeMakefile(t);
}
else if(project->first("TEMPLATE") == "subdirs") {
writeSubDirs(t);
return true;
}
return false;
}
void NmakeMakefileGenerator::writeSubMakeCall(QTextStream &t, const QString &callPrefix,
const QString &makeArguments)
{
// Pass MAKEFLAGS as environment variable to sub-make calls.
// Unlike other make tools nmake doesn't do this automatically.
t << "\n\t@set MAKEFLAGS=$(MAKEFLAGS)";
Win32MakefileGenerator::writeSubMakeCall(t, callPrefix, makeArguments);
}
ProStringList NmakeMakefileGenerator::extraSubTargetDependencies()
{
return { "$(MAKEFILE)" };
}
QString NmakeMakefileGenerator::defaultInstall(const QString &t)
{
QString ret = Win32MakefileGenerator::defaultInstall(t);
if (ret.isEmpty())
return ret;
const QString root = installRoot();
ProStringList &uninst = project->values(ProKey(t + ".uninstall"));
QString targetdir = fileFixify(project->first(ProKey(t + ".path")).toQString(), FileFixifyAbsolute);
if(targetdir.right(1) != Option::dir_sep)
targetdir += Option::dir_sep;
if (project->isActiveConfig("debug_info")) {
if (t == "dlltarget" || project->values(ProKey(t + ".CONFIG")).indexOf("no_dll") == -1) {
const QFileInfo targetFileInfo(project->first("DESTDIR") + project->first("TARGET")
+ project->first("TARGET_EXT"));
const QString pdb_target = targetFileInfo.completeBaseName() + ".pdb";
QString src_targ = (project->isEmpty("DESTDIR") ? QString("$(DESTDIR)") : project->first("DESTDIR")) + pdb_target;
QString dst_targ = filePrefixRoot(root, fileFixify(targetdir + pdb_target, FileFixifyAbsolute));
if(!ret.isEmpty())
ret += "\n\t";
ret += QString("-$(INSTALL_FILE) ") + escapeFilePath(src_targ) + ' ' + escapeFilePath(dst_targ);
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + escapeFilePath(dst_targ));
}
}
return ret;
}
QStringList &NmakeMakefileGenerator::findDependencies(const QString &file)
{
QStringList &aList = MakefileGenerator::findDependencies(file);
for(QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) {
if(file.endsWith(*it)) {
if(!precompObj.isEmpty() && !aList.contains(precompObj))
aList += precompObj;
break;
}
}
for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) {
if (file.endsWith(*it)) {
if (!precompObjC.isEmpty() && !aList.contains(precompObjC))
aList += precompObjC;
break;
}
}
return aList;
}
void NmakeMakefileGenerator::writeNmakeParts(QTextStream &t)
{
writeStandardParts(t);
// precompiled header
if(usePCH) {
QString precompRule = QString("-c -Yc -Fp%1 -Fo%2")
.arg(escapeFilePath(precompPch), escapeFilePath(precompObj));
t << escapeDependencyPath(precompObj) << ": " << escapeDependencyPath(precompH) << ' '
<< finalizeDependencyPaths(findDependencies(precompH)).join(" \\\n\t\t")
<< "\n\t$(CXX) " + precompRule +" $(CXXFLAGS) $(INCPATH) -TP "
<< escapeFilePath(precompH) << Qt::endl << Qt::endl;
}
if (usePCHC) {
QString precompRuleC = QString("-c -Yc -Fp%1 -Fo%2")
.arg(escapeFilePath(precompPchC), escapeFilePath(precompObjC));
t << escapeDependencyPath(precompObjC) << ": " << escapeDependencyPath(precompH) << ' '
<< finalizeDependencyPaths(findDependencies(precompH)).join(" \\\n\t\t")
<< "\n\t$(CC) " + precompRuleC +" $(CFLAGS) $(INCPATH) -TC "
<< escapeFilePath(precompH) << Qt::endl << Qt::endl;
}
}
QString NmakeMakefileGenerator::var(const ProKey &value) const
{
if (usePCH || usePCHC) {
const bool isRunC = (value == "QMAKE_RUN_CC_IMP_BATCH"
|| value == "QMAKE_RUN_CC_IMP"
|| value == "QMAKE_RUN_CC");
const bool isRunCpp = (value == "QMAKE_RUN_CXX_IMP_BATCH"
|| value == "QMAKE_RUN_CXX_IMP"
|| value == "QMAKE_RUN_CXX");
if ((isRunCpp && usePCH) || (isRunC && usePCHC)) {
QString precompH_f = escapeFilePath(fileFixify(precompH, FileFixifyBackwards));
QString precompRule = QString("-c -FI%1 -Yu%2 -Fp%3")
.arg(precompH_f, precompH_f, escapeFilePath(isRunC ? precompPchC : precompPch));
// ### For clang_cl 8 we force inline methods to be compiled here instead
// linking them from a pch.o file. We do this by pretending we are also doing
// the pch.o generation step.
if (project->isActiveConfig("clang_cl"))
precompRule += QString(" -Xclang -building-pch-with-obj");
QString p = MakefileGenerator::var(value);
p.replace(QLatin1String("-c"), precompRule);
return p;
}
}
// Normal val
return MakefileGenerator::var(value);
}
void NmakeMakefileGenerator::suppressBuiltinRules(QTextStream &) const
{
}
void NmakeMakefileGenerator::init()
{
/* this should probably not be here, but I'm using it to wrap the .t files */
if(project->first("TEMPLATE") == "app")
project->values("QMAKE_APP_FLAG").append("1");
else if(project->first("TEMPLATE") == "lib")
project->values("QMAKE_LIB_FLAG").append("1");
else if(project->first("TEMPLATE") == "subdirs") {
MakefileGenerator::init();
if(project->values("MAKEFILE").isEmpty())
project->values("MAKEFILE").append("Makefile");
return;
}
processVars();
project->values("LIBS") += project->values("RES_FILE");
if (!project->values("DEF_FILE").isEmpty()) {
QString defFileName = fileFixify(project->first("DEF_FILE").toQString());
project->values("QMAKE_LFLAGS").append(QString("/DEF:") + escapeFilePath(defFileName));
}
// set /VERSION for EXE/DLL header
ProString major_minor = project->first("VERSION_PE_HEADER");
if (major_minor.isEmpty()) {
ProString version = project->first("VERSION");
if (!version.isEmpty()) {
int firstDot = version.indexOf(".");
int secondDot = version.indexOf(".", firstDot + 1);
major_minor = version.left(secondDot);
}
}
if (!major_minor.isEmpty())
project->values("QMAKE_LFLAGS").append("/VERSION:" + major_minor);
if (project->isEmpty("QMAKE_LINK_O_FLAG"))
project->values("QMAKE_LINK_O_FLAG").append("/OUT:");
// Base class init!
MakefileGenerator::init();
// Setup PCH variables
precompH = project->first("PRECOMPILED_HEADER").toQString();
usePCH = !precompH.isEmpty() && project->isActiveConfig("precompile_header");
usePCHC = !precompH.isEmpty() && project->isActiveConfig("precompile_header_c");
if (usePCH) {
// Created files
precompObj = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch" + Option::obj_ext;
precompPch = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch.pch";
// Add linking of precompObj (required for whole precompiled classes)
// ### For clang_cl we currently let inline methods be generated in the normal objects,
// since the PCH object is buggy (as of clang 8.0.0)
if (!project->isActiveConfig("clang_cl"))
project->values("OBJECTS") += precompObj;
// Add pch file to cleanup
project->values("QMAKE_CLEAN") += precompPch;
// Return to variable pool
project->values("PRECOMPILED_OBJECT") = ProStringList(precompObj);
project->values("PRECOMPILED_PCH") = ProStringList(precompPch);
}
if (usePCHC) {
precompObjC = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch_c" + Option::obj_ext;
precompPchC = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch_c.pch";
if (!project->isActiveConfig("clang_cl"))
project->values("OBJECTS") += precompObjC;
project->values("QMAKE_CLEAN") += precompPchC;
project->values("PRECOMPILED_OBJECT_C") = ProStringList(precompObjC);
project->values("PRECOMPILED_PCH_C") = ProStringList(precompPchC);
}
const QFileInfo targetFileInfo(project->first("DESTDIR") + project->first("TARGET")
+ project->first("TARGET_EXT"));
const ProString targetBase = targetFileInfo.path() + '/' + targetFileInfo.completeBaseName();
if (project->first("TEMPLATE") == "lib" && project->isActiveConfig("shared")) {
project->values("QMAKE_CLEAN").append(targetBase + ".exp");
project->values("QMAKE_DISTCLEAN").append(targetBase + ".lib");
}
if (project->isActiveConfig("debug_info")) {
QString pdbfile;
QString distPdbFile = targetBase + ".pdb";
if (project->isActiveConfig("staticlib")) {
// For static libraries, the compiler's pdb file and the dist pdb file are the same.
pdbfile = distPdbFile;
} else {
// Use $${TARGET}.vc.pdb in the OBJECTS_DIR for the compiler and
// $${TARGET}.pdb (the default) for the linker.
pdbfile = var("OBJECTS_DIR") + project->first("TARGET") + ".vc.pdb";
}
QString escapedPdbFile = escapeFilePath(pdbfile);
project->values("QMAKE_CFLAGS").append("/Fd" + escapedPdbFile);
project->values("QMAKE_CXXFLAGS").append("/Fd" + escapedPdbFile);
project->values("QMAKE_CLEAN").append(pdbfile);
project->values("QMAKE_DISTCLEAN").append(distPdbFile);
}
if (project->isActiveConfig("debug")) {
project->values("QMAKE_CLEAN").append(targetBase + ".ilk");
project->values("QMAKE_CLEAN").append(targetBase + ".idb");
}
if (project->values("QMAKE_APP_FLAG").isEmpty() && project->isActiveConfig("dll")) {
ProStringList &defines = project->values("DEFINES");
if (!defines.contains("_WINDLL"))
defines.append("_WINDLL");
}
}
QStringList NmakeMakefileGenerator::sourceFilesForImplicitRulesFilter()
{
QStringList filter;
const QChar wildcard = QLatin1Char('*');
for (const QString &ext : std::as_const(Option::c_ext))
filter << wildcard + ext;
for (const QString &ext : std::as_const(Option::cpp_ext))
filter << wildcard + ext;
return filter;
}
void NmakeMakefileGenerator::writeImplicitRulesPart(QTextStream &t)
{
t << "####### Implicit rules\n\n";
t << ".SUFFIXES:";
for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit)
t << " " << (*cit);
for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit)
t << " " << (*cppit);
t << Qt::endl << Qt::endl;
bool useInferenceRules = !project->isActiveConfig("no_batch");
QSet<QString> source_directories;
if (useInferenceRules) {
source_directories.insert(".");
static const char * const directories[] = { "UI_SOURCES_DIR", "UI_DIR", nullptr };
for (int y = 0; directories[y]; y++) {
QString dirTemp = project->first(directories[y]).toQString();
if (dirTemp.endsWith("\\"))
dirTemp.truncate(dirTemp.size()-1);
if(!dirTemp.isEmpty())
source_directories.insert(dirTemp);
}
static const char * const srcs[] = { "SOURCES", "GENERATED_SOURCES", nullptr };
for (int x = 0; srcs[x]; x++) {
const ProStringList &l = project->values(srcs[x]);
for (ProStringList::ConstIterator sit = l.begin(); sit != l.end(); ++sit) {
QString sep = "\\";
if((*sit).indexOf(sep) == -1)
sep = "/";
QString dir = (*sit).toQString().section(sep, 0, -2);
if (!dir.isEmpty())
source_directories.insert(dir);
}
}
// nmake's inference rules might pick up the wrong files when encountering source files with
// the same name in different directories. In this situation, turn inference rules off.
QHash<QString, QString> fileNames;
bool duplicatesFound = false;
const QStringList sourceFilesFilter = sourceFilesForImplicitRulesFilter();
QStringList fixifiedSourceDirs = fileFixify(QList<QString>(source_directories.constBegin(), source_directories.constEnd()), FileFixifyAbsolute);
fixifiedSourceDirs.removeDuplicates();
for (const QString &sourceDir : std::as_const(fixifiedSourceDirs)) {
QDirIterator dit(sourceDir, sourceFilesFilter, QDir::Files | QDir::NoDotAndDotDot);
while (dit.hasNext()) {
const QFileInfo fi = dit.nextFileInfo();
QString &duplicate = fileNames[fi.completeBaseName()];
if (duplicate.isNull()) {
duplicate = fi.filePath();
} else {
warn_msg(WarnLogic, "%s conflicts with %s", qPrintable(duplicate),
qPrintable(fi.filePath()));
duplicatesFound = true;
}
}
}
if (duplicatesFound) {
useInferenceRules = false;
warn_msg(WarnLogic, "Automatically turning off nmake's inference rules. (CONFIG += no_batch)");
}
}
if (useInferenceRules) {
// Batchmode doesn't use the non implicit rules QMAKE_RUN_CXX & QMAKE_RUN_CC
project->variables().remove("QMAKE_RUN_CXX");
project->variables().remove("QMAKE_RUN_CC");
for (const QString &sourceDir : std::as_const(source_directories)) {
if (sourceDir.isEmpty())
continue;
QString objDir = var("OBJECTS_DIR");
if (objDir == ".\\")
objDir = "";
for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit)
t << '{' << escapeDependencyPath(sourceDir) << '}' << (*cppit)
<< '{' << escapeDependencyPath(objDir) << '}' << Option::obj_ext << "::\n\t"
<< var("QMAKE_RUN_CXX_IMP_BATCH").replace(QRegularExpression("\\$@"), fileVar("OBJECTS_DIR"))
<< "\n\t$<\n<<\n\n";
for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit)
t << '{' << escapeDependencyPath(sourceDir) << '}' << (*cit)
<< '{' << escapeDependencyPath(objDir) << '}' << Option::obj_ext << "::\n\t"
<< var("QMAKE_RUN_CC_IMP_BATCH").replace(QRegularExpression("\\$@"), fileVar("OBJECTS_DIR"))
<< "\n\t$<\n<<\n\n";
}
} else {
for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit)
t << (*cppit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CXX_IMP") << Qt::endl << Qt::endl;
for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit)
t << (*cit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CC_IMP") << Qt::endl << Qt::endl;
}
}
void NmakeMakefileGenerator::writeBuildRulesPart(QTextStream &t)
{
const ProString templateName = project->first("TEMPLATE");
t << "first: all\n";
t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName()))
<< ' ' << depVar("ALL_DEPS") << ' ' << depVar("DEST_TARGET") << "\n\n";
t << depVar("DEST_TARGET") << ": "
<< depVar("PRE_TARGETDEPS") << " $(OBJECTS) " << depVar("POST_TARGETDEPS");
if (templateName == "aux") {
t << "\n\n";
return;
}
if(!project->isEmpty("QMAKE_PRE_LINK"))
t << "\n\t" <<var("QMAKE_PRE_LINK");
if(project->isActiveConfig("staticlib")) {
t << "\n\t$(LIBAPP) $(LIBFLAGS) " << var("QMAKE_LINK_O_FLAG") << "$(DESTDIR_TARGET) @<<\n\t ";
writeResponseFileFiles(t, project->values("OBJECTS"));
t << "<<";
} else {
const bool embedManifest = ((templateName == "app" && project->isActiveConfig("embed_manifest_exe"))
|| (templateName == "lib" && project->isActiveConfig("embed_manifest_dll")
&& !(project->isActiveConfig("plugin") && project->isActiveConfig("no_plugin_manifest"))
));
if (embedManifest) {
bool generateManifest = false;
const QString target = var("DEST_TARGET");
QString manifest = project->first("QMAKE_MANIFEST").toQString();
QString extraLFlags;
const bool linkerSupportsEmbedding = (msvcVersion() >= 1200);
if (manifest.isEmpty()) {
generateManifest = true;
if (linkerSupportsEmbedding) {
extraLFlags = "/MANIFEST:embed";
} else {
manifest = target + ".embed.manifest";
extraLFlags += "/MANIFEST /MANIFESTFILE:" + escapeFilePath(manifest);
project->values("QMAKE_CLEAN") << manifest;
}
} else {
manifest = fileFixify(manifest);
if (linkerSupportsEmbedding)
extraLFlags = "/MANIFEST:embed /MANIFESTINPUT:" + escapeFilePath(manifest);
}
const QString resourceId = (templateName == "app") ? "1" : "2";
const bool incrementalLinking = project->values("QMAKE_LFLAGS").toQStringList().filter(QRegularExpression("(/|-)INCREMENTAL:NO")).isEmpty();
if (incrementalLinking && !linkerSupportsEmbedding) {
// Link a resource that contains the manifest without modifying the exe/dll after linking.
QString manifest_rc = target + "_manifest.rc";
QString manifest_res = target + "_manifest.res";
project->values("QMAKE_CLEAN") << manifest_rc << manifest_res;
manifest_rc = escapeFilePath(manifest_rc);
manifest_res = escapeFilePath(manifest_res);
t << "\n\techo " << resourceId
<< " /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 24 /* RT_MANIFEST */ "
<< cQuoted(manifest) << '>' << manifest_rc;
if (generateManifest) {
manifest = escapeFilePath(manifest);
QString manifest_bak = escapeFilePath(target + "_manifest.bak");
project->values("QMAKE_CLEAN") << manifest_bak;
t << "\n\tif not exist $(DESTDIR_TARGET) if exist " << manifest
<< " del " << manifest;
t << "\n\tif exist " << manifest << " copy /Y " << manifest << ' ' << manifest_bak;
const QString extraInlineFileContent = "\n!IF EXIST(" + manifest_res + ")\n" + manifest_res + "\n!ENDIF";
t << "\n\t";
writeLinkCommand(t, extraLFlags, extraInlineFileContent);
t << "\n\tif exist " << manifest_bak << " fc /b " << manifest << ' ' << manifest_bak << " >NUL || del " << manifest_bak;
t << "\n\tif not exist " << manifest_bak << " rc.exe /fo" << manifest_res << ' ' << manifest_rc;
t << "\n\tif not exist " << manifest_bak << ' ';
writeLinkCommand(t, extraLFlags, manifest_res);
t << "\n\tif exist " << manifest_bak << " del " << manifest_bak;
} else {
t << "\n\trc.exe /fo" << manifest_res << " " << manifest_rc;
t << "\n\t";
writeLinkCommand(t, extraLFlags, manifest_res);
}
} else {
// directly embed the manifest in the executable after linking
t << "\n\t";
writeLinkCommand(t, extraLFlags);
if (!linkerSupportsEmbedding) {
t << "\n\tmt.exe /nologo /manifest " << escapeFilePath(manifest)
<< " /outputresource:$(DESTDIR_TARGET);" << resourceId;
}
}
} else {
t << "\n\t";
writeLinkCommand(t);
}
}
if(!project->isEmpty("QMAKE_POST_LINK")) {
t << "\n\t" << var("QMAKE_POST_LINK");
}
t << Qt::endl;
}
void NmakeMakefileGenerator::writeLinkCommand(QTextStream &t, const QString &extraFlags, const QString &extraInlineFileContent)
{
t << "$(LINKER) $(LFLAGS)";
if (!extraFlags.isEmpty())
t << ' ' << extraFlags;
t << " " << var("QMAKE_LINK_O_FLAG") << "$(DESTDIR_TARGET) @<<\n";
writeResponseFileFiles(t, project->values("OBJECTS"));
t << "$(LIBS)\n";
if (!extraInlineFileContent.isEmpty())
t << extraInlineFileContent << '\n';
t << "<<";
}
void NmakeMakefileGenerator::writeResponseFileFiles(QTextStream &t, const ProStringList &files)
{
// Add line breaks in file lists in reponse files to work around LNK1170.
// The actual line length limit is 131070, but let's use a smaller limit
// in case other tools are similarly hampered.
const int maxLineLength = 1000;
int len = 0;
for (const ProString &file : files) {
const ProString escapedFilePath = escapeFilePath(file);
if (len) {
if (len + escapedFilePath.length() > maxLineLength) {
t << '\n';
len = 0;
} else {
t << ' ';
len++;
}
}
t << escapedFilePath;
len += escapedFilePath.length();
}
t << '\n';
}
int NmakeMakefileGenerator::msvcVersion() const
{
const int fallbackVersion = 800; // Visual Studio 2005
const QString ver = project->first(ProKey("MSVC_VER")).toQString();
bool ok;
float f = ver.toFloat(&ok);
return ok ? int(f * 100) : fallbackVersion;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,39 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MSVC_NMAKE_H
#define MSVC_NMAKE_H
#include "winmakefile.h"
QT_BEGIN_NAMESPACE
class NmakeMakefileGenerator : public Win32MakefileGenerator
{
void writeNmakeParts(QTextStream &);
bool writeMakefile(QTextStream &) override;
void writeImplicitRulesPart(QTextStream &t) override;
void writeBuildRulesPart(QTextStream &t) override;
void writeLinkCommand(QTextStream &t, const QString &extraFlags = QString(), const QString &extraInlineFileContent = QString());
void writeResponseFileFiles(QTextStream &t, const ProStringList &files);
int msvcVersion() const;
void init() override;
static QStringList sourceFilesForImplicitRulesFilter();
protected:
void writeSubMakeCall(QTextStream &t, const QString &callPrefix,
const QString &makeArguments) override;
ProStringList extraSubTargetDependencies() override;
QString defaultInstall(const QString &t) override;
QStringList &findDependencies(const QString &file) override;
QString var(const ProKey &value) const override;
void suppressBuiltinRules(QTextStream &t) const override;
QString precompH, precompObj, precompPch;
QString precompObjC, precompPchC;
bool usePCH = false;
bool usePCHC = false;
};
QT_END_NAMESPACE
#endif // MSVC_NMAKE_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MSVC_VCPROJ_H
#define MSVC_VCPROJ_H
#include "winmakefile.h"
#include "msvc_objectmodel.h"
QT_BEGIN_NAMESPACE
enum Target {
Application,
SharedLib,
StaticLib
};
class QUuid;
struct VcsolutionDepend;
class VcprojGenerator : public Win32MakefileGenerator
{
bool is64Bit;
bool writeVcprojParts(QTextStream &);
bool writeMakefile(QTextStream &) override;
bool writeProjectMakefile() override;
void init() override;
public:
VcprojGenerator();
~VcprojGenerator();
QString defaultMakefile() const;
QString precompH, precompHFilename, precompSource,
precompObj, precompPch;
bool autogenPrecompSource;
static bool hasBuiltinCompiler(const QString &file);
QHash<QString, QStringList> extraCompilerSources;
QHash<QString, QString> extraCompilerOutputs;
const QString customBuildToolFilterFileSuffix;
bool usePCH;
bool pchIsCFile = false;
VCProjectWriter *projectWriter;
using Win32MakefileGenerator::callExtraCompilerDependCommand;
protected:
virtual VCProjectWriter *createProjectWriter();
bool doDepends() const override { return false; } // Never necessary
using Win32MakefileGenerator::replaceExtraCompilerVariables;
QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &, ReplaceFor) override;
QString extraCompilerName(const ProString &extraCompiler, const QStringList &inputs,
const QStringList &outputs);
bool supportsMetaBuild() override { return true; }
bool supportsMergedBuilds() override { return true; }
bool mergeBuildProject(MakefileGenerator *other) override;
bool openOutput(QFile &file, const QString &build) const override;
virtual void initProject();
void initConfiguration();
void initCompilerTool();
void initLinkerTool();
void initLibrarianTool();
void initManifestTool();
void initResourceTool();
void initIDLTool();
void initCustomBuildTool();
void initPreBuildEventTools();
void initPostBuildEventTools();
void initDeploymentTool();
void initWinDeployQtTool();
void initPreLinkEventTools();
void initRootFiles();
void initSourceFiles();
void initHeaderFiles();
void initGeneratedFiles();
void initTranslationFiles();
void initFormFiles();
void initResourceFiles();
void initDeploymentFiles();
void initDistributionFiles();
void initLexYaccFiles();
void initExtraCompilerOutputs();
void writeSubDirs(QTextStream &t); // Called from VCXProj backend
QUuid getProjectUUID(const QString &filename=QString()); // Called from VCXProj backend
Target projectTarget;
// Used for single project
VCProjectSingleConfig vcProject;
// Holds all configurations for glue (merged) project
QList<VcprojGenerator*> mergedProjects;
private:
ProStringList collectDependencies(QMakeProject *proj, QHash<QString, QString> &projLookup,
QHash<QString, QString> &projGuids,
QHash<VcsolutionDepend *, QStringList> &extraSubdirs,
QHash<QString, VcsolutionDepend*> &solution_depends,
QList<VcsolutionDepend*> &solution_cleanup,
QTextStream &t,
QHash<QString, ProStringList> &subdirProjectLookup,
const ProStringList &allDependencies = ProStringList());
QUuid increaseUUID(const QUuid &id);
QString retrievePlatformToolSet() const;
bool isStandardSuffix(const QString &suffix) const;
ProString firstInputFileName(const ProString &extraCompilerName) const;
QString firstExpandedOutputFileName(const ProString &extraCompilerName);
void createCustomBuildToolFakeFile(const QString &cbtFilePath, const QString &realOutFilePath);
bool otherFiltersContain(const QString &fileName) const;
friend class VCFilter;
};
inline QString VcprojGenerator::defaultMakefile() const
{
return project->first("TARGET") + project->first("VCPROJ_EXTENSION");
}
QT_END_NAMESPACE
#endif // MSVC_VCPROJ_H

View File

@ -0,0 +1,18 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "msvc_vcxproj.h"
#include "msbuild_objectmodel.h"
QT_BEGIN_NAMESPACE
VcxprojGenerator::VcxprojGenerator() : VcprojGenerator()
{
}
VCProjectWriter *VcxprojGenerator::createProjectWriter()
{
return new VCXProjectWriter;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,22 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MSVC_VCXPROJ_H
#define MSVC_VCXPROJ_H
#include "msvc_vcproj.h"
QT_BEGIN_NAMESPACE
class VcxprojGenerator : public VcprojGenerator
{
public:
VcxprojGenerator();
protected:
VCProjectWriter *createProjectWriter() override;
};
QT_END_NAMESPACE
#endif // MSVC_VCXPROJ_H

View File

@ -0,0 +1,861 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "winmakefile.h"
#include "option.h"
#include "project.h"
#include "meta.h"
#include <qtextstream.h>
#include <qstring.h>
#include <qhash.h>
#include <qregularexpression.h>
#include <qstringlist.h>
#include <qdir.h>
#include <stdlib.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
ProString Win32MakefileGenerator::fixLibFlag(const ProString &lib)
{
if (lib.startsWith("-l")) // Fallback for unresolved -l libs.
return escapeFilePath(lib.mid(2) + QLatin1String(".lib"));
if (lib.startsWith("-L")) // Lib search path. Needed only by -l above.
return QLatin1String("/LIBPATH:")
+ escapeFilePath(Option::fixPathToTargetOS(lib.mid(2).toQString(), false));
return escapeFilePath(Option::fixPathToTargetOS(lib.toQString(), false));
}
MakefileGenerator::LibFlagType
Win32MakefileGenerator::parseLibFlag(const ProString &flag, ProString *arg)
{
LibFlagType ret = MakefileGenerator::parseLibFlag(flag, arg);
if (ret != LibFlagFile)
return ret;
// MSVC compatibility. This should be deprecated.
if (flag.startsWith("/LIBPATH:")) {
*arg = flag.mid(9);
return LibFlagPath;
}
// These are pure qmake inventions. They *really* should be deprecated.
if (flag.startsWith("/L")) {
*arg = flag.mid(2);
return LibFlagPath;
}
if (flag.startsWith("/l")) {
*arg = flag.mid(2);
return LibFlagLib;
}
return LibFlagFile;
}
class LibrarySearchPath : public QMakeLocalFileName
{
public:
LibrarySearchPath() = default;
LibrarySearchPath(const QString &s)
: QMakeLocalFileName(s)
{
}
LibrarySearchPath(QString &&s, bool isDefault = false)
: QMakeLocalFileName(std::move(s)), _default(isDefault)
{
}
bool isDefault() const { return _default; }
private:
bool _default = false;
};
bool
Win32MakefileGenerator::findLibraries(bool linkPrl, bool mergeLflags)
{
ProStringList impexts = project->values("QMAKE_LIB_EXTENSIONS");
if (impexts.isEmpty())
impexts = project->values("QMAKE_EXTENSION_STATICLIB");
QList<LibrarySearchPath> dirs;
int libidx = 0;
for (const ProString &dlib : project->values("QMAKE_DEFAULT_LIBDIRS"))
dirs.append(LibrarySearchPath(dlib.toQString(), true));
static const char * const lflags[] = { "LIBS", "LIBS_PRIVATE",
"QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", nullptr };
for (int i = 0; lflags[i]; i++) {
ProStringList &l = project->values(lflags[i]);
for (ProStringList::Iterator it = l.begin(); it != l.end();) {
const ProString &opt = *it;
ProString arg;
LibFlagType type = parseLibFlag(opt, &arg);
if (type == LibFlagPath) {
const QString argqstr = arg.toQString();
auto dit = std::find_if(dirs.cbegin(), dirs.cend(),
[&argqstr](const LibrarySearchPath &p)
{
return p.real() == argqstr;
});
int idx = dit == dirs.cend()
? -1
: std::distance(dirs.cbegin(), dit);
if (idx >= 0 && idx < libidx) {
it = l.erase(it);
continue;
}
const LibrarySearchPath lp(argqstr);
dirs.insert(libidx++, lp);
(*it) = "-L" + lp.real();
} else if (type == LibFlagLib) {
QString lib = arg.toQString();
ProString verovr =
project->first(ProKey("QMAKE_" + lib.toUpper() + "_VERSION_OVERRIDE"));
for (auto dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
QString cand = (*dir_it).real() + Option::dir_sep + lib;
if (linkPrl && processPrlFile(cand, true)) {
(*it) = cand;
goto found;
}
QString libBase = (*dir_it).local() + '/' + lib + verovr;
for (ProStringList::ConstIterator extit = impexts.cbegin();
extit != impexts.cend(); ++extit) {
if (exists(libBase + '.' + *extit)) {
*it = (dir_it->isDefault() ? lib : cand)
+ verovr + '.' + *extit;
goto found;
}
}
}
// We assume if it never finds it that it's correct
found: ;
} else if (linkPrl && type == LibFlagFile) {
QString lib = opt.toQString();
if (fileInfo(lib).isAbsolute()) {
if (processPrlFile(lib, false))
(*it) = lib;
} else {
for (auto dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) {
QString cand = (*dir_it).real() + Option::dir_sep + lib;
if (processPrlFile(cand, false)) {
(*it) = cand;
break;
}
}
}
}
ProStringList &prl_libs = project->values("QMAKE_CURRENT_PRL_LIBS");
for (int prl = 0; prl < prl_libs.size(); ++prl)
it = l.insert(++it, prl_libs.at(prl));
prl_libs.clear();
++it;
}
if (mergeLflags) {
ProStringList lopts;
for (int lit = 0; lit < l.size(); ++lit) {
ProString opt = l.at(lit);
if (opt.startsWith(QLatin1String("-L"))) {
if (!lopts.contains(opt))
lopts.append(opt);
} else {
// Make sure we keep the dependency order of libraries
lopts.removeAll(opt);
lopts.append(opt);
}
}
l = lopts;
}
}
return true;
}
bool Win32MakefileGenerator::processPrlFileBase(QString &origFile, QStringView origName,
QStringView fixedBase, int slashOff)
{
if (MakefileGenerator::processPrlFileBase(origFile, origName, fixedBase, slashOff))
return true;
for (int off = fixedBase.size(); off > slashOff; off--) {
if (!fixedBase.at(off - 1).isDigit()) {
if (off != fixedBase.size()) {
return MakefileGenerator::processPrlFileBase(
origFile, origName, fixedBase.left(off), slashOff);
}
break;
}
}
return false;
}
void Win32MakefileGenerator::processVars()
{
if (project->first("TEMPLATE").endsWith("aux"))
return;
project->values("PRL_TARGET") =
project->values("QMAKE_ORIG_TARGET") = project->values("TARGET");
if (project->isEmpty("QMAKE_PROJECT_NAME"))
project->values("QMAKE_PROJECT_NAME") = project->values("QMAKE_ORIG_TARGET");
else if (project->first("TEMPLATE").startsWith("vc"))
project->values("MAKEFILE") = project->values("QMAKE_PROJECT_NAME");
project->values("QMAKE_INCDIR") += project->values("QMAKE_INCDIR_POST");
project->values("QMAKE_LIBDIR") += project->values("QMAKE_LIBDIR_POST");
if (!project->values("QMAKE_INCDIR").isEmpty())
project->values("INCLUDEPATH") += project->values("QMAKE_INCDIR");
if (!project->values("VERSION").isEmpty()) {
QStringList l = project->first("VERSION").toQString().split('.');
if (l.size() > 0)
project->values("VER_MAJ").append(l[0]);
if (l.size() > 1)
project->values("VER_MIN").append(l[1]);
}
// TARGET_VERSION_EXT will be used to add a version number onto the target name
if (!project->isActiveConfig("skip_target_version_ext")
&& project->values("TARGET_VERSION_EXT").isEmpty()
&& !project->values("VER_MAJ").isEmpty())
project->values("TARGET_VERSION_EXT").append(project->first("VER_MAJ"));
fixTargetExt();
processRcFileVar();
ProStringList libs;
ProStringList &libDir = project->values("QMAKE_LIBDIR");
for (ProStringList::Iterator libDir_it = libDir.begin(); libDir_it != libDir.end(); ++libDir_it) {
QString lib = (*libDir_it).toQString();
if (!lib.isEmpty()) {
if (lib.endsWith('\\'))
lib.chop(1);
libs << QLatin1String("-L") + lib;
}
}
ProStringList &qmklibs = project->values("LIBS");
qmklibs = libs + qmklibs;
if (project->values("TEMPLATE").contains("app")) {
project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_APP");
project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_APP");
project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_APP");
} else if (project->values("TEMPLATE").contains("lib") && project->isActiveConfig("dll")) {
if(!project->isActiveConfig("plugin") || !project->isActiveConfig("plugin_no_share_shlib_cflags")) {
project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_SHLIB");
project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_SHLIB");
}
if (project->isActiveConfig("plugin")) {
project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_PLUGIN");
project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_PLUGIN");
project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_PLUGIN");
} else {
project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_SHLIB");
}
}
}
void Win32MakefileGenerator::fixTargetExt()
{
if (!project->values("QMAKE_APP_FLAG").isEmpty()) {
project->values("TARGET_EXT").append(".exe");
} else if (project->isActiveConfig("shared")) {
project->values("LIB_TARGET").prepend(project->first("QMAKE_PREFIX_STATICLIB")
+ project->first("TARGET") + project->first("TARGET_VERSION_EXT")
+ '.' + project->first("QMAKE_EXTENSION_STATICLIB"));
project->values("TARGET_EXT").append(project->first("TARGET_VERSION_EXT") + "."
+ project->first("QMAKE_EXTENSION_SHLIB"));
project->values("TARGET").first() = project->first("QMAKE_PREFIX_SHLIB") + project->first("TARGET");
} else {
project->values("TARGET_EXT").append("." + project->first("QMAKE_EXTENSION_STATICLIB"));
project->values("TARGET").first() = project->first("QMAKE_PREFIX_STATICLIB") + project->first("TARGET");
project->values("LIB_TARGET").prepend(project->first("TARGET") + project->first("TARGET_EXT")); // for the .prl only
}
}
void Win32MakefileGenerator::processRcFileVar()
{
if (Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING)
return;
const QString manifestFile = project->first("QMAKE_MANIFEST").toQString();
if (((!project->values("VERSION").isEmpty() || !project->values("RC_ICONS").isEmpty() || !manifestFile.isEmpty())
&& project->values("RC_FILE").isEmpty()
&& project->values("RES_FILE").isEmpty()
&& !project->isActiveConfig("no_generated_target_info")
&& (project->isActiveConfig("shared") || !project->values("QMAKE_APP_FLAG").isEmpty()))
|| !project->values("QMAKE_WRITE_DEFAULT_RC").isEmpty()){
QByteArray rcString;
QTextStream ts(&rcString, QFile::WriteOnly);
QStringList vers = project->first("VERSION").toQString().split(".", Qt::SkipEmptyParts);
for (int i = vers.size(); i < 4; i++)
vers += "0";
QString versionString = vers.join('.');
QStringList rcIcons;
const auto icons = project->values("RC_ICONS");
rcIcons.reserve(icons.size());
for (const ProString &icon : icons)
rcIcons.append(fileFixify(icon.toQString(), FileFixifyAbsolute));
QString companyName;
if (!project->values("QMAKE_TARGET_COMPANY").isEmpty())
companyName = project->values("QMAKE_TARGET_COMPANY").join(' ');
QString description;
if (!project->values("QMAKE_TARGET_DESCRIPTION").isEmpty())
description = project->values("QMAKE_TARGET_DESCRIPTION").join(' ');
QString copyright;
if (!project->values("QMAKE_TARGET_COPYRIGHT").isEmpty())
copyright = project->values("QMAKE_TARGET_COPYRIGHT").join(' ');
QString productName;
if (!project->values("QMAKE_TARGET_PRODUCT").isEmpty())
productName = project->values("QMAKE_TARGET_PRODUCT").join(' ');
else
productName = project->first("TARGET").toQString();
QString originalName;
if (!project->values("QMAKE_TARGET_ORIGINAL_FILENAME").isEmpty())
originalName = project->values("QMAKE_TARGET_ORIGINAL_FILENAME").join(' ');
else
originalName = project->first("TARGET") + project->first("TARGET_EXT");
QString internalName;
if (!project->values("QMAKE_TARGET_INTERNALNAME").isEmpty())
internalName = project->values("QMAKE_TARGET_INTERNALNAME").join(' ');
QString comments;
if (!project->values("QMAKE_TARGET_COMMENTS").isEmpty())
comments = project->values("QMAKE_TARGET_COMMENTS").join(' ');
QString trademarks;
if (!project->values("QMAKE_TARGET_TRADEMARKS").isEmpty())
trademarks = project->values("QMAKE_TARGET_TRADEMARKS").join(' ');
int rcLang = project->intValue("RC_LANG", 1033); // default: English(USA)
int rcCodePage = project->intValue("RC_CODEPAGE", 1200); // default: Unicode
ts << "#include <windows.h>\n";
ts << Qt::endl;
if (!rcIcons.isEmpty()) {
for (int i = 0; i < rcIcons.size(); ++i)
ts << QString("IDI_ICON%1\tICON\t%2").arg(i + 1).arg(cQuoted(rcIcons[i])) << Qt::endl;
ts << Qt::endl;
}
if (!manifestFile.isEmpty()) {
QString manifestResourceId;
if (project->first("TEMPLATE") == "lib")
manifestResourceId = QStringLiteral("ISOLATIONAWARE_MANIFEST_RESOURCE_ID");
else
manifestResourceId = QStringLiteral("CREATEPROCESS_MANIFEST_RESOURCE_ID");
ts << manifestResourceId << " RT_MANIFEST \"" << manifestFile << "\"\n";
}
ts << "VS_VERSION_INFO VERSIONINFO\n";
ts << "\tFILEVERSION " << QString(versionString).replace(".", ",") << Qt::endl;
ts << "\tPRODUCTVERSION " << QString(versionString).replace(".", ",") << Qt::endl;
ts << "\tFILEFLAGSMASK 0x3fL\n";
ts << "#ifdef _DEBUG\n";
ts << "\tFILEFLAGS VS_FF_DEBUG\n";
ts << "#else\n";
ts << "\tFILEFLAGS 0x0L\n";
ts << "#endif\n";
ts << "\tFILEOS VOS_NT_WINDOWS32\n";
if (project->isActiveConfig("shared"))
ts << "\tFILETYPE VFT_DLL\n";
else
ts << "\tFILETYPE VFT_APP\n";
ts << "\tFILESUBTYPE VFT2_UNKNOWN\n";
ts << "\tBEGIN\n";
ts << "\t\tBLOCK \"StringFileInfo\"\n";
ts << "\t\tBEGIN\n";
ts << "\t\t\tBLOCK \""
<< QString("%1%2").arg(rcLang, 4, 16, QLatin1Char('0')).arg(rcCodePage, 4, 16, QLatin1Char('0'))
<< "\"\n";
ts << "\t\t\tBEGIN\n";
ts << "\t\t\t\tVALUE \"CompanyName\", \"" << companyName << "\\0\"\n";
ts << "\t\t\t\tVALUE \"FileDescription\", \"" << description << "\\0\"\n";
ts << "\t\t\t\tVALUE \"FileVersion\", \"" << versionString << "\\0\"\n";
ts << "\t\t\t\tVALUE \"LegalCopyright\", \"" << copyright << "\\0\"\n";
ts << "\t\t\t\tVALUE \"OriginalFilename\", \"" << originalName << "\\0\"\n";
ts << "\t\t\t\tVALUE \"ProductName\", \"" << productName << "\\0\"\n";
ts << "\t\t\t\tVALUE \"ProductVersion\", \"" << versionString << "\\0\"\n";
ts << "\t\t\t\tVALUE \"InternalName\", \"" << internalName << "\\0\"\n";
ts << "\t\t\t\tVALUE \"Comments\", \"" << comments << "\\0\"\n";
ts << "\t\t\t\tVALUE \"LegalTrademarks\", \"" << trademarks << "\\0\"\n";
ts << "\t\t\tEND\n";
ts << "\t\tEND\n";
ts << "\t\tBLOCK \"VarFileInfo\"\n";
ts << "\t\tBEGIN\n";
ts << "\t\t\tVALUE \"Translation\", "
<< QString("0x%1").arg(rcLang, 4, 16, QLatin1Char('0'))
<< ", " << QString("%1").arg(rcCodePage, 4) << Qt::endl;
ts << "\t\tEND\n";
ts << "\tEND\n";
ts << "/* End of Version info */\n";
ts << Qt::endl;
ts.flush();
QString rcFilename = project->first("OUT_PWD")
+ "/"
+ project->first("TARGET")
+ "_resource"
+ ".rc";
QFile rcFile(QDir::cleanPath(rcFilename));
bool writeRcFile = true;
if (rcFile.exists() && rcFile.open(QFile::ReadOnly)) {
writeRcFile = rcFile.readAll() != rcString;
rcFile.close();
}
if (writeRcFile) {
bool ok;
ok = rcFile.open(QFile::WriteOnly);
if (!ok) {
// The file can't be opened... try creating the containing
// directory first (needed for clean shadow builds)
QDir().mkpath(QFileInfo(rcFile).path());
ok = rcFile.open(QFile::WriteOnly);
}
if (!ok) {
::fprintf(stderr, "Cannot open for writing: %s", rcFile.fileName().toLatin1().constData());
::exit(1);
}
rcFile.write(rcString);
rcFile.close();
}
if (project->values("QMAKE_WRITE_DEFAULT_RC").isEmpty())
project->values("RC_FILE").insert(0, rcFile.fileName());
}
if (!project->values("RC_FILE").isEmpty()) {
if (!project->values("RES_FILE").isEmpty()) {
fprintf(stderr, "Both rc and res file specified.\n");
fprintf(stderr, "Please specify one of them, not both.");
exit(1);
}
QString resFile = project->first("RC_FILE").toQString();
// if this is a shadow build then use the absolute path of the rc file
if (Option::output_dir != qmake_getpwd()) {
QFileInfo fi(resFile);
project->values("RC_FILE").first() = fi.absoluteFilePath();
}
resFile.replace(QLatin1String(".rc"), Option::res_ext);
project->values("RES_FILE").prepend(fileInfo(resFile).fileName());
QString resDestDir;
if (project->isActiveConfig("staticlib"))
resDestDir = project->first("DESTDIR").toQString();
else
resDestDir = project->first("OBJECTS_DIR").toQString();
if (!resDestDir.isEmpty()) {
resDestDir.append(Option::dir_sep);
project->values("RES_FILE").first().prepend(resDestDir);
}
project->values("RES_FILE").first() = Option::fixPathToTargetOS(
project->first("RES_FILE").toQString(), false);
project->values("POST_TARGETDEPS") += project->values("RES_FILE");
project->values("CLEAN_FILES") += project->values("RES_FILE");
}
}
void Win32MakefileGenerator::writeCleanParts(QTextStream &t)
{
t << "clean: compiler_clean " << depVar("CLEAN_DEPS");
{
const char *clean_targets[] = { "OBJECTS", "QMAKE_CLEAN", "CLEAN_FILES", nullptr };
for(int i = 0; clean_targets[i]; ++i) {
const ProStringList &list = project->values(clean_targets[i]);
const QString del_statement("-$(DEL_FILE)");
if(project->isActiveConfig("no_delete_multiple_files")) {
for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
t << "\n\t" << del_statement
<< ' ' << escapeFilePath(Option::fixPathToTargetOS((*it).toQString()));
} else {
QString files, file;
const int commandlineLimit = 2047; // NT limit, expanded
for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
file = ' ' + escapeFilePath(Option::fixPathToTargetOS((*it).toQString()));
if(del_statement.size() + files.size() +
qMax(fixEnvVariables(file).size(), file.size()) > commandlineLimit) {
t << "\n\t" << del_statement << files;
files.clear();
}
files += file;
}
if(!files.isEmpty())
t << "\n\t" << del_statement << files;
}
}
}
t << Qt::endl << Qt::endl;
t << "distclean: clean " << depVar("DISTCLEAN_DEPS");
{
const char *clean_targets[] = { "QMAKE_DISTCLEAN", nullptr };
for(int i = 0; clean_targets[i]; ++i) {
const ProStringList &list = project->values(clean_targets[i]);
const QString del_statement("-$(DEL_FILE)");
if(project->isActiveConfig("no_delete_multiple_files")) {
for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
t << "\n\t" << del_statement << " "
<< escapeFilePath(Option::fixPathToTargetOS((*it).toQString()));
} else {
QString files, file;
const int commandlineLimit = 2047; // NT limit, expanded
for (ProStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
file = " " + escapeFilePath(Option::fixPathToTargetOS((*it).toQString()));
if(del_statement.size() + files.size() +
qMax(fixEnvVariables(file).size(), file.size()) > commandlineLimit) {
t << "\n\t" << del_statement << files;
files.clear();
}
files += file;
}
if(!files.isEmpty())
t << "\n\t" << del_statement << files;
}
}
}
t << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET)\n";
{
QString ofile = fileFixify(Option::output.fileName());
if(!ofile.isEmpty())
t << "\t-$(DEL_FILE) " << escapeFilePath(ofile) << Qt::endl;
}
t << Qt::endl;
}
void Win32MakefileGenerator::writeIncPart(QTextStream &t)
{
t << "INCPATH = ";
const ProStringList &incs = project->values("INCLUDEPATH");
for(int i = 0; i < incs.size(); ++i) {
QString inc = incs.at(i).toQString();
inc.replace(QRegularExpression("\\\\$"), "");
if(!inc.isEmpty())
t << "-I" << escapeFilePath(inc) << ' ';
}
t << Qt::endl;
}
void Win32MakefileGenerator::writeStandardParts(QTextStream &t)
{
writeExportedVariables(t);
t << "####### Compiler, tools and options\n\n";
t << "CC = " << var("QMAKE_CC") << Qt::endl;
t << "CXX = " << var("QMAKE_CXX") << Qt::endl;
t << "DEFINES = "
<< varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ")
<< varGlue("DEFINES","-D"," -D","") << Qt::endl;
t << "CFLAGS = " << var("QMAKE_CFLAGS") << " $(DEFINES)\n";
t << "CXXFLAGS = " << var("QMAKE_CXXFLAGS") << " $(DEFINES)\n";
writeIncPart(t);
writeLibsPart(t);
writeDefaultVariables(t);
t << Qt::endl;
t << "####### Output directory\n\n";
if(!project->values("OBJECTS_DIR").isEmpty())
t << "OBJECTS_DIR = " << escapeFilePath(var("OBJECTS_DIR").remove(QRegularExpression("\\\\$"))) << Qt::endl;
else
t << "OBJECTS_DIR = . \n";
t << Qt::endl;
t << "####### Files\n\n";
t << "SOURCES = " << valList(escapeFilePaths(project->values("SOURCES")))
<< " " << valList(escapeFilePaths(project->values("GENERATED_SOURCES"))) << Qt::endl;
// do this here so we can set DEST_TARGET to be the complete path to the final target if it is needed.
QString orgDestDir = var("DESTDIR");
QString destDir = Option::fixPathToTargetOS(orgDestDir, false);
if (!destDir.isEmpty() && (orgDestDir.endsWith('/') || orgDestDir.endsWith(Option::dir_sep)))
destDir += Option::dir_sep;
QString target = QString(project->first("TARGET")+project->first("TARGET_EXT"));
project->values("DEST_TARGET").prepend(destDir + target);
writeObjectsPart(t);
writeExtraCompilerVariables(t);
writeExtraVariables(t);
t << "DIST = " << fileVarList("DISTFILES") << ' '
<< fileVarList("HEADERS") << ' ' << fileVarList("SOURCES") << Qt::endl;
t << "QMAKE_TARGET = " << fileVar("QMAKE_ORIG_TARGET") << Qt::endl; // unused
// The comment is important to maintain variable compatibility with Unix
// Makefiles, while not interpreting a trailing-slash as a linebreak
t << "DESTDIR = " << escapeFilePath(destDir) << " #avoid trailing-slash linebreak\n";
t << "TARGET = " << escapeFilePath(target) << Qt::endl;
t << "DESTDIR_TARGET = " << fileVar("DEST_TARGET") << Qt::endl;
t << Qt::endl;
writeImplicitRulesPart(t);
t << "####### Build rules\n\n";
writeBuildRulesPart(t);
if (project->first("TEMPLATE") != "aux") {
if (project->isActiveConfig("shared") && !project->values("DLLDESTDIR").isEmpty()) {
const ProStringList &dlldirs = project->values("DLLDESTDIR");
for (ProStringList::ConstIterator dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) {
t << "\t-$(COPY_FILE) $(DESTDIR_TARGET) "
<< escapeFilePath(Option::fixPathToTargetOS((*dlldir).toQString(), false)) << Qt::endl;
}
}
t << Qt::endl;
writeRcFilePart(t);
}
writeMakeQmake(t);
QStringList dist_files = fileFixify(Option::mkfile::project_files);
if(!project->isEmpty("QMAKE_INTERNAL_INCLUDED_FILES"))
dist_files += project->values("QMAKE_INTERNAL_INCLUDED_FILES").toQStringList();
if(!project->isEmpty("TRANSLATIONS"))
dist_files << var("TRANSLATIONS");
if(!project->isEmpty("FORMS")) {
const ProStringList &forms = project->values("FORMS");
for (ProStringList::ConstIterator formit = forms.begin(); formit != forms.end(); ++formit) {
QString ui_h = fileFixify((*formit) + Option::h_ext.first());
if(exists(ui_h))
dist_files << ui_h;
}
}
t << "dist:\n\t"
<< "$(ZIP) " << var("QMAKE_ORIG_TARGET") << ".zip $(SOURCES) $(DIST) "
<< escapeFilePaths(dist_files).join(' ') << ' ' << fileVar("TRANSLATIONS") << ' ';
if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) {
const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
const ProStringList &inputs = project->values(ProKey(*it + ".input"));
for (ProStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) {
const ProStringList &val = project->values((*input).toKey());
t << escapeFilePaths(val).join(' ') << ' ';
}
}
}
t << Qt::endl << Qt::endl;
writeCleanParts(t);
writeExtraTargets(t);
writeExtraCompilerTargets(t);
t << Qt::endl << Qt::endl;
}
void Win32MakefileGenerator::writeLibsPart(QTextStream &t)
{
if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") {
t << "LIBAPP = " << var("QMAKE_LIB") << Qt::endl;
t << "LIBFLAGS = " << var("QMAKE_LIBFLAGS") << Qt::endl;
} else {
t << "LINKER = " << var("QMAKE_LINK") << Qt::endl;
t << "LFLAGS = " << var("QMAKE_LFLAGS") << Qt::endl;
t << "LIBS = " << fixLibFlags("LIBS").join(' ') << ' '
<< fixLibFlags("LIBS_PRIVATE").join(' ') << ' '
<< fixLibFlags("QMAKE_LIBS").join(' ') << ' '
<< fixLibFlags("QMAKE_LIBS_PRIVATE").join(' ') << Qt::endl;
}
}
void Win32MakefileGenerator::writeObjectsPart(QTextStream &t)
{
// Used in both deps and commands.
t << "OBJECTS = " << valList(escapeDependencyPaths(project->values("OBJECTS"))) << Qt::endl;
}
void Win32MakefileGenerator::writeImplicitRulesPart(QTextStream &)
{
}
void Win32MakefileGenerator::writeBuildRulesPart(QTextStream &)
{
}
void Win32MakefileGenerator::writeRcFilePart(QTextStream &t)
{
if(!project->values("RC_FILE").isEmpty()) {
const ProString res_file = project->first("RES_FILE");
const QString rc_file = fileFixify(project->first("RC_FILE").toQString());
const ProStringList rcIncPaths = project->values("RC_INCLUDEPATH");
QString incPathStr;
for (int i = 0; i < rcIncPaths.size(); ++i) {
const ProString &path = rcIncPaths.at(i);
if (path.isEmpty())
continue;
incPathStr += QStringLiteral(" /i ");
incPathStr += escapeFilePath(path);
}
addSourceFile(rc_file, QMakeSourceFileInfo::SEEK_DEPS);
const QStringList rcDeps = QStringList(rc_file) << dependencies(rc_file);
// The resource tool may use defines. This might be the same defines passed in as the
// compiler, since you may use these defines in the .rc file itself.
// As the escape syntax for the command line defines for RC is different from that for CL,
// we might have to set specific defines for RC.
ProString defines = varGlue("RC_DEFINES", " -D", " -D", "");
if (defines.isEmpty())
defines = ProString(" $(DEFINES)");
// Also, we need to add the _DEBUG define manually since the compiler defines this symbol
// by itself, and we use it in the automatically created rc file when VERSION is defined
// in the .pro file.
t << escapeDependencyPath(res_file) << ": "
<< escapeDependencyPaths(rcDeps).join(' ') << "\n\t"
<< var("QMAKE_RC") << (project->isActiveConfig("debug") ? " -D_DEBUG" : "")
<< defines << incPathStr << " -fo " << escapeFilePath(res_file)
<< ' ' << escapeFilePath(rc_file);
t << Qt::endl << Qt::endl;
}
}
QString Win32MakefileGenerator::defaultInstall(const QString &t)
{
if((t != "target" && t != "dlltarget") ||
(t == "dlltarget" && (project->first("TEMPLATE") != "lib" || !project->isActiveConfig("shared"))) ||
project->first("TEMPLATE") == "subdirs" || project->first("TEMPLATE") == "aux")
return QString();
const QString root = installRoot();
ProStringList &uninst = project->values(ProKey(t + ".uninstall"));
QString ret;
QString targetdir = fileFixify(project->first(ProKey(t + ".path")).toQString(), FileFixifyAbsolute);
if(targetdir.right(1) != Option::dir_sep)
targetdir += Option::dir_sep;
const ProStringList &targets = project->values(ProKey(t + ".targets"));
for (int i = 0; i < targets.size(); ++i) {
QString src = targets.at(i).toQString(),
dst = escapeFilePath(filePrefixRoot(root, targetdir + src.section('/', -1)));
if (!ret.isEmpty())
ret += "\n\t";
ret += "$(QINSTALL) " + escapeFilePath(Option::fixPathToTargetOS(src, false)) + ' ' + dst;
if (!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst);
}
if(t == "target" && project->first("TEMPLATE") == "lib") {
if(project->isActiveConfig("create_prl") && !project->isActiveConfig("no_install_prl") &&
!project->isEmpty("QMAKE_INTERNAL_PRL_FILE")) {
QString dst_prl = Option::fixPathToTargetOS(project->first("QMAKE_INTERNAL_PRL_FILE").toQString());
int slsh = dst_prl.lastIndexOf(Option::dir_sep);
if(slsh != -1)
dst_prl = dst_prl.right(dst_prl.size() - slsh - 1);
dst_prl = filePrefixRoot(root, targetdir + dst_prl);
if (!ret.isEmpty())
ret += "\n\t";
ret += installMetaFile(ProKey("QMAKE_PRL_INSTALL_REPLACE"), project->first("QMAKE_INTERNAL_PRL_FILE").toQString(), dst_prl);
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + escapeFilePath(dst_prl));
}
if(project->isActiveConfig("create_pc")) {
QString dst_pc = pkgConfigFileName(false);
if (!dst_pc.isEmpty()) {
dst_pc = filePrefixRoot(root, targetdir + dst_pc);
const QString dst_pc_dir = Option::fixPathToTargetOS(fileInfo(dst_pc).path(), false);
if (!dst_pc_dir.isEmpty()) {
if (!ret.isEmpty())
ret += "\n\t";
ret += mkdir_p_asstring(dst_pc_dir, true);
}
if(!ret.isEmpty())
ret += "\n\t";
ret += installMetaFile(ProKey("QMAKE_PKGCONFIG_INSTALL_REPLACE"), pkgConfigFileName(true), dst_pc);
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + escapeFilePath(dst_pc));
}
}
if(project->isActiveConfig("shared") && !project->isActiveConfig("plugin")) {
ProString lib_target = project->first("LIB_TARGET");
QString src_targ = escapeFilePath(
(project->isEmpty("DESTDIR") ? QString("$(DESTDIR)") : project->first("DESTDIR"))
+ lib_target);
QString dst_targ = escapeFilePath(
filePrefixRoot(root, fileFixify(targetdir + lib_target, FileFixifyAbsolute)));
if(!ret.isEmpty())
ret += "\n\t";
ret += QString("-$(INSTALL_FILE) ") + src_targ + ' ' + dst_targ;
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst_targ);
}
}
if (t == "dlltarget" || project->values(ProKey(t + ".CONFIG")).indexOf("no_dll") == -1) {
QString src_targ = "$(DESTDIR_TARGET)";
QString dst_targ = escapeFilePath(
filePrefixRoot(root, fileFixify(targetdir + "$(TARGET)", FileFixifyAbsolute)));
if(!ret.isEmpty())
ret += "\n\t";
ret += QString("-$(INSTALL_FILE) ") + src_targ + ' ' + dst_targ;
if(!uninst.isEmpty())
uninst.append("\n\t");
uninst.append("-$(DEL_FILE) " + dst_targ);
}
return ret;
}
void Win32MakefileGenerator::writeDefaultVariables(QTextStream &t)
{
MakefileGenerator::writeDefaultVariables(t);
t << "IDC = " << (project->isEmpty("QMAKE_IDC") ? QString("idc") : var("QMAKE_IDC"))
<< Qt::endl;
t << "IDL = " << (project->isEmpty("QMAKE_IDL") ? QString("midl") : var("QMAKE_IDL"))
<< Qt::endl;
t << "ZIP = " << var("QMAKE_ZIP") << Qt::endl;
t << "DEF_FILE = " << fileVar("DEF_FILE") << Qt::endl;
t << "RES_FILE = " << fileVar("RES_FILE") << Qt::endl; // Not on mingw, can't see why not though...
t << "SED = " << var("QMAKE_STREAM_EDITOR") << Qt::endl;
t << "MOVE = " << var("QMAKE_MOVE") << Qt::endl;
}
QString Win32MakefileGenerator::escapeFilePath(const QString &path) const
{
QString ret = path;
if(!ret.isEmpty()) {
if (ret.contains(' ') || ret.contains('\t'))
ret = "\"" + ret + "\"";
debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
}
return ret;
}
QString Win32MakefileGenerator::escapeDependencyPath(const QString &path) const
{
QString ret = path;
if (!ret.isEmpty()) {
static const QRegularExpression criticalChars(QStringLiteral("([\t #])"));
if (ret.contains(criticalChars))
ret = "\"" + ret + "\"";
debug_msg(2, "EscapeDependencyPath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData());
}
return ret;
}
QString Win32MakefileGenerator::cQuoted(const QString &str)
{
QString ret = str;
ret.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
ret.replace(QLatin1Char('"'), QLatin1String("\\\""));
ret.prepend(QLatin1Char('"'));
ret.append(QLatin1Char('"'));
return ret;
}
ProKey Win32MakefileGenerator::fullTargetVariable() const
{
return "DEST_TARGET";
}
QT_END_NAMESPACE

View File

@ -0,0 +1,48 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef WINMAKEFILE_H
#define WINMAKEFILE_H
#include "makefile.h"
QT_BEGIN_NAMESPACE
class Win32MakefileGenerator : public MakefileGenerator
{
protected:
QString defaultInstall(const QString &) override;
void writeDefaultVariables(QTextStream &t) override;
virtual void writeCleanParts(QTextStream &t);
virtual void writeStandardParts(QTextStream &t);
virtual void writeIncPart(QTextStream &t);
virtual void writeLibsPart(QTextStream &t);
virtual void writeObjectsPart(QTextStream &t);
virtual void writeImplicitRulesPart(QTextStream &t);
virtual void writeBuildRulesPart(QTextStream &);
using MakefileGenerator::escapeFilePath;
QString escapeFilePath(const QString &path) const override;
using MakefileGenerator::escapeDependencyPath;
QString escapeDependencyPath(const QString &path) const override;
virtual void writeRcFilePart(QTextStream &t);
bool findLibraries(bool linkPrl, bool mergeLflags) override;
LibFlagType parseLibFlag(const ProString &flag, ProString *arg) override;
ProString fixLibFlag(const ProString &lib) override;
bool processPrlFileBase(QString &origFile, QStringView origName,
QStringView fixedBase, int slashOff) override;
void processVars();
void fixTargetExt();
void processRcFileVar();
static QString cQuoted(const QString &str);
public:
ProKey fullTargetVariable() const override;
};
QT_END_NAMESPACE
#endif // WINMAKEFILE_H

View File

@ -0,0 +1,345 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "xmloutput.h"
QT_BEGIN_NAMESPACE
XmlOutput::XmlOutput(QTextStream &file, ConverstionType type)
: xmlFile(file), indent("\t"), currentLevel(0), currentState(Bare), format(NewLine),
conversion(type)
{
tagStack.clear();
}
XmlOutput::~XmlOutput()
{
closeAll();
}
// Settings ------------------------------------------------------------------
void XmlOutput::setIndentString(const QString &indentString)
{
indent = indentString;
}
QString XmlOutput::indentString()
{
return indent;
}
void XmlOutput::setIndentLevel(int level)
{
currentLevel = level;
}
int XmlOutput::indentLevel()
{
return currentLevel;
}
void XmlOutput::setState(XMLState state)
{
currentState = state;
}
void XmlOutput::setFormat(XMLFormat newFormat)
{
format = newFormat;
}
XmlOutput::XMLState XmlOutput::state()
{
return currentState;
}
void XmlOutput::updateIndent()
{
currentIndent.clear();
currentIndent.reserve(currentLevel);
for (int i = 0; i < currentLevel; ++i)
currentIndent.append(indent);
}
void XmlOutput::increaseIndent()
{
++currentLevel;
updateIndent();
}
void XmlOutput::decreaseIndent()
{
if (currentLevel)
--currentLevel;
updateIndent();
if (!currentLevel)
currentState = Bare;
}
QString XmlOutput::doConversion(const QString &text)
{
if (!text.size())
return QString();
else if (conversion == NoConversion)
return text;
QString output;
if (conversion == XMLConversion) {
// this is a way to escape characters that shouldn't be converted
for (int i=0; i<text.size(); ++i) {
const QChar c = text.at(i);
if (c == QLatin1Char('&')) {
if ( (i + 7) < text.size() &&
text.at(i + 1) == QLatin1Char('#') &&
text.at(i + 2) == QLatin1Char('x') &&
text.at(i + 7) == QLatin1Char(';') ) {
output += text.at(i);
} else {
output += QLatin1String("&amp;");
}
} else if (c == QLatin1Char('<')) {
output += QLatin1String("&lt;");
} else if (c == QLatin1Char('>')) {
output += QLatin1String("&gt;");
} else {
if (c.unicode() < 0x20) {
output += QString("&#x%1;").arg(c.unicode(), 2, 16, QLatin1Char('0'));
} else {
output += c;
}
}
}
} else {
output = text;
}
if (conversion == XMLConversion) {
output.replace('\"', QLatin1String("&quot;"));
output.replace('\'', QLatin1String("&apos;"));
} else if (conversion == EscapeConversion) {
output.replace('\"', QLatin1String("\\\""));
output.replace('\'', QLatin1String("\\\'"));
}
return output;
}
// Stream functions ----------------------------------------------------------
XmlOutput& XmlOutput::operator<<(const QString& o)
{
return operator<<(data(o));
}
XmlOutput& XmlOutput::operator<<(const xml_output& o)
{
switch(o.xo_type) {
case tNothing:
break;
case tRaw:
addRaw(o.xo_text);
break;
case tDeclaration:
addDeclaration(o.xo_text, o.xo_value);
break;
case tTag:
newTagOpen(o.xo_text);
break;
case tTagValue:
addRaw(QString("\n%1<%2>").arg(currentIndent).arg(o.xo_text));
addRaw(doConversion(o.xo_value));
addRaw(QString("</%1>").arg(o.xo_text));
break;
case tValueTag:
addRaw(doConversion(o.xo_text));
setFormat(NoNewLine);
closeTag();
setFormat(NewLine);
break;
case tImport:
addRaw(QString("\n%1<Import %2=\"%3\" />").arg(currentIndent).arg(o.xo_text).arg(o.xo_value));
break;
case tCloseTag:
if (o.xo_value.size())
closeAll();
else if (o.xo_text.size())
closeTo(o.xo_text);
else
closeTag();
break;
case tAttribute:
addAttribute(o.xo_text, o.xo_value);
break;
case tAttributeTag:
addAttributeTag(o.xo_text, o.xo_value);
break;
case tData:
{
// Special case to be able to close tag in normal
// way ("</tag>", not "/>") without using addRaw()..
if (!o.xo_text.size()) {
closeOpen();
break;
}
QString output = doConversion(o.xo_text);
output.replace('\n', "\n" + currentIndent);
addRaw(QString("\n%1%2").arg(currentIndent).arg(output));
}
break;
case tComment:
{
QString output("<!--%1-->");
addRaw(output.arg(o.xo_text));
}
break;
case tCDATA:
{
QString output("<![CDATA[\n%1\n]]>");
addRaw(output.arg(o.xo_text));
}
break;
}
return *this;
}
// Output functions ----------------------------------------------------------
void XmlOutput::newTag(const QString &tag)
{
Q_ASSERT_X(tag.size(), "XmlOutput", "Cannot open an empty tag");
newTagOpen(tag);
closeOpen();
}
void XmlOutput::newTagOpen(const QString &tag)
{
Q_ASSERT_X(tag.size(), "XmlOutput", "Cannot open an empty tag");
closeOpen();
if (format == NewLine)
xmlFile << Qt::endl << currentIndent;
xmlFile << '<' << doConversion(tag);
currentState = Attribute;
tagStack.append(tag);
increaseIndent(); // ---> indent
}
void XmlOutput::closeOpen()
{
switch(currentState) {
case Bare:
case Tag:
return;
case Attribute:
break;
}
xmlFile << '>';
currentState = Tag;
}
void XmlOutput::closeTag()
{
switch(currentState) {
case Bare:
if (tagStack.size())
//warn_msg(WarnLogic, "<Root>: Cannot close tag in Bare state, %d tags on stack", tagStack.count());
qDebug("<Root>: Cannot close tag in Bare state, %d tags on stack", int(tagStack.size()));
else
//warn_msg(WarnLogic, "<Root>: Cannot close tag, no tags on stack");
qDebug("<Root>: Cannot close tag, no tags on stack");
return;
case Tag:
decreaseIndent(); // <--- Pre-decrease indent
if (format == NewLine)
xmlFile << Qt::endl << currentIndent;
xmlFile << "</" << doConversion(tagStack.last()) << '>';
tagStack.pop_back();
break;
case Attribute:
xmlFile << " />";
tagStack.pop_back();
currentState = Tag;
decreaseIndent(); // <--- Post-decrease indent
break;
}
}
void XmlOutput::closeTo(const QString &tag)
{
bool cont = true;
if (!tagStack.contains(tag) && !tag.isNull()) {
//warn_msg(WarnLogic, "<%s>: Cannot close to tag <%s>, not on stack", tagStack.last().latin1(), tag.latin1());
qDebug("<%s>: Cannot close to tag <%s>, not on stack", tagStack.last().toLatin1().constData(), tag.toLatin1().constData());
return;
}
int left = tagStack.size();
while (left-- && cont) {
cont = tagStack.last().compare(tag) != 0;
closeTag();
}
}
void XmlOutput::closeAll()
{
if (!tagStack.size())
return;
closeTo(QString());
}
void XmlOutput::addDeclaration(const QString &version, const QString &encoding)
{
switch(currentState) {
case Bare:
break;
case Tag:
case Attribute:
//warn_msg(WarnLogic, "<%s>: Cannot add declaration when not in bare state", tagStack.last().toLatin1().constData());
qDebug("<%s>: Cannot add declaration when not in bare state", tagStack.last().toLatin1().constData());
return;
}
QString outData = QString("<?xml version=\"%1\" encoding=\"%2\"?>")
.arg(doConversion(version))
.arg(doConversion(encoding));
addRaw(outData);
}
void XmlOutput::addRaw(const QString &rawText)
{
closeOpen();
xmlFile << rawText;
}
void XmlOutput::addAttribute(const QString &attribute, const QString &value)
{
switch(currentState) {
case Bare:
case Tag:
//warn_msg(WarnLogic, "<%s>: Cannot add attribute since tags not open", tagStack.last().toLatin1().constData());
qDebug("<%s>: Cannot add attribute (%s) since tag's not open",
(tagStack.size() ? tagStack.last().toLatin1().constData() : "Root"),
attribute.toLatin1().constData());
return;
case Attribute:
break;
}
if (format == NewLine)
xmlFile << Qt::endl;
xmlFile << currentIndent << doConversion(attribute) << "=\"" << doConversion(value) << "\"";
}
void XmlOutput::addAttributeTag(const QString &attribute, const QString &value)
{
switch(currentState) {
case Bare:
case Tag:
//warn_msg(WarnLogic, "<%s>: Cannot add attribute since tags not open", tagStack.last().toLatin1().constData());
qDebug("<%s>: Cannot add attribute (%s) since tag's not open",
(tagStack.size() ? tagStack.last().toLatin1().constData() : "Root"),
attribute.toLatin1().constData());
return;
case Attribute:
break;
}
xmlFile << " " << doConversion(attribute) << "=\"" << doConversion(value) << "\"";
}
QT_END_NAMESPACE

View File

@ -0,0 +1,206 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef XMLOUTPUT_H
#define XMLOUTPUT_H
#include <qtextstream.h>
#include <qstack.h>
QT_BEGIN_NAMESPACE
class XmlOutput
{
public:
enum ConverstionType {
NoConversion, // No change
EscapeConversion, // Use '\"'
XMLConversion // Use &quot;
};
enum XMLFormat {
NoNewLine, // No new lines, unless added manually
NewLine // All properties & tags indented on new lines
};
enum XMLState {
Bare, // Not in tag or attribute
Tag, // <tagname attribute1="value"
Attribute // attribute2="value">
};
enum XMLType {
tNothing, // No XML output, and not state change
tRaw, // Raw text (no formating)
tDeclaration, // <?xml version="x.x" encoding="xxx"?>
tTag, // <tagname attribute1="value"
tTagValue, // <tagname>value</tagname>
tValueTag, // value</tagname>
tCloseTag, // Closes an open tag
tAttribute, // attribute2="value">
tAttributeTag, // attribute on the same line as a tag
tData, // Tag data (formating done)
tImport, // <import "type"="path" />
tComment, // <!-- Comment -->
tCDATA // <![CDATA[ ... ]]>
};
XmlOutput(QTextStream &file, ConverstionType type = XMLConversion);
~XmlOutput();
// Settings
void setIndentString(const QString &indentString);
QString indentString();
void setIndentLevel(int level);
int indentLevel();
void setState(XMLState state);
void setFormat(XMLFormat newFormat);
XMLState state();
struct xml_output {
XMLType xo_type; // Type of struct instance
QString xo_text; // Tag/Attribute name/xml version
QString xo_value; // Value of attributes/xml encoding
xml_output(XMLType type, const QString &text, const QString &value)
: xo_type(type), xo_text(text), xo_value(value) {}
xml_output(const xml_output &xo)
: xo_type(xo.xo_type), xo_text(xo.xo_text), xo_value(xo.xo_value) {}
};
// Streams
XmlOutput& operator<<(const QString& o);
XmlOutput& operator<<(const xml_output& o);
private:
void increaseIndent();
void decreaseIndent();
void updateIndent();
QString doConversion(const QString &text);
// Output functions
void newTag(const QString &tag);
void newTagOpen(const QString &tag);
void closeOpen();
void closeTag();
void closeTo(const QString &tag);
void closeAll();
void addDeclaration(const QString &version, const QString &encoding);
void addRaw(const QString &rawText);
void addAttribute(const QString &attribute, const QString &value);
void addAttributeTag(const QString &attribute, const QString &value);
void addData(const QString &data);
// Data
QTextStream &xmlFile;
QString indent;
QString currentIndent;
int currentLevel;
XMLState currentState;
XMLFormat format;
ConverstionType conversion;
QStack<QString> tagStack;
};
inline XmlOutput::xml_output noxml()
{
return XmlOutput::xml_output(XmlOutput::tNothing, QString(), QString());
}
inline XmlOutput::xml_output raw(const QString &rawText)
{
return XmlOutput::xml_output(XmlOutput::tRaw, rawText, QString());
}
inline XmlOutput::xml_output declaration(const QString &version = QString("1.0"),
const QString &encoding = QString())
{
return XmlOutput::xml_output(XmlOutput::tDeclaration, version, encoding);
}
inline XmlOutput::xml_output decl(const QString &version = QString("1.0"),
const QString &encoding = QString())
{
return declaration(version, encoding);
}
inline XmlOutput::xml_output tag(const QString &name)
{
return XmlOutput::xml_output(XmlOutput::tTag, name, QString());
}
inline XmlOutput::xml_output valueTag(const QString &value)
{
return XmlOutput::xml_output(XmlOutput::tValueTag, value, QString());
}
inline XmlOutput::xml_output tagValue(const QString &tagName, const QString &value)
{
return XmlOutput::xml_output(XmlOutput::tTagValue, tagName, value);
}
inline XmlOutput::xml_output import(const QString &tagName, const QString &value)
{
return XmlOutput::xml_output(XmlOutput::tImport, tagName, value);
}
inline XmlOutput::xml_output closetag()
{
return XmlOutput::xml_output(XmlOutput::tCloseTag, QString(), QString());
}
inline XmlOutput::xml_output closetag(const QString &toTag)
{
return XmlOutput::xml_output(XmlOutput::tCloseTag, toTag, QString());
}
inline XmlOutput::xml_output closeall()
{
return XmlOutput::xml_output(XmlOutput::tCloseTag, QString(), QString("all"));
}
inline XmlOutput::xml_output attribute(const QString &name,
const QString &value)
{
return XmlOutput::xml_output(XmlOutput::tAttribute, name, value);
}
inline XmlOutput::xml_output attributeTag(const QString &name,
const QString &value)
{
return XmlOutput::xml_output(XmlOutput::tAttributeTag, name, value);
}
inline XmlOutput::xml_output attr(const QString &name,
const QString &value)
{
return attribute(name, value);
}
inline XmlOutput::xml_output attrTag(const QString &name,
const QString &value)
{
return attributeTag(name, value);
}
inline XmlOutput::xml_output data(const QString &text = QString())
{
return XmlOutput::xml_output(XmlOutput::tData, text, QString());
}
inline XmlOutput::xml_output comment(const QString &text)
{
return XmlOutput::xml_output(XmlOutput::tComment, text, QString());
}
inline XmlOutput::xml_output cdata(const QString &text)
{
return XmlOutput::xml_output(XmlOutput::tCDATA, text, QString());
}
QT_END_NAMESPACE
#endif // XMLOUTPUT_H

300
qmake/library/ioutils.cpp Normal file
View File

@ -0,0 +1,300 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "ioutils.h"
#include <qdir.h>
#include <qfile.h>
#include <qregularexpression.h>
#ifdef Q_OS_WIN
# include <qt_windows.h>
# include <private/qsystemerror_p.h>
#else
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <utime.h>
# include <fcntl.h>
# include <errno.h>
#endif
#define fL1S(s) QString::fromLatin1(s)
QT_BEGIN_NAMESPACE
using namespace QMakeInternal;
QString IoUtils::binaryAbsLocation(const QString &argv0)
{
QString ret;
if (!argv0.isEmpty() && isAbsolutePath(argv0)) {
ret = argv0;
} else if (argv0.contains(QLatin1Char('/'))
#ifdef Q_OS_WIN
|| argv0.contains(QLatin1Char('\\'))
#endif
) { // relative PWD
ret = QDir::current().absoluteFilePath(argv0);
} else { // in the PATH
QByteArray pEnv = qgetenv("PATH");
QDir currentDir = QDir::current();
#ifdef Q_OS_WIN
QStringList paths = QString::fromLocal8Bit(pEnv).split(QLatin1String(";"));
paths.prepend(QLatin1String("."));
#else
QStringList paths = QString::fromLocal8Bit(pEnv).split(QLatin1String(":"));
#endif
for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) {
if ((*p).isEmpty())
continue;
QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + argv0);
if (QFile::exists(candidate)) {
ret = candidate;
break;
}
}
}
return QDir::cleanPath(ret);
}
IoUtils::FileType IoUtils::fileType(const QString &fileName)
{
Q_ASSERT(fileName.isEmpty() || isAbsolutePath(fileName));
#ifdef Q_OS_WIN
DWORD attr = GetFileAttributesW((WCHAR*)fileName.utf16());
if (attr == INVALID_FILE_ATTRIBUTES)
return FileNotFound;
return (attr & FILE_ATTRIBUTE_DIRECTORY) ? FileIsDir : FileIsRegular;
#else
struct ::stat st;
if (::stat(fileName.toLocal8Bit().constData(), &st))
return FileNotFound;
return S_ISDIR(st.st_mode) ? FileIsDir : S_ISREG(st.st_mode) ? FileIsRegular : FileNotFound;
#endif
}
bool IoUtils::isRelativePath(const QString &path)
{
#ifdef QMAKE_BUILTIN_PRFS
if (path.startsWith(QLatin1String(":/")))
return false;
#endif
#ifdef Q_OS_WIN
// Unlike QFileInfo, this considers only paths with both a drive prefix and
// a subsequent (back-)slash absolute:
if (path.length() >= 3 && path.at(1) == QLatin1Char(':') && path.at(0).isLetter()
&& (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\'))) {
return false;
}
// ... unless, of course, they're UNC:
if (path.length() >= 2
&& (path.at(0).unicode() == '\\' || path.at(0).unicode() == '/')
&& path.at(1) == path.at(0)) {
return false;
}
#else
if (path.startsWith(QLatin1Char('/')))
return false;
#endif // Q_OS_WIN
return true;
}
QStringView IoUtils::pathName(const QString &fileName)
{
return QStringView{fileName}.left(fileName.lastIndexOf(QLatin1Char('/')) + 1);
}
QStringView IoUtils::fileName(const QString &fileName)
{
return QStringView(fileName).mid(fileName.lastIndexOf(QLatin1Char('/')) + 1);
}
QString IoUtils::resolvePath(const QString &baseDir, const QString &fileName)
{
if (fileName.isEmpty())
return QString();
if (isAbsolutePath(fileName))
return QDir::cleanPath(fileName);
#ifdef Q_OS_WIN // Add drive to otherwise-absolute path:
if (fileName.at(0).unicode() == '/' || fileName.at(0).unicode() == '\\') {
Q_ASSERT_X(isAbsolutePath(baseDir), "IoUtils::resolvePath", qUtf8Printable(baseDir));
return QDir::cleanPath(baseDir.left(2) + fileName);
}
#endif // Q_OS_WIN
return QDir::cleanPath(baseDir + QLatin1Char('/') + fileName);
}
inline static
bool isSpecialChar(ushort c, const uchar (&iqm)[16])
{
if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
return true;
return false;
}
inline static
bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
{
for (int x = arg.size() - 1; x >= 0; --x) {
if (isSpecialChar(arg.unicode()[x].unicode(), iqm))
return true;
}
return false;
}
QString IoUtils::shellQuoteUnix(const QString &arg)
{
// Chars that should be quoted (TM). This includes:
static const uchar iqm[] = {
0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
}; // 0-32 \'"$`<>|;&(){}*?#!~[]
if (!arg.size())
return QString::fromLatin1("''");
QString ret(arg);
if (hasSpecialChars(ret, iqm)) {
ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
ret.prepend(QLatin1Char('\''));
ret.append(QLatin1Char('\''));
}
return ret;
}
QString IoUtils::shellQuoteWin(const QString &arg)
{
// Chars that should be quoted (TM). This includes:
// - control chars & space
// - the shell meta chars "&()<>^|
// - the potential separators ,;=
static const uchar iqm[] = {
0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
};
// Shell meta chars that need escaping.
static const uchar ism[] = {
0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, 0x50,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
}; // &()<>^|
if (!arg.size())
return QString::fromLatin1("\"\"");
QString ret(arg);
if (hasSpecialChars(ret, iqm)) {
// The process-level standard quoting allows escaping quotes with backslashes (note
// that backslashes don't escape themselves, unless they are followed by a quote).
// Consequently, quotes are escaped and their preceding backslashes are doubled.
ret.replace(QRegularExpression(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
// Trailing backslashes must be doubled as well, as they are followed by a quote.
ret.replace(QRegularExpression(QLatin1String("(\\\\+)$")), QLatin1String("\\1\\1"));
// However, the shell also interprets the command, and no backslash-escaping exists
// there - a quote always toggles the quoting state, but is nonetheless passed down
// to the called process verbatim. In the unquoted state, the circumflex escapes
// meta chars (including itself and quotes), and is removed from the command.
bool quoted = true;
for (int i = 0; i < ret.size(); i++) {
QChar c = ret.unicode()[i];
if (c.unicode() == '"')
quoted = !quoted;
else if (!quoted && isSpecialChar(c.unicode(), ism))
ret.insert(i++, QLatin1Char('^'));
}
if (!quoted)
ret.append(QLatin1Char('^'));
ret.append(QLatin1Char('"'));
ret.prepend(QLatin1Char('"'));
}
return ret;
}
#if defined(PROEVALUATOR_FULL)
bool IoUtils::touchFile(const QString &targetFileName, const QString &referenceFileName, QString *errorString)
{
# ifdef Q_OS_UNIX
struct stat st;
if (stat(referenceFileName.toLocal8Bit().constData(), &st)) {
*errorString = fL1S("Cannot stat() reference file %1: %2.").arg(referenceFileName, fL1S(strerror(errno)));
return false;
}
# if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L
const struct timespec times[2] = { { 0, UTIME_NOW }, st.st_mtim };
const bool utimeError = utimensat(AT_FDCWD, targetFileName.toLocal8Bit().constData(), times, 0) < 0;
# else
struct utimbuf utb;
utb.actime = time(0);
utb.modtime = st.st_mtime;
const bool utimeError= utime(targetFileName.toLocal8Bit().constData(), &utb) < 0;
# endif
if (utimeError) {
*errorString = fL1S("Cannot touch %1: %2.").arg(targetFileName, fL1S(strerror(errno)));
return false;
}
# else
HANDLE rHand = CreateFile((wchar_t*)referenceFileName.utf16(),
GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (rHand == INVALID_HANDLE_VALUE) {
*errorString = fL1S("Cannot open reference file %1: %2")
.arg(referenceFileName, QSystemError::windowsString());
return false;
}
FILETIME ft;
GetFileTime(rHand, NULL, NULL, &ft);
CloseHandle(rHand);
HANDLE wHand = CreateFile((wchar_t*)targetFileName.utf16(),
GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (wHand == INVALID_HANDLE_VALUE) {
*errorString = fL1S("Cannot open %1: %2")
.arg(targetFileName, QSystemError::windowsString());
return false;
}
SetFileTime(wHand, NULL, NULL, &ft);
CloseHandle(wHand);
# endif
return true;
}
#if defined(QT_BUILD_QMAKE) && defined(Q_OS_UNIX)
bool IoUtils::readLinkTarget(const QString &symlinkPath, QString *target)
{
const QByteArray localSymlinkPath = QFile::encodeName(symlinkPath);
# if defined(__GLIBC__) && !defined(PATH_MAX)
# define PATH_CHUNK_SIZE 256
char *s = 0;
int len = -1;
int size = PATH_CHUNK_SIZE;
forever {
s = (char *)::realloc(s, size);
len = ::readlink(localSymlinkPath.constData(), s, size);
if (len < 0) {
::free(s);
break;
}
if (len < size)
break;
size *= 2;
}
# else
char s[PATH_MAX+1];
int len = readlink(localSymlinkPath.constData(), s, PATH_MAX);
# endif
if (len <= 0)
return false;
*target = QFile::decodeName(QByteArray(s, len));
# if defined(__GLIBC__) && !defined(PATH_MAX)
::free(s);
# endif
return true;
}
#endif
#endif // PROEVALUATOR_FULL
QT_END_NAMESPACE

55
qmake/library/ioutils.h Normal file
View File

@ -0,0 +1,55 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef IOUTILS_H
#define IOUTILS_H
#include "qmake_global.h"
#include <qstring.h>
QT_BEGIN_NAMESPACE
namespace QMakeInternal {
/*!
This class provides replacement functionality for QFileInfo, QFile & QDir,
as these are abysmally slow.
*/
class QMAKE_EXPORT IoUtils {
public:
enum FileType {
FileNotFound = 0,
FileIsRegular = 1,
FileIsDir = 2
};
static QString binaryAbsLocation(const QString &argv0);
static FileType fileType(const QString &fileName);
static bool exists(const QString &fileName) { return fileType(fileName) != FileNotFound; }
static bool isRelativePath(const QString &fileName);
static bool isAbsolutePath(const QString &fileName) { return !isRelativePath(fileName); }
static QStringView pathName(const QString &fileName); // Requires normalized path
static QStringView fileName(const QString &fileName); // Requires normalized path
static QString resolvePath(const QString &baseDir, const QString &fileName);
static QString shellQuoteUnix(const QString &arg);
static QString shellQuoteWin(const QString &arg);
static QString shellQuote(const QString &arg)
#ifdef Q_OS_UNIX
{ return shellQuoteUnix(arg); }
#else
{ return shellQuoteWin(arg); }
#endif
#if defined(PROEVALUATOR_FULL)
static bool touchFile(const QString &targetFileName, const QString &referenceFileName, QString *errorString);
# if defined(QT_BUILD_QMAKE) && defined(Q_OS_UNIX)
static bool readLinkTarget(const QString &symlinkPath, QString *target);
# endif
#endif
};
} // namespace ProFileEvaluatorInternal
QT_END_NAMESPACE
#endif // IOUTILS_H

468
qmake/library/proitems.cpp Normal file
View File

@ -0,0 +1,468 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "proitems.h"
#include <qfileinfo.h>
#include <qset.h>
#include <qstringlist.h>
#include <qtextstream.h>
#include <private/qduplicatetracker_p.h>
QT_BEGIN_NAMESPACE
// from qhash.cpp
size_t ProString::hash(const QChar *p, int n)
{
size_t h = 0;
while (n--) {
h = (h << 4) + (*p++).unicode();
h ^= (h & 0xf0000000) >> 23;
h &= 0x0fffffff;
}
return h;
}
ProString::ProString() :
m_offset(0), m_length(0), m_file(0), m_hash(0x80000000)
{
}
ProString::ProString(const ProString &other) :
m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(other.m_hash)
{
}
ProString::ProString(const ProString &other, OmitPreHashing) :
m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(0x80000000)
{
}
ProString::ProString(const QString &str, DoPreHashing) :
m_string(str), m_offset(0), m_length(str.size()), m_file(0)
{
updatedHash();
}
ProString::ProString(const QString &str) :
m_string(str), m_offset(0), m_length(str.size()), m_file(0), m_hash(0x80000000)
{
}
ProString::ProString(QStringView str) :
m_string(str.toString()), m_offset(0), m_length(str.size()), m_file(0), m_hash(0x80000000)
{
}
ProString::ProString(const char *str, DoPreHashing) :
m_string(QString::fromLatin1(str)), m_offset(0), m_length(int(qstrlen(str))), m_file(0)
{
updatedHash();
}
ProString::ProString(const char *str) :
m_string(QString::fromLatin1(str)), m_offset(0), m_length(int(qstrlen(str))), m_file(0), m_hash(0x80000000)
{
}
ProString::ProString(const QString &str, int offset, int length, DoPreHashing) :
m_string(str), m_offset(offset), m_length(length), m_file(0)
{
updatedHash();
}
ProString::ProString(const QString &str, int offset, int length, uint hash) :
m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(hash)
{
}
ProString::ProString(const QString &str, int offset, int length) :
m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(0x80000000)
{
}
void ProString::setValue(const QString &str)
{
m_string = str, m_offset = 0, m_length = str.size(), m_hash = 0x80000000;
}
size_t ProString::updatedHash() const
{
return (m_hash = hash(m_string.constData() + m_offset, m_length));
}
size_t qHash(const ProString &str)
{
if (!(str.m_hash & 0x80000000))
return str.m_hash;
return str.updatedHash();
}
ProKey::ProKey(const QString &str) :
ProString(str, DoHash)
{
}
ProKey::ProKey(const char *str) :
ProString(str, DoHash)
{
}
ProKey::ProKey(const QString &str, int off, int len) :
ProString(str, off, len, DoHash)
{
}
ProKey::ProKey(const QString &str, int off, int len, uint hash) :
ProString(str, off, len, hash)
{
}
void ProKey::setValue(const QString &str)
{
m_string = str, m_offset = 0, m_length = str.size();
updatedHash();
}
QString ProString::toQString() const
{
return m_string.mid(m_offset, m_length);
}
QString &ProString::toQString(QString &tmp) const
{
tmp = m_string.mid(m_offset, m_length);
return tmp;
}
ProString &ProString::prepend(const ProString &other)
{
if (other.m_length) {
if (!m_length) {
*this = other;
} else {
m_string = other.toQStringView() + toQStringView();
m_offset = 0;
m_length = m_string.size();
if (!m_file)
m_file = other.m_file;
m_hash = 0x80000000;
}
}
return *this;
}
ProString &ProString::append(const QLatin1String other)
{
if (other.size()) {
if (m_length != m_string.size()) {
m_string = toQStringView() + other;
m_offset = 0;
m_length = m_string.size();
} else {
Q_ASSERT(m_offset == 0);
m_string.append(other);
m_length += other.size();
}
m_hash = 0x80000000;
}
return *this;
}
ProString &ProString::append(QChar other)
{
if (m_length != m_string.size()) {
m_string = toQStringView() + other;
m_offset = 0;
m_length = m_string.size();
} else {
Q_ASSERT(m_offset == 0);
m_string.append(other);
++m_length;
}
m_hash = 0x80000000;
return *this;
}
// If pending != 0, prefix with space if appending to non-empty non-pending
ProString &ProString::append(const ProString &other, bool *pending)
{
if (other.m_length) {
if (!m_length) {
*this = other;
} else {
if (m_length != m_string.size())
m_string = toQString();
if (pending && !*pending) {
m_string += QLatin1Char(' ') + other.toQStringView();
} else {
m_string += other.toQStringView();
}
m_length = m_string.size();
m_offset = 0;
if (other.m_file)
m_file = other.m_file;
m_hash = 0x80000000;
}
if (pending)
*pending = true;
}
return *this;
}
ProString &ProString::append(const ProStringList &other, bool *pending, bool skipEmpty1st)
{
if (const int sz = other.size()) {
int startIdx = 0;
if (pending && !*pending && skipEmpty1st && other.at(0).isEmpty()) {
if (sz == 1)
return *this;
startIdx = 1;
}
if (!m_length && sz == startIdx + 1) {
*this = other.at(startIdx);
} else {
bool putSpace = false;
if (pending && !*pending && m_length)
putSpace = true;
m_string = toQString();
m_offset = 0;
for (int i = startIdx; i < sz; ++i) {
if (putSpace)
m_string += QLatin1Char(' ');
else
putSpace = true;
const ProString &str = other.at(i);
m_string += str.toQStringView();
}
m_length = m_string.size();
if (other.last().m_file)
m_file = other.last().m_file;
m_hash = 0x80000000;
}
if (pending)
*pending = true;
}
return *this;
}
QString operator+(const ProString &one, const ProString &two)
{
if (two.m_length) {
if (!one.m_length) {
return two.toQString();
} else {
QString neu(one.m_length + two.m_length, Qt::Uninitialized);
ushort *ptr = (ushort *)neu.constData();
memcpy(ptr, one.m_string.constData() + one.m_offset, one.m_length * 2);
memcpy(ptr + one.m_length, two.m_string.constData() + two.m_offset, two.m_length * 2);
return neu;
}
}
return one.toQString();
}
ProString ProString::mid(int off, int len) const
{
ProString ret(*this, NoHash);
if (off > m_length)
off = m_length;
ret.m_offset += off;
ret.m_length -= off;
if ((uint)ret.m_length > (uint)len) // Unsigned comparison to interpret < 0 as infinite
ret.m_length = len;
return ret;
}
ProString ProString::trimmed() const
{
ProString ret(*this, NoHash);
int cur = m_offset;
int end = cur + m_length;
const QChar *data = m_string.constData();
for (; cur < end; cur++)
if (!data[cur].isSpace()) {
// No underrun check - we know there is at least one non-whitespace
while (data[end - 1].isSpace())
end--;
break;
}
ret.m_offset = cur;
ret.m_length = end - cur;
return ret;
}
QTextStream &operator<<(QTextStream &t, const ProString &str)
{
t << str.toQStringView();
return t;
}
static QString ProStringList_join(const ProStringList &this_, const QChar *sep, const int sepSize)
{
int totalLength = 0;
const int sz = this_.size();
for (int i = 0; i < sz; ++i)
totalLength += this_.at(i).size();
if (sz)
totalLength += sepSize * (sz - 1);
QString res(totalLength, Qt::Uninitialized);
QChar *ptr = (QChar *)res.constData();
for (int i = 0; i < sz; ++i) {
if (i) {
memcpy(ptr, sep, sepSize * sizeof(QChar));
ptr += sepSize;
}
const ProString &str = this_.at(i);
memcpy(ptr, str.constData(), str.size() * sizeof(QChar));
ptr += str.size();
}
return res;
}
QString ProStringList::join(const ProString &sep) const
{
return ProStringList_join(*this, sep.constData(), sep.size());
}
QString ProStringList::join(const QString &sep) const
{
return ProStringList_join(*this, sep.constData(), sep.size());
}
QString ProStringList::join(QChar sep) const
{
return ProStringList_join(*this, &sep, 1);
}
void ProStringList::removeAll(const ProString &str)
{
for (int i = size(); --i >= 0; )
if (at(i) == str)
remove(i);
}
void ProStringList::removeAll(const char *str)
{
for (int i = size(); --i >= 0; )
if (at(i) == str)
remove(i);
}
void ProStringList::removeEach(const ProStringList &value)
{
for (const ProString &str : value) {
if (isEmpty())
break;
if (!str.isEmpty())
removeAll(str);
}
}
void ProStringList::removeEmpty()
{
for (int i = size(); --i >= 0;)
if (at(i).isEmpty())
remove(i);
}
void ProStringList::removeDuplicates()
{
QDuplicateTracker<ProString> seen(size());
removeIf([&](const ProString &s) { return seen.hasSeen(s); });
}
void ProStringList::insertUnique(const ProStringList &value)
{
for (const ProString &str : value)
if (!str.isEmpty() && !contains(str))
append(str);
}
ProStringList::ProStringList(const QStringList &list)
{
reserve(list.size());
for (const QString &str : list)
*this << ProString(str);
}
QStringList ProStringList::toQStringList() const
{
QStringList ret;
ret.reserve(size());
for (const auto &e : *this)
ret.append(e.toQString());
return ret;
}
bool ProStringList::contains(const ProString &str, Qt::CaseSensitivity cs) const
{
for (int i = 0; i < size(); i++)
if (!at(i).compare(str, cs))
return true;
return false;
}
bool ProStringList::contains(QStringView str, Qt::CaseSensitivity cs) const
{
for (int i = 0; i < size(); i++)
if (!at(i).toQStringView().compare(str, cs))
return true;
return false;
}
bool ProStringList::contains(const char *str, Qt::CaseSensitivity cs) const
{
for (int i = 0; i < size(); i++)
if (!at(i).compare(str, cs))
return true;
return false;
}
ProFile::ProFile(int id, const QString &fileName)
: m_refCount(1),
m_fileName(fileName),
m_id(id),
m_ok(true),
m_hostBuild(false)
{
if (!fileName.startsWith(QLatin1Char('(')))
m_directoryName = QFileInfo( // qmake sickness: canonicalize only the directory!
fileName.left(fileName.lastIndexOf(QLatin1Char('/')))).canonicalFilePath();
}
ProFile::~ProFile()
{
}
ProString ProFile::getStr(const ushort *&tPtr)
{
uint len = *tPtr++;
ProString ret(items(), tPtr - tokPtr(), len);
ret.setSource(m_id);
tPtr += len;
return ret;
}
ProKey ProFile::getHashStr(const ushort *&tPtr)
{
uint hash = *tPtr++;
hash |= (uint)*tPtr++ << 16;
uint len = *tPtr++;
ProKey ret(items(), tPtr - tokPtr(), len, hash);
tPtr += len;
return ret;
}
QDebug operator<<(QDebug debug, const ProString &str)
{
return debug << str.toQString();
}
QT_END_NAMESPACE

489
qmake/library/proitems.h Normal file
View File

@ -0,0 +1,489 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PROITEMS_H
#define PROITEMS_H
#include "qmake_global.h"
#include <qdebug.h>
#include <qhash.h>
#include <qlist.h>
#include <qmap.h>
#include <qstring.h>
QT_BEGIN_NAMESPACE
class QTextStream;
#ifdef PROPARSER_THREAD_SAFE
typedef QAtomicInt ProItemRefCount;
#else
class ProItemRefCount {
public:
ProItemRefCount(int cnt = 0) : m_cnt(cnt) {}
bool ref() { return ++m_cnt != 0; }
bool deref() { return --m_cnt != 0; }
ProItemRefCount &operator=(int value) { m_cnt = value; return *this; }
private:
int m_cnt;
};
#endif
#ifndef QT_BUILD_QMAKE
# define PROITEM_EXPLICIT explicit
#else
# define PROITEM_EXPLICIT
#endif
class ProKey;
class ProStringList;
class ProFile;
class ProString {
public:
ProString();
ProString(const ProString &other);
ProString &operator=(const ProString &) = default;
template<typename A, typename B>
ProString &operator=(const QStringBuilder<A, B> &str)
{ return *this = QString(str); }
ProString(const QString &str);
PROITEM_EXPLICIT ProString(QStringView str);
PROITEM_EXPLICIT ProString(const char *str);
template<typename A, typename B>
ProString(const QStringBuilder<A, B> &str)
: ProString(QString(str))
{}
ProString(const QString &str, int offset, int length);
void setValue(const QString &str);
void clear() { m_string.clear(); m_length = 0; }
ProString &setSource(const ProString &other) { m_file = other.m_file; return *this; }
ProString &setSource(int id) { m_file = id; return *this; }
int sourceFile() const { return m_file; }
ProString &prepend(const ProString &other);
ProString &append(const ProString &other, bool *pending = nullptr);
ProString &append(const QString &other) { return append(ProString(other)); }
template<typename A, typename B>
ProString &append(const QStringBuilder<A, B> &other) { return append(QString(other)); }
ProString &append(const QLatin1String other);
ProString &append(const char *other) { return append(QLatin1String(other)); }
ProString &append(QChar other);
ProString &append(const ProStringList &other, bool *pending = nullptr, bool skipEmpty1st = false);
ProString &operator+=(const ProString &other) { return append(other); }
ProString &operator+=(const QString &other) { return append(other); }
template<typename A, typename B>
ProString &operator+=(const QStringBuilder<A, B> &other) { return append(QString(other)); }
ProString &operator+=(const QLatin1String other) { return append(other); }
ProString &operator+=(const char *other) { return append(other); }
ProString &operator+=(QChar other) { return append(other); }
void chop(int n) { Q_ASSERT(n <= m_length); m_length -= n; }
void chopFront(int n) { Q_ASSERT(n <= m_length); m_offset += n; m_length -= n; }
bool operator==(const ProString &other) const { return toQStringView() == other.toQStringView(); }
bool operator==(const QString &other) const { return toQStringView() == other; }
bool operator==(QStringView other) const { return toQStringView() == other; }
bool operator==(QLatin1String other) const { return toQStringView() == other; }
bool operator==(const char *other) const { return toQStringView() == QLatin1String(other); }
bool operator!=(const ProString &other) const { return !(*this == other); }
bool operator!=(const QString &other) const { return !(*this == other); }
bool operator!=(QLatin1String other) const { return !(*this == other); }
bool operator!=(const char *other) const { return !(*this == other); }
bool operator<(const ProString &other) const { return toQStringView() < other.toQStringView(); }
bool isNull() const { return m_string.isNull(); }
bool isEmpty() const { return !m_length; }
int length() const { return m_length; }
int size() const { return m_length; }
QChar at(int i) const { Q_ASSERT((uint)i < (uint)m_length); return constData()[i]; }
const QChar *constData() const { return m_string.constData() + m_offset; }
ProString mid(int off, int len = -1) const;
ProString left(int len) const { return mid(0, len); }
ProString right(int len) const { return mid(qMax(0, size() - len)); }
ProString trimmed() const;
int compare(const ProString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().compare(sub.toQStringView(), cs); }
int compare(const QString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().compare(sub, cs); }
int compare(const char *sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().compare(QLatin1String(sub), cs); }
bool startsWith(const ProString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().startsWith(sub.toQStringView(), cs); }
bool startsWith(const QString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().startsWith(sub, cs); }
bool startsWith(const char *sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().startsWith(QLatin1String(sub), cs); }
bool startsWith(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().startsWith(c, cs); }
template<typename A, typename B>
bool startsWith(const QStringBuilder<A, B> &str) { return startsWith(QString(str)); }
bool endsWith(const ProString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().endsWith(sub.toQStringView(), cs); }
bool endsWith(const QString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().endsWith(sub, cs); }
bool endsWith(const char *sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().endsWith(QLatin1String(sub), cs); }
template<typename A, typename B>
bool endsWith(const QStringBuilder<A, B> &str) { return endsWith(QString(str)); }
bool endsWith(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().endsWith(c, cs); }
int indexOf(const QString &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().indexOf(s, from, cs); }
int indexOf(const char *s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().indexOf(QLatin1String(s), from, cs); }
int indexOf(QChar c, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().indexOf(c, from, cs); }
int lastIndexOf(const QString &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().lastIndexOf(s, from, cs); }
int lastIndexOf(const char *s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().lastIndexOf(QLatin1String(s), from, cs); }
int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringView().lastIndexOf(c, from, cs); }
bool contains(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(s, 0, cs) >= 0; }
bool contains(const char *s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(QLatin1String(s), 0, cs) >= 0; }
bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(c, 0, cs) >= 0; }
qlonglong toLongLong(bool *ok = nullptr, int base = 10) const { return toQStringView().toLongLong(ok, base); }
int toInt(bool *ok = nullptr, int base = 10) const { return toQStringView().toInt(ok, base); }
short toShort(bool *ok = nullptr, int base = 10) const { return toQStringView().toShort(ok, base); }
size_t hash() const { return m_hash; }
static size_t hash(const QChar *p, int n);
ALWAYS_INLINE QStringView toQStringView() const { return QStringView(m_string).mid(m_offset, m_length); }
ALWAYS_INLINE ProKey &toKey() { return *(ProKey *)this; }
ALWAYS_INLINE const ProKey &toKey() const { return *(const ProKey *)this; }
QString toQString() const;
QString &toQString(QString &tmp) const;
QByteArray toLatin1() const { return toQStringView().toLatin1(); }
private:
ProString(const ProKey &other);
ProString &operator=(const ProKey &other);
enum OmitPreHashing { NoHash };
ProString(const ProString &other, OmitPreHashing);
enum DoPreHashing { DoHash };
ALWAYS_INLINE ProString(const QString &str, DoPreHashing);
ALWAYS_INLINE ProString(const char *str, DoPreHashing);
ALWAYS_INLINE ProString(const QString &str, int offset, int length, DoPreHashing);
ALWAYS_INLINE ProString(const QString &str, int offset, int length, uint hash);
QString m_string;
int m_offset, m_length;
int m_file;
mutable size_t m_hash;
size_t updatedHash() const;
friend size_t qHash(const ProString &str);
friend QString operator+(const ProString &one, const ProString &two);
friend class ProKey;
};
Q_DECLARE_TYPEINFO(ProString, Q_RELOCATABLE_TYPE);
class ProKey : public ProString {
public:
ALWAYS_INLINE ProKey() : ProString() {}
explicit ProKey(const QString &str);
template<typename A, typename B>
ProKey(const QStringBuilder<A, B> &str)
: ProString(str)
{}
PROITEM_EXPLICIT ProKey(const char *str);
ProKey(const QString &str, int off, int len);
ProKey(const QString &str, int off, int len, uint hash);
void setValue(const QString &str);
#ifdef Q_CC_MSVC
// Workaround strange MSVC behaviour when exporting classes with ProKey members.
ALWAYS_INLINE ProKey(const ProKey &other) : ProString(other.toString()) {}
ALWAYS_INLINE ProKey &operator=(const ProKey &other)
{
toString() = other.toString();
return *this;
}
#endif
ALWAYS_INLINE ProString &toString() { return *(ProString *)this; }
ALWAYS_INLINE const ProString &toString() const { return *(const ProString *)this; }
private:
ProKey(const ProString &other);
};
Q_DECLARE_TYPEINFO(ProKey, Q_RELOCATABLE_TYPE);
template <> struct QConcatenable<ProString> : private QAbstractConcatenable
{
typedef ProString type;
typedef QString ConvertTo;
enum { ExactSize = true };
static int size(const ProString &a) { return a.length(); }
static inline void appendTo(const ProString &a, QChar *&out)
{
const auto n = a.size();
if (!n)
return;
memcpy(out, a.toQStringView().data(), sizeof(QChar) * n);
out += n;
}
};
template <> struct QConcatenable<ProKey> : private QAbstractConcatenable
{
typedef ProKey type;
typedef QString ConvertTo;
enum { ExactSize = true };
static int size(const ProKey &a) { return a.length(); }
static inline void appendTo(const ProKey &a, QChar *&out)
{
const auto n = a.size();
if (!n)
return;
memcpy(out, a.toQStringView().data(), sizeof(QChar) * n);
out += n;
}
};
size_t qHash(const ProString &str);
inline QString &operator+=(QString &that, const ProString &other)
{ return that += other.toQStringView(); }
QTextStream &operator<<(QTextStream &t, const ProString &str);
template<typename A, typename B>
QTextStream &operator<<(QTextStream &t, const QStringBuilder<A, B> &str) { return t << QString(str); }
// This class manages read-only access to a ProString via a raw data QString
// temporary, ensuring that the latter is accessed exclusively.
class ProStringRoUser
{
public:
ProStringRoUser(QString &rs)
{
m_rs = &rs;
}
ProStringRoUser(const ProString &ps, QString &rs)
: ProStringRoUser(rs)
{
ps.toQString(rs);
}
// No destructor, as a RAII pattern cannot be used: references to the
// temporary string can legitimately outlive instances of this class
// (if they are held by Qt, e.g. in QRegExp).
QString &set(const ProString &ps) { return ps.toQString(*m_rs); }
QString &str() { return *m_rs; }
protected:
QString *m_rs;
};
// This class manages read-write access to a ProString via a raw data QString
// temporary, ensuring that the latter is accessed exclusively, and that raw
// data does not leak outside its source's refcounting.
class ProStringRwUser : public ProStringRoUser
{
public:
ProStringRwUser(QString &rs)
: ProStringRoUser(rs), m_ps(nullptr) {}
ProStringRwUser(const ProString &ps, QString &rs)
: ProStringRoUser(ps, rs), m_ps(&ps) {}
QString &set(const ProString &ps) { m_ps = &ps; return ProStringRoUser::set(ps); }
ProString extract(const QString &s) const
{ return s.isSharedWith(*m_rs) ? *m_ps : ProString(s).setSource(*m_ps); }
ProString extract(const QString &s, const ProStringRwUser &other) const
{
if (other.m_ps && s.isSharedWith(*other.m_rs))
return *other.m_ps;
return extract(s);
}
private:
const ProString *m_ps;
};
class ProStringList : public QList<ProString> {
public:
ProStringList() {}
ProStringList(const ProString &str) { *this << str; }
explicit ProStringList(const QStringList &list);
QStringList toQStringList() const;
ProStringList &operator<<(const ProString &str)
{ QList<ProString>::operator<<(str); return *this; }
int length() const { return size(); }
QString join(const ProString &sep) const;
QString join(const QString &sep) const;
QString join(QChar sep) const;
template<typename A, typename B>
QString join(const QStringBuilder<A, B> &str) { return join(QString(str)); }
void insertUnique(const ProStringList &value);
void removeAll(const ProString &str);
void removeAll(const char *str);
void removeEach(const ProStringList &value);
void removeAt(int idx) { remove(idx); }
void removeEmpty();
void removeDuplicates();
bool contains(const ProString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
bool contains(QStringView str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
bool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
{ return contains(ProString(str), cs); }
bool contains(const char *str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
};
Q_DECLARE_TYPEINFO(ProStringList, Q_RELOCATABLE_TYPE);
inline ProStringList operator+(const ProStringList &one, const ProStringList &two)
{ ProStringList ret = one; ret += two; return ret; }
typedef QMap<ProKey, ProStringList> ProValueMap;
// These token definitions affect both ProFileEvaluator and ProWriter
enum ProToken {
TokTerminator = 0, // end of stream (possibly not included in length; must be zero)
TokLine, // line marker:
// - line (1)
TokAssign, // variable =
TokAppend, // variable +=
TokAppendUnique, // variable *=
TokRemove, // variable -=
TokReplace, // variable ~=
// previous literal/expansion is a variable manipulation
// - lower bound for expected output length (1)
// - value expression + TokValueTerminator
TokValueTerminator, // assignment value terminator
TokLiteral, // literal string (fully dequoted)
// - length (1)
// - string data (length; unterminated)
TokHashLiteral, // literal string with hash (fully dequoted)
// - hash (2)
// - length (1)
// - string data (length; unterminated)
TokVariable, // qmake variable expansion
// - hash (2)
// - name length (1)
// - name (name length; unterminated)
TokProperty, // qmake property expansion
// - hash (2)
// - name length (1)
// - name (name length; unterminated)
TokEnvVar, // environment variable expansion
// - name length (1)
// - name (name length; unterminated)
TokFuncName, // replace function expansion
// - hash (2)
// - name length (1)
// - name (name length; unterminated)
// - ((nested expansion + TokArgSeparator)* + nested expansion)?
// - TokFuncTerminator
TokArgSeparator, // function argument separator
TokFuncTerminator, // function argument list terminator
TokCondition, // previous literal/expansion is a conditional
TokTestCall, // previous literal/expansion is a test function call
// - ((nested expansion + TokArgSeparator)* + nested expansion)?
// - TokFuncTerminator
TokReturn, // previous literal/expansion is a return value
TokBreak, // break loop
TokNext, // shortcut to next loop iteration
TokNot, // '!' operator
TokAnd, // ':' operator
TokOr, // '|' operator
TokBranch, // branch point:
// - then block length (2)
// - then block + TokTerminator (then block length)
// - else block length (2)
// - else block + TokTerminator (else block length)
TokForLoop, // for loop:
// - variable name: hash (2), length (1), chars (length)
// - expression: length (2), bytes + TokValueTerminator (length)
// - body length (2)
// - body + TokTerminator (body length)
TokTestDef, // test function definition:
TokReplaceDef, // replace function definition:
// - function name: hash (2), length (1), chars (length)
// - body length (2)
// - body + TokTerminator (body length)
TokBypassNesting, // escape from function local variable scopes:
// - block length (2)
// - block + TokTerminator (block length)
TokMask = 0xff,
TokQuoted = 0x100, // The expression is quoted => join expanded stringlist
TokNewStr = 0x200 // Next stringlist element
};
class QMAKE_EXPORT ProFile
{
public:
ProFile(int id, const QString &fileName);
~ProFile();
int id() const { return m_id; }
QString fileName() const { return m_fileName; }
QString directoryName() const { return m_directoryName; }
const QString &items() const { return m_proitems; }
QString *itemsRef() { return &m_proitems; }
const ushort *tokPtr() const { return (const ushort *)m_proitems.constData(); }
const ushort *tokPtrEnd() const { return (const ushort *)m_proitems.constData() + m_proitems.size(); }
void ref() { m_refCount.ref(); }
void deref() { if (!m_refCount.deref()) delete this; }
bool isOk() const { return m_ok; }
void setOk(bool ok) { m_ok = ok; }
bool isHostBuild() const { return m_hostBuild; }
void setHostBuild(bool host_build) { m_hostBuild = host_build; }
ProString getStr(const ushort *&tPtr);
ProKey getHashStr(const ushort *&tPtr);
private:
ProItemRefCount m_refCount;
QString m_proitems;
QString m_fileName;
QString m_directoryName;
int m_id;
bool m_ok;
bool m_hostBuild;
};
class ProFunctionDef {
public:
ProFunctionDef(ProFile *pro, int offset) : m_pro(pro), m_offset(offset) { m_pro->ref(); }
ProFunctionDef(const ProFunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); }
ProFunctionDef(ProFunctionDef &&other) noexcept
: m_pro(other.m_pro), m_offset(other.m_offset) { other.m_pro = nullptr; }
~ProFunctionDef() { if (m_pro) m_pro->deref(); }
ProFunctionDef &operator=(const ProFunctionDef &o)
{
if (this != &o) {
if (m_pro)
m_pro->deref();
m_pro = o.m_pro;
m_pro->ref();
m_offset = o.m_offset;
}
return *this;
}
ProFunctionDef &operator=(ProFunctionDef &&other) noexcept
{
ProFunctionDef moved(std::move(other));
swap(moved);
return *this;
}
void swap(ProFunctionDef &other) noexcept
{
qSwap(m_pro, other.m_pro);
qSwap(m_offset, other.m_offset);
}
ProFile *pro() const { return m_pro; }
const ushort *tokPtr() const { return m_pro->tokPtr() + m_offset; }
private:
ProFile *m_pro;
int m_offset;
};
Q_DECLARE_TYPEINFO(ProFunctionDef, Q_RELOCATABLE_TYPE);
struct ProFunctionDefs {
QHash<ProKey, ProFunctionDef> testFunctions;
QHash<ProKey, ProFunctionDef> replaceFunctions;
};
QDebug operator<<(QDebug debug, const ProString &str);
QT_END_NAMESPACE
#endif // PROITEMS_H

View File

@ -0,0 +1,33 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMAKE_GLOBAL_H
#define QMAKE_GLOBAL_H
#include <qglobal.h>
#if defined(QMAKE_AS_LIBRARY)
# if defined(QMAKE_LIBRARY)
# define QMAKE_EXPORT Q_DECL_EXPORT
# else
# define QMAKE_EXPORT Q_DECL_IMPORT
# endif
#else
# define QMAKE_EXPORT
#endif
// Be fast even for debug builds
// MinGW GCC 4.5+ has a problem with always_inline putTok and putBlockLen
#if defined(__GNUC__) && !(defined(__MINGW32__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 5)
# define ALWAYS_INLINE inline __attribute__((always_inline))
#elif defined(_MSC_VER)
# define ALWAYS_INLINE __forceinline
#else
# define ALWAYS_INLINE inline
#endif
#ifdef PROEVALUATOR_FULL
# define PROEVALUATOR_DEBUG
#endif
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,309 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMAKEEVALUATOR_H
#define QMAKEEVALUATOR_H
#if defined(PROEVALUATOR_FULL) && defined(PROEVALUATOR_THREAD_SAFE)
# error PROEVALUATOR_FULL is incompatible with PROEVALUATOR_THREAD_SAFE due to cache() implementation
#endif
#include "qmakeparser.h"
#include "qmakevfs.h"
#include "ioutils.h"
#include <qlist.h>
#include <qmap.h>
#include <qset.h>
#include <qstack.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qshareddata.h>
#if QT_CONFIG(process)
# include <qprocess.h>
#else
# include <qiodevice.h>
#endif
#ifdef PROEVALUATOR_THREAD_SAFE
# include <qmutex.h>
#endif
#include <list>
QT_BEGIN_NAMESPACE
class QMakeGlobals;
class QMAKE_EXPORT QMakeHandler : public QMakeParserHandler
{
public:
enum {
SourceEvaluator = 0x10,
CumulativeEvalMessage = 0x1000,
EvalWarnLanguage = SourceEvaluator | WarningMessage | WarnLanguage,
EvalWarnDeprecated = SourceEvaluator | WarningMessage | WarnDeprecated,
EvalError = ErrorMessage | SourceEvaluator
};
// error(), warning() and message() from .pro file
virtual void fileMessage(int type, const QString &msg) = 0;
enum EvalFileType { EvalProjectFile, EvalIncludeFile, EvalConfigFile, EvalFeatureFile, EvalAuxFile };
virtual void aboutToEval(ProFile *parent, ProFile *proFile, EvalFileType type) = 0;
virtual void doneWithEval(ProFile *parent) = 0;
};
typedef QPair<QString, QString> QMakeFeatureKey; // key, parent
typedef QHash<QMakeFeatureKey, QString> QMakeFeatureHash;
class QMAKE_EXPORT QMakeFeatureRoots : public QSharedData
{
public:
QMakeFeatureRoots(const QStringList &_paths) : paths(_paths) {}
const QStringList paths;
mutable QMakeFeatureHash cache;
#ifdef PROEVALUATOR_THREAD_SAFE
mutable QMutex mutex;
#endif
};
// We use a list-based stack instead of a vector-based one, so that
// the addresses of value maps stay constant. The qmake generators rely on that.
class QMAKE_EXPORT ProValueMapStack : public std::list<ProValueMap>
{
public:
inline void push(const ProValueMap &t) { push_back(t); }
inline ProValueMap pop() { auto r = std::move(back()); pop_back(); return r; }
ProValueMap &top() { return back(); }
const ProValueMap &top() const { return back(); }
};
namespace QMakeInternal { struct QMakeBuiltin; }
class QMAKE_EXPORT QMakeEvaluator
{
public:
enum LoadFlag {
LoadProOnly = 0,
LoadPreFiles = 1,
LoadPostFiles = 2,
LoadAll = LoadPreFiles|LoadPostFiles,
LoadSilent = 0x10,
LoadHidden = 0x20
};
Q_DECLARE_FLAGS(LoadFlags, LoadFlag)
static void initStatics();
static void initFunctionStatics();
QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, QMakeVfs *vfs,
QMakeHandler *handler);
~QMakeEvaluator();
void setExtraVars(const ProValueMap &extraVars) { m_extraVars = extraVars; }
void setExtraConfigs(const ProStringList &extraConfigs) { m_extraConfigs = extraConfigs; }
void setOutputDir(const QString &outputDir) { m_outputDir = outputDir; }
ProStringList values(const ProKey &variableName) const;
ProStringList &valuesRef(const ProKey &variableName);
ProString first(const ProKey &variableName) const;
ProString propertyValue(const ProKey &val) const;
ProString dirSep() const { return m_dirSep; }
bool isHostBuild() const { return m_hostBuild; }
enum VisitReturn {
ReturnFalse,
ReturnTrue,
ReturnError,
ReturnBreak,
ReturnNext,
ReturnReturn
};
static ALWAYS_INLINE VisitReturn returnBool(bool b)
{ return b ? ReturnTrue : ReturnFalse; }
static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr);
VisitReturn evaluateExpression(const ushort *&tokPtr, ProStringList *ret, bool joined);
static ALWAYS_INLINE void skipStr(const ushort *&tokPtr);
static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr);
void skipExpression(const ushort *&tokPtr);
void loadDefaults();
bool prepareProject(const QString &inDir);
bool loadSpecInternal();
bool loadSpec();
void initFrom(const QMakeEvaluator *other);
void setupProject();
void evaluateCommand(const QString &cmds, const QString &where);
void applyExtraConfigs();
VisitReturn visitProFile(ProFile *pro, QMakeHandler::EvalFileType type,
LoadFlags flags);
VisitReturn visitProBlock(ProFile *pro, const ushort *tokPtr);
VisitReturn visitProBlock(const ushort *tokPtr);
VisitReturn visitProLoop(const ProKey &variable, const ushort *exprPtr,
const ushort *tokPtr);
void visitProFunctionDef(ushort tok, const ProKey &name, const ushort *tokPtr);
VisitReturn visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr);
ALWAYS_INLINE const ProKey &map(const ProString &var) { return map(var.toKey()); }
const ProKey &map(const ProKey &var);
ProValueMap *findValues(const ProKey &variableName, ProValueMap::Iterator *it);
void setTemplate();
ProStringList split_value_list(QStringView vals, int source = 0);
VisitReturn expandVariableReferences(const ushort *&tokPtr, int sizeHint, ProStringList *ret, bool joined);
QString currentFileName() const;
QString currentDirectory() const;
ProFile *currentProFile() const;
int currentFileId() const;
QString resolvePath(const QString &fileName) const
{ return QMakeInternal::IoUtils::resolvePath(currentDirectory(), fileName); }
QString filePathArg0(const ProStringList &args);
QString filePathEnvArg0(const ProStringList &args);
VisitReturn evaluateFile(const QString &fileName, QMakeHandler::EvalFileType type,
LoadFlags flags);
VisitReturn evaluateFileChecked(const QString &fileName, QMakeHandler::EvalFileType type,
LoadFlags flags);
VisitReturn evaluateFeatureFile(const QString &fileName, bool silent = false);
VisitReturn evaluateFileInto(const QString &fileName,
ProValueMap *values, // output-only
LoadFlags flags);
VisitReturn evaluateConfigFeatures();
void message(int type, const QString &msg) const;
void evalError(const QString &msg) const
{ message(QMakeHandler::EvalError, msg); }
void languageWarning(const QString &msg) const
{ message(QMakeHandler::EvalWarnLanguage, msg); }
void deprecationWarning(const QString &msg) const
{ message(QMakeHandler::EvalWarnDeprecated, msg); }
VisitReturn prepareFunctionArgs(const ushort *&tokPtr, QList<ProStringList> *ret);
VisitReturn evaluateFunction(const ProFunctionDef &func,
const QList<ProStringList> &argumentsList, ProStringList *ret);
VisitReturn evaluateBoolFunction(const ProFunctionDef &func,
const QList<ProStringList> &argumentsList,
const ProString &function);
VisitReturn evaluateExpandFunction(const ProKey &function, const ushort *&tokPtr, ProStringList *ret);
VisitReturn evaluateConditionalFunction(const ProKey &function, const ushort *&tokPtr);
VisitReturn evaluateBuiltinExpand(const QMakeInternal::QMakeBuiltin &adef,
const ProKey &function, const ProStringList &args, ProStringList &ret);
VisitReturn evaluateBuiltinConditional(const QMakeInternal::QMakeBuiltin &adef,
const ProKey &function, const ProStringList &args);
VisitReturn evaluateConditional(QStringView cond, const QString &where, int line = -1);
#ifdef PROEVALUATOR_FULL
VisitReturn checkRequirements(const ProStringList &deps);
#endif
void updateMkspecPaths();
void updateFeaturePaths();
bool isActiveConfig(QStringView config, bool regex = false);
void populateDeps(
const ProStringList &deps, const ProString &prefix, const ProStringList &suffixes,
const ProString &priosfx,
QHash<ProKey, QSet<ProKey> > &dependencies, ProValueMap &dependees,
QMultiMap<int, ProString> &rootSet) const;
bool getMemberArgs(const ProKey &name, int srclen, const ProStringList &args,
int *start, int *end);
VisitReturn parseJsonInto(const QByteArray &json, const QString &into, ProValueMap *value);
VisitReturn writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode,
QMakeVfs::VfsFlags flags, const QString &contents);
#if QT_CONFIG(process)
void runProcess(QProcess *proc, const QString &command) const;
#endif
QByteArray getCommandOutput(const QString &args, int *exitCode) const;
private:
// Implementation detail of evaluateBuiltinConditional():
VisitReturn testFunc_cache(const ProStringList &args);
public:
QMakeEvaluator *m_caller;
#ifdef PROEVALUATOR_CUMULATIVE
bool m_cumulative;
int m_skipLevel;
#else
enum { m_cumulative = 0 };
enum { m_skipLevel = 0 };
#endif
static QString quoteValue(const ProString &val);
#ifdef PROEVALUATOR_DEBUG
void debugMsgInternal(int level, const char *fmt, ...) const;
void traceMsgInternal(const char *fmt, ...) const;
static QString formatValue(const ProString &val, bool forceQuote = false);
static QString formatValueList(const ProStringList &vals, bool commas = false);
static QString formatValueListList(const QList<ProStringList> &vals);
const int m_debugLevel;
#else
ALWAYS_INLINE void debugMsgInternal(int, const char *, ...) const {}
ALWAYS_INLINE void traceMsgInternal(const char *, ...) const {}
enum { m_debugLevel = 0 };
#endif
struct Location {
Location() : pro(nullptr), line(0) {}
Location(ProFile *_pro, ushort _line) : pro(_pro), line(_line) {}
void clear() { pro = nullptr; line = 0; }
ProFile *pro;
ushort line;
};
Location m_current; // Currently evaluated location
QStack<Location> m_locationStack; // All execution location changes
QStack<ProFile *> m_profileStack; // Includes only
ProValueMap m_extraVars;
ProStringList m_extraConfigs;
QString m_outputDir;
int m_listCount;
int m_toggle;
bool m_valuemapInited;
bool m_hostBuild;
QString m_qmakespec;
QString m_qmakespecName;
QString m_superfile;
QString m_conffile;
QString m_cachefile;
QString m_stashfile;
QString m_sourceRoot;
QString m_buildRoot;
QStringList m_qmakepath;
QStringList m_qmakefeatures;
QStringList m_mkspecPaths;
QExplicitlySharedDataPointer<QMakeFeatureRoots> m_featureRoots;
ProString m_dirSep;
ProFunctionDefs m_functionDefs;
ProStringList m_returnValue;
ProValueMapStack m_valuemapStack; // VariableName must be us-ascii, the content however can be non-us-ascii.
QString m_tmp1, m_tmp2, m_tmp3, m_tmp[2]; // Temporaries for efficient toQString
QMakeGlobals *m_option;
QMakeParser *m_parser;
QMakeHandler *m_handler;
QMakeVfs *m_vfs;
};
Q_DECLARE_TYPEINFO(QMakeEvaluator::Location, Q_PRIMITIVE_TYPE);
Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeEvaluator::LoadFlags)
QT_END_NAMESPACE
#endif // QMAKEEVALUATOR_H

View File

@ -0,0 +1,89 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMAKEEVALUATOR_P_H
#define QMAKEEVALUATOR_P_H
#include "proitems.h"
#define debugMsg if (!m_debugLevel) {} else debugMsgInternal
#define traceMsg if (!m_debugLevel) {} else traceMsgInternal
#ifdef PROEVALUATOR_DEBUG
# define dbgBool(b) (b ? "true" : "false")
# define dbgReturn(r) \
(r == ReturnError ? "error" : \
r == ReturnBreak ? "break" : \
r == ReturnNext ? "next" : \
r == ReturnReturn ? "return" : \
"<invalid>")
# define dbgKey(s) s.toString().toQStringView().toLocal8Bit().constData()
# define dbgStr(s) qPrintable(formatValue(s, true))
# define dbgStrList(s) qPrintable(formatValueList(s))
# define dbgSepStrList(s) qPrintable(formatValueList(s, true))
# define dbgStrListList(s) qPrintable(formatValueListList(s))
# define dbgQStr(s) dbgStr(ProString(s))
#else
# define dbgBool(b) 0
# define dbgReturn(r) 0
# define dbgKey(s) 0
# define dbgStr(s) 0
# define dbgStrList(s) 0
# define dbgSepStrList(s) 0
# define dbgStrListList(s) 0
# define dbgQStr(s) 0
#endif
QT_BEGIN_NAMESPACE
namespace QMakeInternal {
struct QMakeBuiltinInit
{
const char *name;
int func;
enum { VarArgs = 1000 };
int min_args, max_args;
const char *args;
};
struct QMakeBuiltin
{
QMakeBuiltin(const QMakeBuiltinInit &data);
QString usage;
int index, minArgs, maxArgs;
};
struct QMakeStatics {
QString field_sep;
QString strtrue;
QString strfalse;
ProKey strCONFIG;
ProKey strARGS;
ProKey strARGC;
QString strDot;
QString strDotDot;
QString strever;
QString strforever;
QString strhost_build;
ProKey strTEMPLATE;
ProKey strQMAKE_PLATFORM;
ProKey strQMAKE_DIR_SEP;
ProKey strQMAKESPEC;
#ifdef PROEVALUATOR_FULL
ProKey strREQUIRES;
#endif
QHash<ProKey, QMakeBuiltin> expands;
QHash<ProKey, QMakeBuiltin> functions;
QHash<ProKey, ProKey> varMap;
ProStringList fakeValue;
};
extern QMakeStatics statics;
}
Q_DECLARE_TYPEINFO(QMakeInternal::QMakeBuiltin, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
#endif // QMAKEEVALUATOR_P_H

View File

@ -0,0 +1,374 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmakeglobals.h"
#include "qmakeevaluator.h"
#include "ioutils.h"
#include <qbytearray.h>
#include <qdatetime.h>
#include <qdebug.h>
#include <qdir.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qlist.h>
#include <qset.h>
#include <qstack.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qtextstream.h>
#ifdef PROEVALUATOR_THREAD_SAFE
# include <qthreadpool.h>
#endif
#ifdef Q_OS_UNIX
#include <unistd.h>
#include <sys/utsname.h>
#else
#include <qt_windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef Q_OS_WIN32
#define QT_POPEN _popen
#define QT_POPEN_READ "rb"
#define QT_PCLOSE _pclose
#else
#define QT_POPEN popen
#define QT_POPEN_READ "r"
#define QT_PCLOSE pclose
#endif
QT_BEGIN_NAMESPACE
using namespace QMakeInternal; // for IoUtils
#define fL1S(s) QString::fromLatin1(s)
QMakeGlobals::QMakeGlobals()
{
do_cache = true;
#ifdef PROEVALUATOR_DEBUG
debugLevel = 0;
#endif
#ifdef Q_OS_WIN
dirlist_sep = QLatin1Char(';');
dir_sep = QLatin1Char('\\');
#else
dirlist_sep = QLatin1Char(':');
dir_sep = QLatin1Char('/');
#endif
}
QMakeGlobals::~QMakeGlobals()
{
qDeleteAll(baseEnvs);
}
QString QMakeGlobals::cleanSpec(QMakeCmdLineParserState &state, const QString &spec)
{
QString ret = QDir::cleanPath(spec);
if (ret.contains(QLatin1Char('/'))) {
QString absRet = IoUtils::resolvePath(state.pwd, ret);
if (QFile::exists(absRet))
ret = absRet;
}
return ret;
}
/*
* Return value meanings:
* ArgumentUnknown The argument at *pos was not handled by this function.
* Leave it to the caller to handle this argument.
* ArgumentMalformed There was an error detected.
* ArgumentsOk All arguments were known. There are no arguments left to handle.
*/
QMakeGlobals::ArgumentReturn QMakeGlobals::addCommandLineArguments(
QMakeCmdLineParserState &state, QStringList &args, int *pos)
{
enum { ArgNone, ArgConfig, ArgSpec, ArgXSpec, ArgTmpl, ArgTmplPfx, ArgCache, ArgQtConf } argState = ArgNone;
for (; *pos < args.size(); (*pos)++) {
QString arg = args.at(*pos);
switch (argState) {
case ArgConfig:
state.configs[state.phase] << arg;
break;
case ArgSpec:
qmakespec = args[*pos] = cleanSpec(state, arg);
break;
case ArgXSpec:
xqmakespec = args[*pos] = cleanSpec(state, arg);
break;
case ArgTmpl:
user_template = arg;
break;
case ArgTmplPfx:
user_template_prefix = arg;
break;
case ArgCache:
cachefile = args[*pos] = IoUtils::resolvePath(state.pwd, arg);
break;
case ArgQtConf:
qtconf = args[*pos] = IoUtils::resolvePath(state.pwd, arg);
break;
default:
if (arg.startsWith(QLatin1Char('-'))) {
if (arg == QLatin1String("--")) {
state.extraargs = args.mid(*pos + 1);
args.erase(args.begin() + *pos, args.end());
return ArgumentsOk;
}
if (arg == QLatin1String("-early"))
state.phase = QMakeEvalEarly;
else if (arg == QLatin1String("-before"))
state.phase = QMakeEvalBefore;
else if (arg == QLatin1String("-after"))
state.phase = QMakeEvalAfter;
else if (arg == QLatin1String("-late"))
state.phase = QMakeEvalLate;
else if (arg == QLatin1String("-config"))
argState = ArgConfig;
else if (arg == QLatin1String("-nocache"))
do_cache = false;
else if (arg == QLatin1String("-cache"))
argState = ArgCache;
else if (arg == QLatin1String("-qtconf"))
argState = ArgQtConf;
else if (arg == QLatin1String("-platform") || arg == QLatin1String("-spec"))
argState = ArgSpec;
else if (arg == QLatin1String("-xplatform") || arg == QLatin1String("-xspec"))
argState = ArgXSpec;
else if (arg == QLatin1String("-template") || arg == QLatin1String("-t"))
argState = ArgTmpl;
else if (arg == QLatin1String("-template_prefix") || arg == QLatin1String("-tp"))
argState = ArgTmplPfx;
else if (arg == QLatin1String("-win32"))
dir_sep = QLatin1Char('\\');
else if (arg == QLatin1String("-unix"))
dir_sep = QLatin1Char('/');
else
return ArgumentUnknown;
} else if (arg.contains(QLatin1Char('='))) {
state.cmds[state.phase] << arg;
} else {
return ArgumentUnknown;
}
continue;
}
argState = ArgNone;
}
if (argState != ArgNone)
return ArgumentMalformed;
return ArgumentsOk;
}
void QMakeGlobals::commitCommandLineArguments(QMakeCmdLineParserState &state)
{
if (!state.extraargs.isEmpty()) {
QString extra = fL1S("QMAKE_EXTRA_ARGS =");
for (const QString &ea : std::as_const(state.extraargs))
extra += QLatin1Char(' ') + QMakeEvaluator::quoteValue(ProString(ea));
state.cmds[QMakeEvalBefore] << extra;
}
for (int p = 0; p < 4; p++) {
if (!state.configs[p].isEmpty())
state.cmds[p] << (fL1S("CONFIG += ") + state.configs[p].join(QLatin1Char(' ')));
extra_cmds[p] = state.cmds[p].join(QLatin1Char('\n'));
}
if (xqmakespec.isEmpty())
xqmakespec = qmakespec;
}
void QMakeGlobals::useEnvironment()
{
if (xqmakespec.isEmpty())
xqmakespec = getEnv(QLatin1String("XQMAKESPEC"));
if (qmakespec.isEmpty()) {
qmakespec = getEnv(QLatin1String("QMAKESPEC"));
if (xqmakespec.isEmpty())
xqmakespec = qmakespec;
}
}
void QMakeGlobals::setCommandLineArguments(const QString &pwd, const QStringList &_args)
{
QStringList args = _args;
QMakeCmdLineParserState state(pwd);
for (int pos = 0; pos < args.size(); pos++)
addCommandLineArguments(state, args, &pos);
commitCommandLineArguments(state);
useEnvironment();
}
void QMakeGlobals::setDirectories(const QString &input_dir, const QString &output_dir)
{
if (input_dir != output_dir && !output_dir.isEmpty()) {
QString srcpath = input_dir;
if (!srcpath.endsWith(QLatin1Char('/')))
srcpath += QLatin1Char('/');
QString dstpath = output_dir;
if (!dstpath.endsWith(QLatin1Char('/')))
dstpath += QLatin1Char('/');
int srcLen = srcpath.size();
int dstLen = dstpath.size();
int lastSl = -1;
while (++lastSl, --srcLen, --dstLen,
srcLen && dstLen && srcpath.at(srcLen) == dstpath.at(dstLen))
if (srcpath.at(srcLen) == QLatin1Char('/'))
lastSl = 0;
source_root = srcpath.left(srcLen + lastSl);
build_root = dstpath.left(dstLen + lastSl);
}
}
QString QMakeGlobals::shadowedPath(const QString &fileName) const
{
if (source_root.isEmpty())
return fileName;
if (fileName.startsWith(source_root)
&& (fileName.size() == source_root.size()
|| fileName.at(source_root.size()) == QLatin1Char('/'))) {
return build_root + fileName.mid(source_root.size());
}
return QString();
}
QStringList QMakeGlobals::splitPathList(const QString &val) const
{
QStringList ret;
if (!val.isEmpty()) {
QString cwd(QDir::currentPath());
const QStringList vals = val.split(dirlist_sep, Qt::SkipEmptyParts);
ret.reserve(vals.size());
for (const QString &it : vals)
ret << IoUtils::resolvePath(cwd, it);
}
return ret;
}
QString QMakeGlobals::getEnv(const QString &var) const
{
#ifdef PROEVALUATOR_SETENV
return environment.value(var);
#else
return QString::fromLocal8Bit(qgetenv(var.toLocal8Bit().constData()));
#endif
}
QStringList QMakeGlobals::getPathListEnv(const QString &var) const
{
return splitPathList(getEnv(var));
}
QString QMakeGlobals::expandEnvVars(const QString &str) const
{
QString string = str;
int startIndex = 0;
forever {
startIndex = string.indexOf(QLatin1Char('$'), startIndex);
if (startIndex < 0)
break;
if (string.size() < startIndex + 3)
break;
if (string.at(startIndex + 1) != QLatin1Char('(')) {
startIndex++;
continue;
}
int endIndex = string.indexOf(QLatin1Char(')'), startIndex + 2);
if (endIndex < 0)
break;
QString value = getEnv(string.mid(startIndex + 2, endIndex - startIndex - 2));
string.replace(startIndex, endIndex - startIndex + 1, value);
startIndex += value.size();
}
return string;
}
#ifndef QT_BUILD_QMAKE
#ifdef PROEVALUATOR_INIT_PROPS
bool QMakeGlobals::initProperties()
{
QByteArray data;
#if QT_CONFIG(process)
QProcess proc;
proc.start(qmake_abslocation, QStringList() << QLatin1String("-query"));
if (!proc.waitForFinished())
return false;
data = proc.readAll();
#else
if (FILE *proc = QT_POPEN(QString(IoUtils::shellQuote(qmake_abslocation)
+ QLatin1String(" -query")).toLocal8Bit(), QT_POPEN_READ)) {
char buff[1024];
while (!feof(proc))
data.append(buff, int(fread(buff, 1, 1023, proc)));
QT_PCLOSE(proc);
}
#endif
parseProperties(data, properties);
return true;
}
#endif
void QMakeGlobals::parseProperties(const QByteArray &data, QHash<ProKey, ProString> &properties)
{
const auto lines = data.split('\n');
for (QByteArray line : lines) {
int off = line.indexOf(':');
if (off < 0) // huh?
continue;
if (line.endsWith('\r'))
line.chop(1);
QString name = QString::fromLatin1(line.left(off));
ProString value = ProString(QDir::fromNativeSeparators(
QString::fromLocal8Bit(line.mid(off + 1))));
if (value.isNull())
value = ProString(""); // Make sure it is not null, to discern from missing keys
properties.insert(ProKey(name), value);
if (name.startsWith(QLatin1String("QT_"))) {
enum { PropPut, PropRaw, PropGet } variant;
if (name.contains(QLatin1Char('/'))) {
if (name.endsWith(QLatin1String("/raw")))
variant = PropRaw;
else if (name.endsWith(QLatin1String("/get")))
variant = PropGet;
else // Nothing falls back on /src or /dev.
continue;
name.chop(4);
} else {
variant = PropPut;
}
if (name.startsWith(QLatin1String("QT_INSTALL_"))) {
if (variant < PropRaw) {
if (name == QLatin1String("QT_INSTALL_PREFIX")
|| name == QLatin1String("QT_INSTALL_DATA")
|| name == QLatin1String("QT_INSTALL_LIBS")
|| name == QLatin1String("QT_INSTALL_BINS")) {
// Qt4 fallback
QString hname = name;
hname.replace(3, 7, QLatin1String("HOST"));
properties.insert(ProKey(hname), value);
properties.insert(ProKey(hname + QLatin1String("/get")), value);
properties.insert(ProKey(hname + QLatin1String("/src")), value);
}
properties.insert(ProKey(name + QLatin1String("/raw")), value);
}
if (variant <= PropRaw)
properties.insert(ProKey(name + QLatin1String("/dev")), value);
} else if (!name.startsWith(QLatin1String("QT_HOST_"))) {
continue;
}
if (variant != PropRaw) {
if (variant < PropGet)
properties.insert(ProKey(name + QLatin1String("/get")), value);
properties.insert(ProKey(name + QLatin1String("/src")), value);
}
}
}
}
#endif // QT_BUILD_QMAKE
QT_END_NAMESPACE

View File

@ -0,0 +1,146 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMAKEGLOBALS_H
#define QMAKEGLOBALS_H
#include "qmake_global.h"
#include "proitems.h"
#ifdef QT_BUILD_QMAKE
# include <property.h>
#endif
#include <qhash.h>
#include <qstringlist.h>
#if QT_CONFIG(process)
# include <qprocess.h>
#endif
#ifdef PROEVALUATOR_THREAD_SAFE
# include <qmutex.h>
# include <qwaitcondition.h>
#endif
QT_BEGIN_NAMESPACE
class QMakeEvaluator;
enum QMakeEvalPhase { QMakeEvalEarly, QMakeEvalBefore, QMakeEvalAfter, QMakeEvalLate };
class QMakeBaseKey
{
public:
QMakeBaseKey(const QString &_root, const QString &_stash, bool _hostBuild);
QString root;
QString stash;
bool hostBuild;
};
size_t qHash(const QMakeBaseKey &key);
bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two);
class QMakeBaseEnv
{
public:
QMakeBaseEnv();
~QMakeBaseEnv();
#ifdef PROEVALUATOR_THREAD_SAFE
QMutex mutex;
QWaitCondition cond;
bool inProgress;
// The coupling of this flag to thread safety exists because for other
// use cases failure is immediately fatal anyway.
bool isOk;
#endif
QMakeEvaluator *evaluator;
};
class QMAKE_EXPORT QMakeCmdLineParserState
{
public:
QMakeCmdLineParserState(const QString &_pwd) : pwd(_pwd), phase(QMakeEvalBefore) {}
QString pwd;
QStringList cmds[4], configs[4];
QStringList extraargs;
QMakeEvalPhase phase;
void flush() { phase = QMakeEvalBefore; }
};
class QMAKE_EXPORT QMakeGlobals
{
public:
QMakeGlobals();
~QMakeGlobals();
bool do_cache;
QString dir_sep;
QString dirlist_sep;
QString cachefile;
#ifdef PROEVALUATOR_SETENV
QProcessEnvironment environment;
#endif
QString qmake_abslocation;
QStringList qmake_args, qmake_extra_args;
QString qtconf;
QString qmakespec, xqmakespec;
QString user_template, user_template_prefix;
QString extra_cmds[4];
#ifdef PROEVALUATOR_DEBUG
int debugLevel;
#endif
enum ArgumentReturn { ArgumentUnknown, ArgumentMalformed, ArgumentsOk };
ArgumentReturn addCommandLineArguments(QMakeCmdLineParserState &state,
QStringList &args, int *pos);
void commitCommandLineArguments(QMakeCmdLineParserState &state);
void setCommandLineArguments(const QString &pwd, const QStringList &args);
void useEnvironment();
void setDirectories(const QString &input_dir, const QString &output_dir);
#ifdef QT_BUILD_QMAKE
void setQMakeProperty(QMakeProperty *prop) { property = prop; }
void reloadProperties() { property->reload(); }
ProString propertyValue(const ProKey &name) const { return property->value(name); }
#else
static void parseProperties(const QByteArray &data, QHash<ProKey, ProString> &props);
# ifdef PROEVALUATOR_INIT_PROPS
bool initProperties();
# else
void setProperties(const QHash<ProKey, ProString> &props) { properties = props; }
# endif
ProString propertyValue(const ProKey &name) const { return properties.value(name); }
#endif
QString expandEnvVars(const QString &str) const;
QString shadowedPath(const QString &fileName) const;
QStringList splitPathList(const QString &value) const;
private:
QString getEnv(const QString &) const;
QStringList getPathListEnv(const QString &var) const;
QString cleanSpec(QMakeCmdLineParserState &state, const QString &spec);
QString source_root, build_root;
#ifdef QT_BUILD_QMAKE
QMakeProperty *property;
#else
QHash<ProKey, ProString> properties;
#endif
#ifdef PROEVALUATOR_THREAD_SAFE
QMutex mutex;
#endif
QHash<QMakeBaseKey, QMakeBaseEnv *> baseEnvs;
friend class QMakeEvaluator;
};
QT_END_NAMESPACE
#endif // QMAKEGLOBALS_H

File diff suppressed because it is too large Load Diff

214
qmake/library/qmakeparser.h Normal file
View File

@ -0,0 +1,214 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMAKEPARSER_H
#define QMAKEPARSER_H
#include "qmake_global.h"
#include "qmakevfs.h"
#include "proitems.h"
#include <qhash.h>
#include <qstack.h>
#ifdef PROPARSER_THREAD_SAFE
# include <qmutex.h>
# include <qwaitcondition.h>
#endif
QT_BEGIN_NAMESPACE
class QMAKE_EXPORT QMakeParserHandler
{
public:
enum {
CategoryMask = 0xf00,
InfoMessage = 0x100,
WarningMessage = 0x200,
ErrorMessage = 0x300,
SourceMask = 0xf0,
SourceParser = 0,
CodeMask = 0xf,
WarnLanguage = 0,
WarnDeprecated,
ParserWarnLanguage = SourceParser | WarningMessage | WarnLanguage,
ParserWarnDeprecated = SourceParser | WarningMessage | WarnDeprecated,
ParserIoError = ErrorMessage | SourceParser,
ParserError
};
virtual void message(int type, const QString &msg,
const QString &fileName = QString(), int lineNo = 0) = 0;
};
class ProFileCache;
class QMakeVfs;
class QMAKE_EXPORT QMakeParser
{
public:
// Call this from a concurrency-free context
static void initialize();
enum ParseFlag {
ParseDefault = 0,
ParseUseCache = 1,
ParseReportMissing = 4,
#ifdef PROEVALUATOR_DUAL_VFS
ParseCumulative = 8
#else
ParseCumulative = 0
#endif
};
Q_DECLARE_FLAGS(ParseFlags, ParseFlag)
QMakeParser(ProFileCache *cache, QMakeVfs *vfs, QMakeParserHandler *handler);
enum SubGrammar { FullGrammar, TestGrammar, ValueGrammar };
// fileName is expected to be absolute and cleanPath()ed.
ProFile *parsedProFile(const QString &fileName, ParseFlags flags = ParseDefault);
ProFile *parsedProBlock(QStringView contents, int id, const QString &name, int line = 0,
SubGrammar grammar = FullGrammar);
void discardFileFromCache(int id);
#ifdef PROPARSER_DEBUG
static QString formatProBlock(const QString &block);
#endif
private:
enum ScopeNesting {
NestNone = 0,
NestLoop = 1,
NestFunction = 2
};
struct BlockScope {
BlockScope() : start(nullptr), braceLevel(0), special(false), inBranch(false), nest(NestNone) {}
ushort *start; // Where this block started; store length here
int braceLevel; // Nesting of braces in scope
bool special; // Single-line conditionals inside loops, etc. cannot have else branches
bool inBranch; // The 'else' branch of the previous TokBranch is still open
uchar nest; // Into what control structures we are nested
};
enum ScopeState {
StNew, // Fresh scope
StCtrl, // Control statement (for or else) met on current line
StCond // Conditionals met on current line
};
enum Context { CtxTest, CtxValue, CtxPureValue, CtxArgs };
struct ParseCtx {
int parens; // Nesting of non-functional parentheses
int argc; // Number of arguments in current function call
int wordCount; // Number of words in current expression
Context context;
ushort quote; // Enclosing quote type
ushort terminator; // '}' if replace function call is braced, ':' if test function
};
bool readFile(int id, QMakeParser::ParseFlags flags, QString *contents);
void read(ProFile *pro, QStringView content, int line, SubGrammar grammar);
ALWAYS_INLINE void putTok(ushort *&tokPtr, ushort tok);
ALWAYS_INLINE void putBlockLen(ushort *&tokPtr, uint len);
ALWAYS_INLINE void putBlock(ushort *&tokPtr, const ushort *buf, uint len);
void putHashStr(ushort *&pTokPtr, const ushort *buf, uint len);
void finalizeHashStr(ushort *buf, uint len);
void putLineMarker(ushort *&tokPtr);
ALWAYS_INLINE bool resolveVariable(ushort *xprPtr, int tlen, int needSep, ushort **ptr,
ushort **buf, QString *xprBuff,
ushort **tokPtr, QString *tokBuff,
const ushort *cur, QStringView in);
void finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount);
void finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc);
void warnOperator(const char *msg);
bool failOperator(const char *msg);
bool acceptColon(const char *msg);
void putOperator(ushort *&tokPtr);
void finalizeTest(ushort *&tokPtr);
void bogusTest(ushort *&tokPtr, const QString &msg);
void enterScope(ushort *&tokPtr, bool special, ScopeState state);
void leaveScope(ushort *&tokPtr);
void flushCond(ushort *&tokPtr);
void flushScopes(ushort *&tokPtr);
void message(int type, const QString &msg) const;
void parseError(const QString &msg) const
{
message(QMakeParserHandler::ParserError, msg);
m_proFile->setOk(false);
}
void languageWarning(const QString &msg) const
{ message(QMakeParserHandler::ParserWarnLanguage, msg); }
void deprecationWarning(const QString &msg) const
{ message(QMakeParserHandler::ParserWarnDeprecated, msg); }
// Current location
ProFile *m_proFile;
int m_lineNo;
QStack<BlockScope> m_blockstack;
ScopeState m_state;
int m_markLine; // Put marker for this line
bool m_inError; // Current line had a parsing error; suppress followup error messages
bool m_canElse; // Conditionals met on previous line, but no scope was opened
int m_invert; // Pending conditional is negated
enum { NoOperator, AndOperator, OrOperator } m_operator; // Pending conditional is ORed/ANDed
QString m_tmp; // Temporary for efficient toQString
ProFileCache *m_cache;
QMakeParserHandler *m_handler;
QMakeVfs *m_vfs;
// This doesn't help gcc 3.3 ...
template<typename T> friend class QTypeInfo;
friend class ProFileCache;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeParser::ParseFlags)
class QMAKE_EXPORT ProFileCache
{
public:
ProFileCache();
~ProFileCache();
void discardFile(int id);
void discardFile(const QString &fileName, QMakeVfs *vfs);
void discardFiles(const QString &prefix, QMakeVfs *vfs);
private:
struct Entry {
ProFile *pro;
#ifdef PROPARSER_THREAD_SAFE
struct Locker {
Locker() : waiters(0), done(false) {}
QWaitCondition cond;
int waiters;
bool done;
};
Locker *locker;
#endif
};
QHash<int, Entry> parsed_files;
#ifdef PROPARSER_THREAD_SAFE
QMutex mutex;
#endif
friend class QMakeParser;
};
#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
Q_DECLARE_TYPEINFO(QMakeParser::BlockScope, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(QMakeParser::Context, Q_PRIMITIVE_TYPE);
#endif
QT_END_NAMESPACE
#endif // PROFILEPARSER_H

257
qmake/library/qmakevfs.cpp Normal file
View File

@ -0,0 +1,257 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmakevfs.h"
#include "ioutils.h"
using namespace QMakeInternal;
#include <qdir.h>
#include <qfile.h>
#include <qfileinfo.h>
#define fL1S(s) QString::fromLatin1(s)
QT_BEGIN_NAMESPACE
QMakeVfs::QMakeVfs()
#ifndef PROEVALUATOR_FULL
: m_magicMissing(fL1S("missing"))
, m_magicExisting(fL1S("existing"))
#endif
{
ref();
}
QMakeVfs::~QMakeVfs()
{
deref();
}
void QMakeVfs::ref()
{
#ifdef PROEVALUATOR_THREAD_SAFE
QMutexLocker locker(&s_mutex);
#endif
++s_refCount;
}
void QMakeVfs::deref()
{
#ifdef PROEVALUATOR_THREAD_SAFE
QMutexLocker locker(&s_mutex);
#endif
if (!--s_refCount) {
s_fileIdCounter = 0;
s_fileIdMap.clear();
s_idFileMap.clear();
}
}
#ifdef PROPARSER_THREAD_SAFE
QMutex QMakeVfs::s_mutex;
#endif
int QMakeVfs::s_refCount;
QAtomicInt QMakeVfs::s_fileIdCounter;
QHash<QString, int> QMakeVfs::s_fileIdMap;
QHash<int, QString> QMakeVfs::s_idFileMap;
int QMakeVfs::idForFileName(const QString &fn, VfsFlags flags)
{
#ifdef PROEVALUATOR_DUAL_VFS
{
# ifdef PROPARSER_THREAD_SAFE
QMutexLocker locker(&m_vmutex);
# endif
int idx = (flags & VfsCumulative) ? 1 : 0;
if (flags & VfsCreate) {
int &id = m_virtualFileIdMap[idx][fn];
if (!id) {
id = ++s_fileIdCounter;
m_virtualIdFileMap[id] = fn;
}
return id;
}
int id = m_virtualFileIdMap[idx].value(fn);
if (id || (flags & VfsCreatedOnly))
return id;
}
#endif
#ifdef PROPARSER_THREAD_SAFE
QMutexLocker locker(&s_mutex);
#endif
if (!(flags & VfsAccessedOnly)) {
int &id = s_fileIdMap[fn];
if (!id) {
id = ++s_fileIdCounter;
s_idFileMap[id] = fn;
}
return id;
}
return s_fileIdMap.value(fn);
}
QString QMakeVfs::fileNameForId(int id)
{
#ifdef PROEVALUATOR_DUAL_VFS
{
# ifdef PROPARSER_THREAD_SAFE
QMutexLocker locker(&m_vmutex);
# endif
const QString &fn = m_virtualIdFileMap.value(id);
if (!fn.isEmpty())
return fn;
}
#endif
#ifdef PROPARSER_THREAD_SAFE
QMutexLocker locker(&s_mutex);
#endif
return s_idFileMap.value(id);
}
bool QMakeVfs::writeFile(int id, QIODevice::OpenMode mode, VfsFlags flags,
const QString &contents, QString *errStr)
{
#ifndef PROEVALUATOR_FULL
# ifdef PROEVALUATOR_THREAD_SAFE
QMutexLocker locker(&m_mutex);
# endif
QString *cont = &m_files[id];
Q_UNUSED(flags);
if (mode & QIODevice::Append)
*cont += contents;
else
*cont = contents;
Q_UNUSED(errStr);
return true;
#else
QFileInfo qfi(fileNameForId(id));
if (!QDir::current().mkpath(qfi.path())) {
*errStr = fL1S("Cannot create parent directory");
return false;
}
QByteArray bytes = contents.toLocal8Bit();
QFile cfile(qfi.filePath());
if (!(mode & QIODevice::Append) && cfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
if (cfile.readAll() == bytes) {
if (flags & VfsExecutable) {
cfile.setPermissions(cfile.permissions()
| QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther);
} else {
cfile.setPermissions(cfile.permissions()
& ~(QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther));
}
return true;
}
cfile.close();
}
if (!cfile.open(mode | QIODevice::WriteOnly | QIODevice::Text)) {
*errStr = cfile.errorString();
return false;
}
cfile.write(bytes);
cfile.close();
if (cfile.error() != QFile::NoError) {
*errStr = cfile.errorString();
return false;
}
if (flags & VfsExecutable)
cfile.setPermissions(cfile.permissions()
| QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther);
return true;
#endif
}
QMakeVfs::ReadResult QMakeVfs::readFile(int id, QString *contents, QString *errStr)
{
#ifndef PROEVALUATOR_FULL
# ifdef PROEVALUATOR_THREAD_SAFE
QMutexLocker locker(&m_mutex);
# endif
auto it = m_files.constFind(id);
if (it != m_files.constEnd()) {
if (it->constData() == m_magicMissing.constData()) {
*errStr = fL1S("No such file or directory");
return ReadNotFound;
}
if (it->constData() != m_magicExisting.constData()) {
*contents = *it;
return ReadOk;
}
}
#endif
QFile file(fileNameForId(id));
if (!file.open(QIODevice::ReadOnly)) {
if (!file.exists()) {
#ifndef PROEVALUATOR_FULL
m_files[id] = m_magicMissing;
#endif
*errStr = fL1S("No such file or directory");
return ReadNotFound;
}
*errStr = file.errorString();
return ReadOtherError;
}
#ifndef PROEVALUATOR_FULL
m_files[id] = m_magicExisting;
#endif
QByteArray bcont = file.readAll();
if (bcont.startsWith("\xef\xbb\xbf")) {
// UTF-8 BOM will cause subtle errors
*errStr = fL1S("Unexpected UTF-8 BOM");
return ReadOtherError;
}
*contents = QString::fromLocal8Bit(bcont);
return ReadOk;
}
bool QMakeVfs::exists(const QString &fn, VfsFlags flags)
{
#ifndef PROEVALUATOR_FULL
# ifdef PROEVALUATOR_THREAD_SAFE
QMutexLocker locker(&m_mutex);
# endif
int id = idForFileName(fn, flags);
auto it = m_files.constFind(id);
if (it != m_files.constEnd())
return it->constData() != m_magicMissing.constData();
#else
Q_UNUSED(flags);
#endif
bool ex = IoUtils::fileType(fn) == IoUtils::FileIsRegular;
#ifndef PROEVALUATOR_FULL
m_files[id] = ex ? m_magicExisting : m_magicMissing;
#endif
return ex;
}
#ifndef PROEVALUATOR_FULL
// This should be called when the sources may have changed (e.g., VCS update).
void QMakeVfs::invalidateCache()
{
# ifdef PROEVALUATOR_THREAD_SAFE
QMutexLocker locker(&m_mutex);
# endif
auto it = m_files.begin(), eit = m_files.end();
while (it != eit) {
if (it->constData() == m_magicMissing.constData()
||it->constData() == m_magicExisting.constData())
it = m_files.erase(it);
else
++it;
}
}
// This should be called when generated files may have changed (e.g., actual build).
void QMakeVfs::invalidateContents()
{
# ifdef PROEVALUATOR_THREAD_SAFE
QMutexLocker locker(&m_mutex);
# endif
m_files.clear();
}
#endif
QT_END_NAMESPACE

105
qmake/library/qmakevfs.h Normal file
View File

@ -0,0 +1,105 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMAKEVFS_H
#define QMAKEVFS_H
#include "qmake_global.h"
#include <qiodevice.h>
#include <qhash.h>
#include <qstring.h>
#ifdef PROEVALUATOR_THREAD_SAFE
# include <qmutex.h>
#endif
#ifdef PROEVALUATOR_DUAL_VFS
# ifndef PROEVALUATOR_CUMULATIVE
# error PROEVALUATOR_DUAL_VFS requires PROEVALUATOR_CUMULATIVE
# endif
#endif
QT_BEGIN_NAMESPACE
class QMAKE_EXPORT QMakeVfs
{
public:
enum ReadResult {
ReadOk,
ReadNotFound,
ReadOtherError
};
enum VfsFlag {
VfsExecutable = 1,
VfsExact = 0,
#ifdef PROEVALUATOR_DUAL_VFS
VfsCumulative = 2,
VfsCreate = 4,
VfsCreatedOnly = 8,
#else
VfsCumulative = 0,
VfsCreate = 0,
VfsCreatedOnly = 0,
#endif
VfsAccessedOnly = 16
};
Q_DECLARE_FLAGS(VfsFlags, VfsFlag)
QMakeVfs();
~QMakeVfs();
static void ref();
static void deref();
int idForFileName(const QString &fn, VfsFlags flags);
QString fileNameForId(int id);
bool writeFile(int id, QIODevice::OpenMode mode, VfsFlags flags, const QString &contents, QString *errStr);
ReadResult readFile(int id, QString *contents, QString *errStr);
bool exists(const QString &fn, QMakeVfs::VfsFlags flags);
#ifndef PROEVALUATOR_FULL
void invalidateCache();
void invalidateContents();
#endif
private:
#ifdef PROEVALUATOR_THREAD_SAFE
static QMutex s_mutex;
#endif
static int s_refCount;
static QAtomicInt s_fileIdCounter;
// Qt Creator's ProFile cache is a singleton to maximize its cross-project
// effectiveness (shared prf files from QtVersions).
// For this to actually work, real files need a global mapping.
// This is fine, because the namespace of real files is indeed global.
static QHash<QString, int> s_fileIdMap;
static QHash<int, QString> s_idFileMap;
#ifdef PROEVALUATOR_DUAL_VFS
# ifdef PROEVALUATOR_THREAD_SAFE
// The simple way to avoid recursing m_mutex.
QMutex m_vmutex;
# endif
// Virtual files are bound to the project context they were created in,
// so their ids need to be local as well.
// We violate that rule in lupdate (which has a non-dual VFS), but that
// does not matter, because it has only one project context anyway.
QHash<QString, int> m_virtualFileIdMap[2]; // Exact and cumulative
QHash<int, QString> m_virtualIdFileMap; // Only one map, as ids are unique across realms.
#endif
#ifndef PROEVALUATOR_FULL
# ifdef PROEVALUATOR_THREAD_SAFE
QMutex m_mutex;
# endif
QHash<int, QString> m_files;
QString m_magicMissing;
QString m_magicExisting;
#endif
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeVfs::VfsFlags)
QT_END_NAMESPACE
#endif // QMAKEVFS_H

133
qmake/library/registry.cpp Normal file
View File

@ -0,0 +1,133 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/qstringlist.h>
#include "registry_p.h"
QT_BEGIN_NAMESPACE
#ifdef Q_OS_WIN32
/*
Returns the path part of a registry key.
e.g.
For a key
"Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir"
it returns
"Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\"
*/
static QString keyPath(const QString &rKey)
{
int idx = rKey.lastIndexOf(QLatin1Char('\\'));
if (idx == -1)
return QString();
return rKey.left(idx + 1);
}
/*
Returns the name part of a registry key.
e.g.
For a key
"Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir"
it returns
"ProductDir"
*/
static QString keyName(const QString &rKey)
{
int idx = rKey.lastIndexOf(QLatin1Char('\\'));
if (idx == -1)
return rKey;
QString res(rKey.mid(idx + 1));
if (res == QLatin1String("Default") || res == QLatin1String("."))
res = QString();
return res;
}
#endif
QString qt_readRegistryKey(HKEY parentHandle, const QString &rSubkey, unsigned long options)
{
QString result;
#ifdef Q_OS_WIN32
QString rSubkeyName = keyName(rSubkey);
QString rSubkeyPath = keyPath(rSubkey);
HKEY handle = nullptr;
LONG res = RegOpenKeyEx(parentHandle, (wchar_t*)rSubkeyPath.utf16(), 0,
KEY_READ | options, &handle);
if (res != ERROR_SUCCESS)
return QString();
// get the size and type of the value
DWORD dataType;
DWORD dataSize;
res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), nullptr, &dataType, nullptr, &dataSize);
if (res != ERROR_SUCCESS) {
RegCloseKey(handle);
return QString();
}
// get the value
QByteArray data(dataSize, 0);
res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), nullptr, nullptr,
reinterpret_cast<unsigned char*>(data.data()), &dataSize);
if (res != ERROR_SUCCESS) {
RegCloseKey(handle);
return QString();
}
switch (dataType) {
case REG_EXPAND_SZ:
case REG_SZ: {
result = QString::fromWCharArray(((const wchar_t *)data.constData()));
break;
}
case REG_MULTI_SZ: {
QStringList l;
int i = 0;
for (;;) {
QString s = QString::fromWCharArray((const wchar_t *)data.constData() + i);
i += s.length() + 1;
if (s.isEmpty())
break;
l.append(s);
}
result = l.join(QLatin1String(", "));
break;
}
case REG_NONE:
case REG_BINARY: {
result = QString::fromWCharArray((const wchar_t *)data.constData(), data.size() / 2);
break;
}
case REG_DWORD_BIG_ENDIAN:
case REG_DWORD: {
Q_ASSERT(data.size() == sizeof(int));
int i;
memcpy((char*)&i, data.constData(), sizeof(int));
result = QString::number(i);
break;
}
default:
qWarning("QSettings: unknown data %u type in windows registry", quint32(dataType));
break;
}
RegCloseKey(handle);
#else
Q_UNUSED(parentHandle);
Q_UNUSED(rSubkey);
Q_UNUSED(options);
#endif
return result;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,48 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QT_WINDOWS_REGISTRY_H
#define QT_WINDOWS_REGISTRY_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qglobal.h>
#ifdef Q_OS_WIN32
#include <QtCore/qt_windows.h>
#else
typedef void* HKEY;
#endif
#include <QtCore/qstring.h>
QT_BEGIN_NAMESPACE
/**
* Read a value from the Windows registry.
*
* If the key is not found, or the registry cannot be accessed (for example
* if this code is compiled for a platform other than Windows), a null
* string is returned.
*
* 32-bit code reads from the registry's 32 bit view (Wow6432Node),
* 64 bit code reads from the 64 bit view.
* Pass KEY_WOW64_32KEY to access the 32 bit view regardless of the
* application's architecture, KEY_WOW64_64KEY respectively.
*/
QString qt_readRegistryKey(HKEY parentHandle, const QString &rSubkey,
unsigned long options = 0);
QT_END_NAMESPACE
#endif // QT_WINDOWS_REGISTRY_H

579
qmake/main.cpp Normal file
View File

@ -0,0 +1,579 @@
// Copyright (C) 2020 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "project.h"
#include "property.h"
#include "option.h"
#include "cachekeys.h"
#include "metamakefile.h"
#include <qcoreapplication.h>
#include <qnamespace.h>
#include <qdebug.h>
#include <qregularexpression.h>
#include <qdir.h>
#include <qdiriterator.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(Q_OS_UNIX)
#include <errno.h>
#include <unistd.h>
#endif
#ifdef Q_OS_WIN
# include <qt_windows.h>
#endif
using namespace QMakeInternal;
QT_BEGIN_NAMESPACE
#ifdef Q_OS_WIN
struct SedSubst {
QRegularExpression from;
QString to;
};
Q_DECLARE_TYPEINFO(SedSubst, Q_RELOCATABLE_TYPE);
static int doSed(int argc, char **argv)
{
QList<SedSubst> substs;
QList<const char *> inFiles;
for (int i = 0; i < argc; i++) {
if (!strcmp(argv[i], "-e")) {
if (++i == argc) {
fprintf(stderr, "Error: sed option -e requires an argument\n");
return 3;
}
QString cmd = QString::fromLocal8Bit(argv[i]);
for (int j = 0; j < cmd.length(); j++) {
QChar c = cmd.at(j);
if (c.isSpace())
continue;
if (c != QLatin1Char('s')) {
fprintf(stderr, "Error: unrecognized sed command '%c'\n", c.toLatin1());
return 3;
}
QChar sep = ++j < cmd.length() ? cmd.at(j) : QChar();
QRegularExpression::PatternOptions matchcase = QRegularExpression::NoPatternOption;
bool escaped = false;
int phase = 1;
QStringList phases;
QString curr;
while (++j < cmd.length()) {
c = cmd.at(j);
if (!escaped) {
if (c == QLatin1Char(';'))
break;
if (c == QLatin1Char('\\')) {
escaped = true;
continue;
}
if (c == sep) {
phase++;
phases << curr;
curr.clear();
continue;
}
}
if (phase == 1
&& (c == QLatin1Char('+') || c == QLatin1Char('?') || c == QLatin1Char('|')
|| c == QLatin1Char('{') || c == QLatin1Char('}')
|| c == QLatin1Char('(') || c == QLatin1Char(')'))) {
// translate sed rx to QRegularExpression
escaped ^= 1;
}
if (escaped) {
escaped = false;
curr += QLatin1Char('\\');
}
curr += c;
}
if (escaped) {
fprintf(stderr, "Error: unterminated escape sequence in sed s command\n");
return 3;
}
if (phase != 3) {
fprintf(stderr, "Error: sed s command requires three arguments (%d, %c, %s)\n", phase, sep.toLatin1(), qPrintable(curr));
return 3;
}
if (curr.contains(QLatin1Char('i'))) {
curr.remove(QLatin1Char('i'));
matchcase = QRegularExpression::CaseInsensitiveOption;
}
if (curr != QLatin1String("g")) {
fprintf(stderr, "Error: sed s command supports only g & i options; g is required\n");
return 3;
}
SedSubst subst;
subst.from = QRegularExpression(phases.at(0), matchcase);
subst.to = phases.at(1);
subst.to.replace(QLatin1String("\\\\"), QLatin1String("\\")); // QString::replace(rx, sub) groks \1, but not \\.
substs << subst;
}
} else if (argv[i][0] == '-' && argv[i][1] != 0) {
fprintf(stderr, "Error: unrecognized sed option '%s'\n", argv[i]);
return 3;
} else {
inFiles << argv[i];
}
}
if (inFiles.isEmpty())
inFiles << "-";
for (const char *inFile : std::as_const(inFiles)) {
FILE *f;
if (!strcmp(inFile, "-")) {
f = stdin;
} else if (!(f = fopen(inFile, "rb"))) {
perror(inFile);
return 1;
}
QTextStream is(f);
while (!is.atEnd()) {
QString line = is.readLine();
for (int i = 0; i < substs.size(); i++)
line.replace(substs.at(i).from, substs.at(i).to);
puts(qPrintable(line));
}
if (f != stdin)
fclose(f);
}
return 0;
}
static int doLink(int argc, char **argv)
{
bool isSymlink = false;
bool force = false;
QList<const char *> inFiles;
for (int i = 0; i < argc; i++) {
if (!strcmp(argv[i], "-s")) {
isSymlink = true;
} else if (!strcmp(argv[i], "-f")) {
force = true;
} else if (argv[i][0] == '-') {
fprintf(stderr, "Error: unrecognized ln option '%s'\n", argv[i]);
return 3;
} else {
inFiles << argv[i];
}
}
if (inFiles.size() != 2) {
fprintf(stderr, "Error: this ln requires exactly two file arguments\n");
return 3;
}
if (!isSymlink) {
fprintf(stderr, "Error: this ln supports faking symlinks only\n");
return 3;
}
QString target = QString::fromLocal8Bit(inFiles[0]);
QString linkname = QString::fromLocal8Bit(inFiles[1]);
QDir destdir;
QFileInfo tfi(target);
QFileInfo lfi(linkname);
if (lfi.isDir()) {
destdir.setPath(linkname);
lfi.setFile(destdir, tfi.fileName());
} else {
destdir.setPath(lfi.path());
}
if (!destdir.exists()) {
fprintf(stderr, "Error: destination directory %s does not exist\n", qPrintable(destdir.path()));
return 1;
}
tfi.setFile(destdir.absoluteFilePath(tfi.filePath()));
if (!tfi.exists()) {
fprintf(stderr, "Error: this ln does not support symlinking non-existing targets\n");
return 3;
}
if (tfi.isDir()) {
fprintf(stderr, "Error: this ln does not support symlinking directories\n");
return 3;
}
if (lfi.exists()) {
if (!force) {
fprintf(stderr, "Error: %s exists\n", qPrintable(lfi.filePath()));
return 1;
}
if (!QFile::remove(lfi.filePath())) {
fprintf(stderr, "Error: cannot overwrite %s\n", qPrintable(lfi.filePath()));
return 1;
}
}
if (!QFile::copy(tfi.filePath(), lfi.filePath())) {
fprintf(stderr, "Error: cannot copy %s to %s\n",
qPrintable(tfi.filePath()), qPrintable(lfi.filePath()));
return 1;
}
return 0;
}
#endif
static bool setFilePermissions(QFile &file, QFileDevice::Permissions permissions)
{
if (file.setPermissions(permissions))
return true;
fprintf(stderr, "Error setting permissions on %s: %s\n",
qPrintable(file.fileName()), qPrintable(file.errorString()));
return false;
}
static bool copyFileTimes(QFile &targetFile, const QString &sourceFilePath,
bool mustEnsureWritability, QString *errorString)
{
#ifdef Q_OS_WIN
bool mustRestorePermissions = false;
QFileDevice::Permissions targetPermissions;
if (mustEnsureWritability) {
targetPermissions = targetFile.permissions();
if (!targetPermissions.testFlag(QFileDevice::WriteUser)) {
mustRestorePermissions = true;
if (!setFilePermissions(targetFile, targetPermissions | QFileDevice::WriteUser))
return false;
}
}
#else
Q_UNUSED(mustEnsureWritability);
#endif
if (!IoUtils::touchFile(targetFile.fileName(), sourceFilePath, errorString))
return false;
#ifdef Q_OS_WIN
if (mustRestorePermissions && !setFilePermissions(targetFile, targetPermissions))
return false;
#endif
return true;
}
static int installFile(const QString &source, const QString &target, bool exe = false,
bool preservePermissions = false)
{
QFile sourceFile(source);
QFile targetFile(target);
if (targetFile.exists()) {
#ifdef Q_OS_WIN
targetFile.setPermissions(targetFile.permissions() | QFile::WriteUser);
#endif
QFile::remove(target);
} else {
QDir::root().mkpath(QFileInfo(target).absolutePath());
}
if (!sourceFile.copy(target)) {
fprintf(stderr, "Error copying %s to %s: %s\n", source.toLatin1().constData(), qPrintable(target), qPrintable(sourceFile.errorString()));
return 3;
}
QFileDevice::Permissions targetPermissions = preservePermissions
? sourceFile.permissions()
: (QFileDevice::ReadOwner | QFileDevice::WriteOwner
| QFileDevice::ReadUser | QFileDevice::WriteUser
| QFileDevice::ReadGroup | QFileDevice::ReadOther);
if (exe) {
targetPermissions |= QFileDevice::ExeOwner | QFileDevice::ExeUser |
QFileDevice::ExeGroup | QFileDevice::ExeOther;
}
if (!setFilePermissions(targetFile, targetPermissions))
return 3;
QString error;
if (!copyFileTimes(targetFile, sourceFile.fileName(), preservePermissions, &error)) {
fprintf(stderr, "%s", qPrintable(error));
return 3;
}
return 0;
}
static int installFileOrDirectory(const QString &source, const QString &target,
bool preservePermissions = false)
{
QFileInfo fi(source);
if (false) {
#if defined(Q_OS_UNIX)
} else if (fi.isSymLink()) {
QString linkTarget;
if (!IoUtils::readLinkTarget(fi.absoluteFilePath(), &linkTarget)) {
fprintf(stderr, "Could not read link %s: %s\n", qPrintable(fi.absoluteFilePath()), strerror(errno));
return 3;
}
QFile::remove(target);
if (::symlink(linkTarget.toLocal8Bit().constData(), target.toLocal8Bit().constData()) < 0) {
fprintf(stderr, "Could not create link: %s\n", strerror(errno));
return 3;
}
#endif
} else if (fi.isDir()) {
QDir::current().mkpath(target);
QDirIterator it(source, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
while (it.hasNext()) {
const QFileInfo entry = it.nextFileInfo();
const QString &entryTarget = target + QDir::separator() + entry.fileName();
const int recursionResult = installFileOrDirectory(entry.filePath(), entryTarget, true);
if (recursionResult != 0)
return recursionResult;
}
} else {
const int fileCopyResult = installFile(source, target, /*exe*/ false, preservePermissions);
if (fileCopyResult != 0)
return fileCopyResult;
}
return 0;
}
static int doQInstall(int argc, char **argv)
{
bool installExecutable = false;
if (argc == 3 && !strcmp(argv[0], "-exe")) {
installExecutable = true;
--argc;
++argv;
}
if (argc != 2 && !installExecutable) {
fprintf(stderr, "Error: usage: [-exe] source target\n");
return 3;
}
const QString source = QString::fromLocal8Bit(argv[0]);
const QString target = QString::fromLocal8Bit(argv[1]);
if (installExecutable)
return installFile(source, target, /*exe=*/true);
return installFileOrDirectory(source, target);
}
static int doInstall(int argc, char **argv)
{
if (!argc) {
fprintf(stderr, "Error: -install requires further arguments\n");
return 3;
}
#ifdef Q_OS_WIN
if (!strcmp(argv[0], "sed"))
return doSed(argc - 1, argv + 1);
if (!strcmp(argv[0], "ln"))
return doLink(argc - 1, argv + 1);
#endif
if (!strcmp(argv[0], "qinstall"))
return doQInstall(argc - 1, argv + 1);
fprintf(stderr, "Error: unrecognized -install subcommand '%s'\n", argv[0]);
return 3;
}
#ifdef Q_OS_WIN
static int dumpMacros(const wchar_t *cmdline)
{
// from http://stackoverflow.com/questions/3665537/how-to-find-out-cl-exes-built-in-macros
int argc;
wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
if (!argv)
return 2;
for (int i = 0; i < argc; ++i) {
if (argv[i][0] != L'-' || argv[i][1] != 'D')
continue;
wchar_t *value = wcschr(argv[i], L'=');
if (value) {
*value = 0;
++value;
} else {
// point to the NUL at the end, so we don't print anything
value = argv[i] + wcslen(argv[i]);
}
wprintf(L"#define %Ls %Ls\n", argv[i] + 2, value);
}
return 0;
}
#endif // Q_OS_WIN
/* This is to work around lame implementation on Darwin. It has been noted that the getpwd(3) function
is much too slow, and called much too often inside of Qt (every fileFixify). With this we use a locally
cached copy because I can control all the times it is set (because Qt never sets the pwd under me).
*/
static QString pwd;
QString qmake_getpwd()
{
if(pwd.isNull())
pwd = QDir::currentPath();
return pwd;
}
bool qmake_setpwd(const QString &p)
{
if(QDir::setCurrent(p)) {
pwd = QDir::currentPath();
return true;
}
return false;
}
int runQMake(int argc, char **argv)
{
QHashSeed::setDeterministicGlobalSeed();
// stderr is unbuffered by default, but stdout buffering depends on whether
// there is a terminal attached. Buffering can make output from stderr and stdout
// appear out of sync, so force stdout to be unbuffered as well.
// This is particularly important for things like QtCreator and scripted builds.
setvbuf(stdout, (char *)NULL, _IONBF, 0);
// Workaround for inferior/missing command line tools on Windows: make our own!
if (argc >= 4 && !strcmp(argv[1], "-qtconf") && !strcmp(argv[3], "-install"))
return doInstall(argc - 4, argv + 4);
if (argc >= 2 && !strcmp(argv[1], "-install"))
return doInstall(argc - 2, argv + 2);
#ifdef Q_OS_WIN
{
// Support running as Visual C++'s compiler
const wchar_t *cmdline = _wgetenv(L"MSC_CMD_FLAGS");
if (!cmdline || !*cmdline)
cmdline = _wgetenv(L"MSC_IDE_FLAGS");
if (cmdline && *cmdline)
return dumpMacros(cmdline);
}
#endif
QMakeVfs vfs;
Option::vfs = &vfs;
QMakeGlobals globals;
Option::globals = &globals;
// parse command line
int ret = Option::init(argc, argv);
if(ret != Option::QMAKE_CMDLINE_SUCCESS) {
if ((ret & Option::QMAKE_CMDLINE_ERROR) != 0)
return 1;
return 0;
}
QString oldpwd = qmake_getpwd();
Option::output_dir = oldpwd; //for now this is the output dir
if (!Option::output.fileName().isEmpty() && Option::output.fileName() != "-") {
// The output 'filename', as given by the -o option, might include one
// or more directories, so we may need to rebase the output directory.
QFileInfo fi(Option::output);
QDir dir(QDir::cleanPath(fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath()));
// Don't treat Xcode project directory as part of OUT_PWD
if (dir.dirName().endsWith(QLatin1String(".xcodeproj"))) {
// Note: we're intentionally not using cdUp(), as the dir may not exist
dir.setPath(QDir::cleanPath(dir.filePath("..")));
}
Option::output_dir = dir.path();
QString absoluteFilePath = QDir::cleanPath(fi.absoluteFilePath());
Option::output.setFileName(absoluteFilePath.mid(Option::output_dir.size() + 1));
}
QMakeProperty prop;
switch (Option::qmake_mode) {
case Option::QMAKE_QUERY_PROPERTY:
return prop.queryProperty(Option::prop::properties);
case Option::QMAKE_SET_PROPERTY:
return prop.setProperty(Option::prop::properties);
case Option::QMAKE_UNSET_PROPERTY:
prop.unsetProperty(Option::prop::properties);
return 0;
default:
break;
}
globals.setQMakeProperty(&prop);
ProFileCache proFileCache;
Option::proFileCache = &proFileCache;
QMakeParser parser(&proFileCache, &vfs, &Option::evalHandler);
Option::parser = &parser;
QMakeProject project;
int exit_val = 0;
QStringList files;
if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT)
files << "(*hack*)"; //we don't even use files, but we do the for() body once
else
files = Option::mkfile::project_files;
for(QStringList::Iterator pfile = files.begin(); pfile != files.end(); pfile++) {
if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
QString fn = Option::normalizePath(*pfile);
if(!QFile::exists(fn)) {
fprintf(stderr, "Cannot find file: %s.\n",
QDir::toNativeSeparators(fn).toLatin1().constData());
exit_val = 2;
continue;
}
//setup pwd properly
debug_msg(1, "Resetting dir to: %s",
QDir::toNativeSeparators(oldpwd).toLatin1().constData());
qmake_setpwd(oldpwd); //reset the old pwd
int di = fn.lastIndexOf(QLatin1Char('/'));
if(di != -1) {
debug_msg(1, "Changing dir to: %s",
QDir::toNativeSeparators(fn.left(di)).toLatin1().constData());
if(!qmake_setpwd(fn.left(di)))
fprintf(stderr, "Cannot find directory: %s\n",
QDir::toNativeSeparators(fn.left(di)).toLatin1().constData());
fn = fn.right(fn.size() - di - 1);
}
Option::prepareProject(fn);
// read project..
if(!project.read(fn)) {
fprintf(stderr, "Error processing project file: %s\n",
QDir::toNativeSeparators(*pfile).toLatin1().constData());
exit_val = 3;
continue;
}
if (Option::mkfile::do_preprocess) {
project.dump();
continue; //no need to create makefile
}
}
bool success = true;
MetaMakefileGenerator *mkfile = MetaMakefileGenerator::createMetaGenerator(&project, QString(), false, &success);
if (!success)
exit_val = 3;
if (mkfile && !mkfile->write()) {
if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT)
fprintf(stderr, "Unable to generate project file.\n");
else
fprintf(stderr, "Unable to generate makefile for: %s\n",
QDir::toNativeSeparators(*pfile).toLatin1().constData());
exit_val = 5;
}
delete mkfile;
mkfile = nullptr;
}
qmakeClearCaches();
return exit_val;
}
QT_END_NAMESPACE
int main(int argc, char **argv)
{
// Set name of the qmake application in QCoreApplication instance
QT_PREPEND_NAMESPACE(QCoreApplication) app(argc, argv);
return QT_PREPEND_NAMESPACE(runQMake)(argc, argv);
}

42
qmake/meta.cpp Normal file
View File

@ -0,0 +1,42 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "meta.h"
#include "project.h"
#include "option.h"
#include <qdir.h>
QT_BEGIN_NAMESPACE
QHash<QString, ProValueMap> QMakeMetaInfo::cache_vars;
bool
QMakeMetaInfo::readLib(const QString &meta_file)
{
if(cache_vars.contains(meta_file)) {
vars = cache_vars[meta_file];
return true;
}
QMakeProject proj;
if (!proj.read(Option::normalizePath(meta_file), QMakeEvaluator::LoadProOnly))
return false;
vars = proj.variables();
cache_vars.insert(meta_file, vars);
return true;
}
QString
QMakeMetaInfo::checkLib(const QString &lib)
{
QString ret = QFile::exists(lib) ? lib : QString();
if(ret.isNull()) {
debug_msg(2, "QMakeMetaInfo: Cannot find info file for %s", lib.toLatin1().constData());
} else {
debug_msg(2, "QMakeMetaInfo: Found info file %s for %s", ret.toLatin1().constData(), lib.toLatin1().constData());
}
return ret;
}
QT_END_NAMESPACE

57
qmake/meta.h Normal file
View File

@ -0,0 +1,57 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef META_H
#define META_H
#include "project.h"
#include <qhash.h>
#include <qstringlist.h>
#include <qstring.h>
QT_BEGIN_NAMESPACE
class QMakeProject;
class QMakeMetaInfo
{
ProValueMap vars;
static QHash<QString, ProValueMap> cache_vars;
public:
// These functions expect the path to be normalized
static QString checkLib(const QString &lib);
bool readLib(const QString &meta_file);
bool isEmpty(const ProKey &v);
ProStringList &values(const ProKey &v);
ProString first(const ProKey &v);
ProValueMap &variables();
};
inline bool QMakeMetaInfo::isEmpty(const ProKey &v)
{ return !vars.contains(v) || vars[v].isEmpty(); }
inline ProStringList &QMakeMetaInfo::values(const ProKey &v)
{ return vars[v]; }
inline ProString QMakeMetaInfo::first(const ProKey &v)
{
#if defined(Q_CC_SUN) && (__SUNPRO_CC == 0x500) || defined(Q_CC_HP)
// workaround for Sun WorkShop 5.0 bug fixed in Forte 6
if (isEmpty(v))
return ProString("");
else
return vars[v].first();
#else
return isEmpty(v) ? ProString("") : vars[v].first();
#endif
}
inline ProValueMap &QMakeMetaInfo::variables()
{ return vars; }
QT_END_NAMESPACE
#endif // META_H

625
qmake/option.cpp Normal file
View File

@ -0,0 +1,625 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "option.h"
#include "cachekeys.h"
#include <ioutils.h>
#include <qdir.h>
#include <qregularexpression.h>
#include <qhash.h>
#include <qdebug.h>
#include <stdlib.h>
#include <stdarg.h>
#include <qmakelibraryinfo.h>
#include <qtversion.h>
#include <private/qlibraryinfo_p.h>
QT_BEGIN_NAMESPACE
using namespace QMakeInternal;
EvalHandler Option::evalHandler;
QMakeGlobals *Option::globals;
ProFileCache *Option::proFileCache;
QMakeVfs *Option::vfs;
QMakeParser *Option::parser;
//convenience
QString Option::prf_ext;
QString Option::prl_ext;
QString Option::libtool_ext;
QString Option::pkgcfg_ext;
QString Option::ui_ext;
QStringList Option::h_ext;
QString Option::cpp_moc_ext;
QStringList Option::cpp_ext;
QStringList Option::c_ext;
QString Option::objc_ext;
QString Option::objcpp_ext;
QString Option::obj_ext;
QString Option::lex_ext;
QString Option::yacc_ext;
QString Option::pro_ext;
QString Option::dir_sep;
QString Option::h_moc_mod;
QString Option::yacc_mod;
QString Option::lex_mod;
QString Option::res_ext;
char Option::field_sep;
//mode
Option::QMAKE_MODE Option::qmake_mode = Option::QMAKE_GENERATE_NOTHING;
//all modes
int Option::warn_level = WarnLogic | WarnDeprecated;
int Option::debug_level = 0;
QFile Option::output;
QString Option::output_dir;
bool Option::recursive = false;
//QMAKE_*_PROPERTY stuff
QStringList Option::prop::properties;
//QMAKE_GENERATE_PROJECT stuff
bool Option::projfile::do_pwd = true;
QStringList Option::projfile::project_dirs;
//QMAKE_GENERATE_MAKEFILE stuff
int Option::mkfile::cachefile_depth = -1;
bool Option::mkfile::do_deps = true;
bool Option::mkfile::do_mocs = true;
bool Option::mkfile::do_dep_heuristics = true;
bool Option::mkfile::do_preprocess = false;
QStringList Option::mkfile::project_files;
static Option::QMAKE_MODE default_mode(QString progname)
{
int s = progname.lastIndexOf(QDir::separator());
if(s != -1)
progname = progname.right(progname.size() - (s + 1));
if(progname == "qmakegen")
return Option::QMAKE_GENERATE_PROJECT;
else if(progname == "qt-config")
return Option::QMAKE_QUERY_PROPERTY;
return Option::QMAKE_GENERATE_MAKEFILE;
}
static QString detectProjectFile(const QString &path, QString *singleProFileCandidate = nullptr)
{
QString ret;
QDir dir(path);
const QString candidate = dir.filePath(dir.dirName() + Option::pro_ext);
if (singleProFileCandidate)
*singleProFileCandidate = candidate;
if (QFile::exists(candidate)) {
ret = candidate;
} else { //last try..
QStringList profiles = dir.entryList(QStringList("*" + Option::pro_ext));
if(profiles.size() == 1)
ret = dir.filePath(profiles.at(0));
}
return ret;
}
bool usage(const char *a0)
{
fprintf(stdout, "Usage: %s [mode] [options] [files]\n"
"\n"
"QMake has two modes, one mode for generating project files based on\n"
"some heuristics, and the other for generating makefiles. Normally you\n"
"shouldn't need to specify a mode, as makefile generation is the default\n"
"mode for qmake, but you may use this to test qmake on an existing project\n"
"\n"
"Mode:\n"
" -project Put qmake into project file generation mode%s\n"
" In this mode qmake interprets [files] as files to\n"
" be added to the .pro file. By default, all files with\n"
" known source extensions are added.\n"
" Note: The created .pro file probably will \n"
" need to be edited. For example add the QT variable to \n"
" specify what modules are required.\n"
" -makefile Put qmake into makefile generation mode%s\n"
" In this mode qmake interprets files as project files to\n"
" be processed, if skipped qmake will try to find a project\n"
" file in your current working directory\n"
"\n"
"Warnings Options:\n"
" -Wnone Turn off all warnings; specific ones may be re-enabled by\n"
" later -W options\n"
" -Wall Turn on all warnings\n"
" -Wparser Turn on parser warnings\n"
" -Wlogic Turn on logic warnings (on by default)\n"
" -Wdeprecated Turn on deprecation warnings (on by default)\n"
"\n"
"Options:\n"
" * You can place any variable assignment in options and it will be *\n"
" * processed as if it was in [files]. These assignments will be *\n"
" * processed before [files] by default. *\n"
" -o file Write output to file\n"
" -d Increase debug level\n"
" -t templ Overrides TEMPLATE as templ\n"
" -tp prefix Overrides TEMPLATE so that prefix is prefixed into the value\n"
" -help This help\n"
" -v Version information\n"
" -early All subsequent variable assignments will be\n"
" parsed right before default_pre.prf\n"
" -before All subsequent variable assignments will be\n"
" parsed right before [files] (the default)\n"
" -after All subsequent variable assignments will be\n"
" parsed after [files]\n"
" -late All subsequent variable assignments will be\n"
" parsed right after default_post.prf\n"
" -norecursive Don't do a recursive search\n"
" -recursive Do a recursive search\n"
" -set <prop> <value> Set persistent property\n"
" -unset <prop> Unset persistent property\n"
" -query <prop> Query persistent property. Show all if <prop> is empty.\n"
" -qtconf file Use file instead of looking for qt" QT_STRINGIFY(QT_VERSION_MAJOR) ".conf, then qt.conf\n"
" -cache file Use file as cache [makefile mode only]\n"
" -spec spec Use spec as QMAKESPEC [makefile mode only]\n"
" -nocache Don't use a cache file [makefile mode only]\n"
" -nodepend Don't generate dependencies [makefile mode only]\n"
" -nomoc Don't generate moc targets [makefile mode only]\n"
" -nopwd Don't look for files in pwd [project mode only]\n"
,a0,
default_mode(a0) == Option::QMAKE_GENERATE_PROJECT ? " (default)" : "",
default_mode(a0) == Option::QMAKE_GENERATE_MAKEFILE ? " (default)" : ""
);
return false;
}
int
Option::parseCommandLine(QStringList &args, QMakeCmdLineParserState &state)
{
enum { ArgNone, ArgOutput } argState = ArgNone;
int x = 0;
while (x < args.size()) {
switch (argState) {
case ArgOutput:
Option::output.setFileName(args.at(x--));
args.erase(args.begin() + x, args.begin() + x + 2);
argState = ArgNone;
continue;
default:
QMakeGlobals::ArgumentReturn cmdRet = globals->addCommandLineArguments(state, args, &x);
if (cmdRet == QMakeGlobals::ArgumentMalformed) {
fprintf(stderr, "***Option %s requires a parameter\n", qPrintable(args.at(x - 1)));
return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
}
if (!globals->qtconf.isEmpty())
QLibraryInfoPrivate::qtconfManualPath = &globals->qtconf;
if (cmdRet == QMakeGlobals::ArgumentsOk)
break;
Q_ASSERT(cmdRet == QMakeGlobals::ArgumentUnknown);
QString arg = args.at(x);
if (arg.startsWith(QLatin1Char('-'))) {
if (arg == "-d") {
Option::debug_level++;
} else if (arg == "-v" || arg == "-version" || arg == "--version") {
fprintf(stdout,
"QMake version %s\n"
"Using Qt version %s in %s\n",
QMAKE_VERSION_STR, QT_VERSION_STR,
QMakeLibraryInfo::path(QLibraryInfo::LibrariesPath)
.toLatin1()
.constData());
#ifdef QMAKE_OPENSOURCE_VERSION
fprintf(stdout, "QMake is Open Source software from The Qt Company Ltd and/or its subsidiary(-ies).\n");
#endif
return Option::QMAKE_CMDLINE_BAIL;
} else if (arg == "-h" || arg == "-help" || arg == "--help") {
return Option::QMAKE_CMDLINE_SHOW_USAGE;
} else if (arg == "-Wall") {
Option::warn_level |= WarnAll;
} else if (arg == "-Wparser") {
Option::warn_level |= WarnParser;
} else if (arg == "-Wlogic") {
Option::warn_level |= WarnLogic;
} else if (arg == "-Wdeprecated") {
Option::warn_level |= WarnDeprecated;
} else if (arg == "-Wnone") {
Option::warn_level = WarnNone;
} else if (arg == "-r" || arg == "-recursive") {
Option::recursive = true;
args.removeAt(x);
continue;
} else if (arg == "-nr" || arg == "-norecursive") {
Option::recursive = false;
args.removeAt(x);
continue;
} else if (arg == "-o" || arg == "-output") {
argState = ArgOutput;
} else {
if (Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
if (arg == "-nodepend" || arg == "-nodepends") {
Option::mkfile::do_deps = false;
} else if (arg == "-nomoc") {
Option::mkfile::do_mocs = false;
} else if (arg == "-nodependheuristics") {
Option::mkfile::do_dep_heuristics = false;
} else if (arg == "-E") {
Option::mkfile::do_preprocess = true;
} else {
fprintf(stderr, "***Unknown option %s\n", arg.toLatin1().constData());
return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
}
} else if (Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
if (arg == "-nopwd") {
Option::projfile::do_pwd = false;
} else {
fprintf(stderr, "***Unknown option %s\n", arg.toLatin1().constData());
return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
}
}
}
} else {
bool handled = true;
if(Option::qmake_mode == Option::QMAKE_QUERY_PROPERTY ||
Option::qmake_mode == Option::QMAKE_SET_PROPERTY ||
Option::qmake_mode == Option::QMAKE_UNSET_PROPERTY) {
Option::prop::properties.append(arg);
} else {
QFileInfo fi(arg);
if(!fi.makeAbsolute()) //strange
arg = fi.filePath();
if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
if(fi.isDir()) {
QString singleProFileCandidate;
QString proj = detectProjectFile(arg, &singleProFileCandidate);
if (proj.isNull()) {
fprintf(stderr, "***Cannot detect .pro file in directory '%s'.\n\n"
"QMake expects the file '%s' "
"or exactly one .pro file in the given directory.\n",
qUtf8Printable(arg),
qUtf8Printable(singleProFileCandidate));
return Option::QMAKE_CMDLINE_ERROR;
}
Option::mkfile::project_files.append(proj);
} else {
Option::mkfile::project_files.append(arg);
}
} else if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
Option::projfile::project_dirs.append(arg);
} else {
handled = false;
}
}
if(!handled) {
return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
}
args.removeAt(x);
continue;
}
}
x++;
}
if (argState != ArgNone) {
fprintf(stderr, "***Option %s requires a parameter\n", qPrintable(args.at(x - 1)));
return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
}
return Option::QMAKE_CMDLINE_SUCCESS;
}
int
Option::init(int argc, char **argv)
{
Option::prf_ext = ".prf";
Option::pro_ext = ".pro";
Option::field_sep = ' ';
if(argc && argv) {
QString argv0 = argv[0];
#ifdef Q_OS_WIN
if (!argv0.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive))
argv0 += QLatin1String(".exe");
#endif
if(Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING)
Option::qmake_mode = default_mode(argv0);
globals->qmake_abslocation = IoUtils::binaryAbsLocation(argv0);
if (Q_UNLIKELY(globals->qmake_abslocation.isNull())) {
// This is rather unlikely to ever happen on a modern system ...
globals->qmake_abslocation =
QMakeLibraryInfo::rawLocation(QMakeLibraryInfo::HostBinariesPath,
QMakeLibraryInfo::EffectivePaths)
+ "/qmake"
#ifdef Q_OS_WIN
".exe"
#endif
;
}
} else {
Option::qmake_mode = Option::QMAKE_GENERATE_MAKEFILE;
}
QMakeCmdLineParserState cmdstate(QDir::currentPath());
const QByteArray envflags = qgetenv("QMAKEFLAGS");
if (!envflags.isNull()) {
QStringList args;
QByteArray buf = "";
char quote = 0;
bool hasWord = false;
for (int i = 0; i < envflags.size(); ++i) {
char c = envflags.at(i);
if (!quote && (c == '\'' || c == '"')) {
quote = c;
} else if (c == quote) {
quote = 0;
} else if (!quote && c == ' ') {
if (hasWord) {
args << QString::fromLocal8Bit(buf);
hasWord = false;
buf = "";
}
} else {
buf += c;
hasWord = true;
}
}
if (hasWord)
args << QString::fromLocal8Bit(buf);
parseCommandLine(args, cmdstate);
cmdstate.flush();
}
if(argc && argv) {
QStringList args;
args.reserve(argc - 1);
for (int i = 1; i < argc; i++)
args << QString::fromLocal8Bit(argv[i]);
qsizetype idx = 0;
while (idx < args.size()) {
QString opt = args.at(idx);
if (opt == "-project") {
Option::recursive = true;
Option::qmake_mode = Option::QMAKE_GENERATE_PROJECT;
} else if (opt == "-prl") {
Option::mkfile::do_deps = false;
Option::mkfile::do_mocs = false;
Option::qmake_mode = Option::QMAKE_GENERATE_PRL;
} else if (opt == "-set") {
Option::qmake_mode = Option::QMAKE_SET_PROPERTY;
} else if (opt == "-unset") {
Option::qmake_mode = Option::QMAKE_UNSET_PROPERTY;
} else if (opt == "-query") {
Option::qmake_mode = Option::QMAKE_QUERY_PROPERTY;
} else if (opt == "-makefile") {
Option::qmake_mode = Option::QMAKE_GENERATE_MAKEFILE;
} else if (opt == "-qtconf") {
// Skip "-qtconf <file>" and proceed.
++idx;
if (idx + 1 < args.size())
++idx;
continue;
} else {
break;
}
args.takeAt(idx);
break;
}
int ret = parseCommandLine(args, cmdstate);
if(ret != Option::QMAKE_CMDLINE_SUCCESS) {
if ((ret & Option::QMAKE_CMDLINE_SHOW_USAGE) != 0)
usage(argv[0]);
return ret;
//return ret == QMAKE_CMDLINE_SHOW_USAGE ? usage(argv[0]) : false;
}
globals->qmake_args = args;
globals->qmake_extra_args = cmdstate.extraargs;
}
globals->commitCommandLineArguments(cmdstate);
globals->debugLevel = Option::debug_level;
//last chance for defaults
if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
globals->useEnvironment();
//try REALLY hard to do it for them, lazy..
if(Option::mkfile::project_files.isEmpty()) {
QString proj = detectProjectFile(qmake_getpwd());
if(!proj.isNull())
Option::mkfile::project_files.append(proj);
if(Option::mkfile::project_files.isEmpty()) {
usage(argv[0]);
return Option::QMAKE_CMDLINE_ERROR;
}
}
}
return QMAKE_CMDLINE_SUCCESS;
}
void Option::prepareProject(const QString &pfile)
{
// Canonicalize only the directory, otherwise things will go haywire
// if the file itself is a symbolic link.
const QString srcpath = QFileInfo(QFileInfo(pfile).absolutePath()).canonicalFilePath();
globals->setDirectories(srcpath, output_dir);
}
bool Option::postProcessProject(QMakeProject *project)
{
Option::cpp_ext = project->values("QMAKE_EXT_CPP").toQStringList();
Option::h_ext = project->values("QMAKE_EXT_H").toQStringList();
Option::c_ext = project->values("QMAKE_EXT_C").toQStringList();
Option::objc_ext = project->first("QMAKE_EXT_OBJC").toQString();
Option::objcpp_ext = project->first("QMAKE_EXT_OBJCXX").toQString();
Option::res_ext = project->first("QMAKE_EXT_RES").toQString();
Option::pkgcfg_ext = project->first("QMAKE_EXT_PKGCONFIG").toQString();
Option::libtool_ext = project->first("QMAKE_EXT_LIBTOOL").toQString();
Option::prl_ext = project->first("QMAKE_EXT_PRL").toQString();
Option::ui_ext = project->first("QMAKE_EXT_UI").toQString();
Option::cpp_moc_ext = project->first("QMAKE_EXT_CPP_MOC").toQString();
Option::lex_ext = project->first("QMAKE_EXT_LEX").toQString();
Option::yacc_ext = project->first("QMAKE_EXT_YACC").toQString();
Option::obj_ext = project->first("QMAKE_EXT_OBJ").toQString();
Option::h_moc_mod = project->first("QMAKE_H_MOD_MOC").toQString();
Option::lex_mod = project->first("QMAKE_MOD_LEX").toQString();
Option::yacc_mod = project->first("QMAKE_MOD_YACC").toQString();
Option::dir_sep = project->dirSep().toQString();
if (!project->buildRoot().isEmpty() && Option::output_dir.startsWith(project->buildRoot()))
Option::mkfile::cachefile_depth =
Option::output_dir.mid(project->buildRoot().size()).count('/');
return true;
}
QString
Option::fixString(QString string, uchar flags)
{
//const QString orig_string = string;
static QHash<FixStringCacheKey, QString> *cache = nullptr;
if(!cache) {
cache = new QHash<FixStringCacheKey, QString>;
qmakeAddCacheClear(qmakeDeleteCacheClear<QHash<FixStringCacheKey, QString> >, (void**)&cache);
}
FixStringCacheKey cacheKey(string, flags);
QHash<FixStringCacheKey, QString>::const_iterator it = cache->constFind(cacheKey);
if (it != cache->constEnd()) {
//qDebug() << "Fix (cached) " << orig_string << "->" << it.value();
return it.value();
}
//fix the environment variables
if(flags & Option::FixEnvVars) {
static QRegularExpression reg_var("\\$\\(.*\\)", QRegularExpression::InvertedGreedinessOption);
QRegularExpressionMatch match;
while ((match = reg_var.match(string)).hasMatch()) {
int start = match.capturedStart();
int len = match.capturedLength();
string.replace(start, len,
QString::fromLocal8Bit(qgetenv(string.mid(start + 2, len - 3).toLatin1().constData()).constData()));
}
}
//canonicalize it (and treat as a path)
if(flags & Option::FixPathCanonicalize) {
#if 0
string = QFileInfo(string).canonicalFilePath();
#endif
string = QDir::cleanPath(string);
}
// either none or only one active flag
Q_ASSERT(((flags & Option::FixPathToLocalSeparators) != 0) +
((flags & Option::FixPathToTargetSeparators) != 0) +
((flags & Option::FixPathToNormalSeparators) != 0) <= 1);
//fix separators
if (flags & Option::FixPathToNormalSeparators) {
string.replace('\\', '/');
} else if (flags & Option::FixPathToLocalSeparators) {
#if defined(Q_OS_WIN32)
string.replace('/', '\\');
#else
string.replace('\\', '/');
#endif
} else if(flags & Option::FixPathToTargetSeparators) {
string.replace('/', Option::dir_sep).replace('\\', Option::dir_sep);
}
if ((string.startsWith("\"") && string.endsWith("\"")) ||
(string.startsWith("\'") && string.endsWith("\'")))
string = string.mid(1, string.size()-2);
//cache
//qDebug() << "Fix" << orig_string << "->" << string;
cache->insert(cacheKey, string);
return string;
}
void debug_msg_internal(int level, const char *fmt, ...)
{
if(Option::debug_level < level)
return;
fprintf(stderr, "DEBUG %d: ", level);
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
fprintf(stderr, "\n");
}
void warn_msg(QMakeWarn type, const char *fmt, ...)
{
if(!(Option::warn_level & type))
return;
fprintf(stderr, "WARNING: ");
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
fprintf(stderr, "\n");
}
void EvalHandler::message(int type, const QString &msg, const QString &fileName, int lineNo)
{
QString pfx;
if ((type & QMakeHandler::CategoryMask) == QMakeHandler::WarningMessage) {
int code = (type & QMakeHandler::CodeMask);
if ((code == QMakeHandler::WarnLanguage && !(Option::warn_level & WarnParser))
|| (code == QMakeHandler::WarnDeprecated && !(Option::warn_level & WarnDeprecated)))
return;
pfx = QString::fromLatin1("WARNING: ");
}
if (lineNo > 0)
fprintf(stderr, "%s%s:%d: %s\n", qPrintable(pfx), qPrintable(fileName), lineNo, qPrintable(msg));
else if (lineNo)
fprintf(stderr, "%s%s: %s\n", qPrintable(pfx), qPrintable(fileName), qPrintable(msg));
else
fprintf(stderr, "%s%s\n", qPrintable(pfx), qPrintable(msg));
}
void EvalHandler::fileMessage(int type, const QString &msg)
{
Q_UNUSED(type);
fprintf(stderr, "%s\n", qPrintable(msg));
}
void EvalHandler::aboutToEval(ProFile *, ProFile *, EvalFileType)
{
}
void EvalHandler::doneWithEval(ProFile *)
{
}
class QMakeCacheClearItem {
private:
qmakeCacheClearFunc func;
void **data;
public:
QMakeCacheClearItem(qmakeCacheClearFunc f, void **d) : func(f), data(d) { }
~QMakeCacheClearItem() {
(*func)(*data);
*data = nullptr;
}
};
Q_CONSTINIT static QList<QMakeCacheClearItem*> cache_items;
void
qmakeClearCaches()
{
qDeleteAll(cache_items);
cache_items.clear();
}
void
qmakeAddCacheClear(qmakeCacheClearFunc func, void **data)
{
cache_items.append(new QMakeCacheClearItem(func, data));
}
QT_END_NAMESPACE

178
qmake/option.h Normal file
View File

@ -0,0 +1,178 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef OPTION_H
#define OPTION_H
#include <qmakeglobals.h>
#include <qmakevfs.h>
#include <qmakeparser.h>
#include <qmakeevaluator.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qfile.h>
QT_BEGIN_NAMESPACE
QString qmake_getpwd();
bool qmake_setpwd(const QString &p);
#define debug_msg if(Option::debug_level) debug_msg_internal
void debug_msg_internal(int level, const char *fmt, ...); //don't call directly, use debug_msg
enum QMakeWarn {
WarnNone = 0x00,
WarnParser = 0x01,
WarnLogic = 0x02,
WarnDeprecated = 0x04,
WarnAll = 0xFF
};
void warn_msg(QMakeWarn t, const char *fmt, ...);
class QMakeProject;
class EvalHandler : public QMakeHandler {
public:
void message(int type, const QString &msg, const QString &fileName, int lineNo) override;
void fileMessage(int type, const QString &msg) override;
void aboutToEval(ProFile *, ProFile *, EvalFileType) override;
void doneWithEval(ProFile *) override;
};
struct Option
{
static EvalHandler evalHandler;
static QMakeGlobals *globals;
static ProFileCache *proFileCache;
static QMakeVfs *vfs;
static QMakeParser *parser;
//simply global convenience
static QString libtool_ext;
static QString pkgcfg_ext;
static QString prf_ext;
static QString prl_ext;
static QString ui_ext;
static QStringList h_ext;
static QStringList cpp_ext;
static QStringList c_ext;
static QString objc_ext;
static QString objcpp_ext;
static QString cpp_moc_ext;
static QString obj_ext;
static QString lex_ext;
static QString yacc_ext;
static QString h_moc_mod;
static QString lex_mod;
static QString yacc_mod;
static QString dir_sep;
static QString pro_ext;
static QString res_ext;
static char field_sep;
enum CmdLineFlags {
QMAKE_CMDLINE_SUCCESS = 0x00,
QMAKE_CMDLINE_SHOW_USAGE = 0x01,
QMAKE_CMDLINE_BAIL = 0x02,
QMAKE_CMDLINE_ERROR = 0x04
};
//both of these must be called..
static int init(int argc = 0, char **argv = nullptr); //parse cmdline
static void prepareProject(const QString &pfile);
static bool postProcessProject(QMakeProject *);
enum StringFixFlags {
FixNone = 0x00,
FixEnvVars = 0x01,
FixPathCanonicalize = 0x02,
FixPathToLocalSeparators = 0x04,
FixPathToTargetSeparators = 0x08,
FixPathToNormalSeparators = 0x10
};
static QString fixString(QString string, uchar flags);
//and convenience functions
inline static QString fixPathToLocalOS(const QString &in, bool fix_env=true, bool canonical=true)
{
uchar flags = FixPathToLocalSeparators;
if(fix_env)
flags |= FixEnvVars;
if(canonical)
flags |= FixPathCanonicalize;
return fixString(in, flags);
}
inline static QString fixPathToTargetOS(const QString &in, bool fix_env=true, bool canonical=true)
{
uchar flags = FixPathToTargetSeparators;
if(fix_env)
flags |= FixEnvVars;
if(canonical)
flags |= FixPathCanonicalize;
return fixString(in, flags);
}
inline static QString normalizePath(const QString &in, bool fix_env=true, bool canonical=true)
{
uchar flags = FixPathToNormalSeparators;
if (fix_env)
flags |= FixEnvVars;
if (canonical)
flags |= FixPathCanonicalize;
return fixString(in, flags);
}
inline static bool hasFileExtension(const QString &str, const QStringList &extensions)
{
for (const QString &ext : extensions)
if (str.endsWith(ext))
return true;
return false;
}
//global qmake mode, can only be in one mode per invocation!
enum QMAKE_MODE { QMAKE_GENERATE_NOTHING,
QMAKE_GENERATE_PROJECT, QMAKE_GENERATE_MAKEFILE, QMAKE_GENERATE_PRL,
QMAKE_SET_PROPERTY, QMAKE_UNSET_PROPERTY, QMAKE_QUERY_PROPERTY };
static QMAKE_MODE qmake_mode;
//all modes
static QFile output;
static QString output_dir;
static int debug_level;
static int warn_level;
static bool recursive;
//QMAKE_*_PROPERTY options
struct prop {
static QStringList properties;
};
//QMAKE_GENERATE_PROJECT options
struct projfile {
static bool do_pwd;
static QStringList project_dirs;
};
//QMAKE_GENERATE_MAKEFILE options
struct mkfile {
static bool do_deps;
static bool do_mocs;
static bool do_dep_heuristics;
static bool do_preprocess;
static int cachefile_depth;
static QStringList project_files;
};
private:
static int parseCommandLine(QStringList &args, QMakeCmdLineParserState &state);
};
inline QString fixEnvVariables(const QString &x) { return Option::fixString(x, Option::FixEnvVars); }
inline QStringList splitPathList(const QString &paths) { return paths.isEmpty() ? QStringList() : paths.split(Option::globals->dirlist_sep); }
QT_END_NAMESPACE
#endif // OPTION_H

145
qmake/project.cpp Normal file
View File

@ -0,0 +1,145 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "project.h"
#include "cachekeys.h"
#include "option.h"
#include <qmakeevaluator_p.h>
#include <qdir.h>
#include <stdio.h>
using namespace QMakeInternal;
QT_BEGIN_NAMESPACE
QMakeProject::QMakeProject()
: QMakeEvaluator(Option::globals, Option::parser, Option::vfs, &Option::evalHandler)
{
}
QMakeProject::QMakeProject(QMakeProject *p)
: QMakeEvaluator(Option::globals, Option::parser, Option::vfs, &Option::evalHandler)
{
initFrom(p);
}
bool QMakeProject::boolRet(VisitReturn vr)
{
if (vr == ReturnError) {
qmakeClearCaches();
exit(3);
}
Q_ASSERT(vr == ReturnTrue || vr == ReturnFalse);
return vr != ReturnFalse;
}
bool QMakeProject::read(const QString &project, LoadFlags what)
{
m_projectFile = project;
setOutputDir(Option::output_dir);
QString absproj = (project == QLatin1String("-"))
? QLatin1String("(stdin)")
: QDir::cleanPath(QDir(qmake_getpwd()).absoluteFilePath(project));
m_projectDir = QFileInfo(absproj).path();
return boolRet(evaluateFile(absproj, QMakeHandler::EvalProjectFile, what));
}
static ProStringList prepareBuiltinArgs(const QList<ProStringList> &args)
{
ProStringList ret;
ret.reserve(args.size());
for (const ProStringList &arg : args)
ret << arg.join(' ');
return ret;
}
bool QMakeProject::test(const ProKey &func, const QList<ProStringList> &args)
{
m_current.clear();
auto adef = statics.functions.constFind(func);
if (adef != statics.functions.constEnd())
return boolRet(evaluateBuiltinConditional(*adef, func, prepareBuiltinArgs(args)));
QHash<ProKey, ProFunctionDef>::ConstIterator it =
m_functionDefs.testFunctions.constFind(func);
if (it != m_functionDefs.testFunctions.constEnd())
return boolRet(evaluateBoolFunction(*it, args, func));
evalError(QStringLiteral("'%1' is not a recognized test function.")
.arg(func.toQStringView()));
return false;
}
QStringList QMakeProject::expand(const ProKey &func, const QList<ProStringList> &args)
{
m_current.clear();
auto adef = statics.expands.constFind(func);
if (adef != statics.expands.constEnd()) {
ProStringList ret;
if (evaluateBuiltinExpand(*adef, func, prepareBuiltinArgs(args), ret) == ReturnError)
exit(3);
return ret.toQStringList();
}
QHash<ProKey, ProFunctionDef>::ConstIterator it =
m_functionDefs.replaceFunctions.constFind(func);
if (it != m_functionDefs.replaceFunctions.constEnd()) {
ProStringList ret;
if (evaluateFunction(*it, args, &ret) == QMakeProject::ReturnError)
exit(3);
return ret.toQStringList();
}
evalError(QStringLiteral("'%1' is not a recognized replace function.")
.arg(func.toQStringView()));
return QStringList();
}
ProString QMakeProject::expand(const QString &expr, const QString &where, int line)
{
ProString ret;
ProFile *pro = m_parser->parsedProBlock(QStringView(expr), 0, where, line,
QMakeParser::ValueGrammar);
if (pro->isOk()) {
m_current.pro = pro;
m_current.line = 0;
const ushort *tokPtr = pro->tokPtr();
ProStringList result;
if (expandVariableReferences(tokPtr, 1, &result, true) == ReturnError)
exit(3);
if (!result.isEmpty())
ret = result.at(0);
}
pro->deref();
return ret;
}
bool QMakeProject::isEmpty(const ProKey &v) const
{
ProValueMap::ConstIterator it = m_valuemapStack.front().constFind(v);
return it == m_valuemapStack.front().constEnd() || it->isEmpty();
}
void QMakeProject::dump() const
{
QStringList out;
for (ProValueMap::ConstIterator it = m_valuemapStack.front().begin();
it != m_valuemapStack.front().end(); ++it) {
if (!it.key().startsWith('.')) {
QString str = it.key() + " =";
for (const ProString &v : it.value())
str += ' ' + formatValue(v);
out << str;
}
}
out.sort();
for (const QString &v : std::as_const(out))
puts(qPrintable(v));
}
QT_END_NAMESPACE

85
qmake/project.h Normal file
View File

@ -0,0 +1,85 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PROJECT_H
#define PROJECT_H
#include <qmakeevaluator.h>
QT_BEGIN_NAMESPACE
class QMakeProject : private QMakeEvaluator
{
QString m_projectFile;
QString m_projectDir;
public:
QMakeProject();
QMakeProject(QMakeProject *p);
bool read(const QString &project, LoadFlags what = LoadAll);
QString projectFile() const { return m_projectFile; }
QString projectDir() const { return m_projectDir; }
QString sourceRoot() const { return m_sourceRoot.isEmpty() ? m_buildRoot : m_sourceRoot; }
QString buildRoot() const { return m_buildRoot; }
QString confFile() const { return m_conffile; }
QString cacheFile() const { return m_cachefile; }
QString specDir() const { return m_qmakespec; }
ProString expand(const QString &v, const QString &file, int line);
QStringList expand(const ProKey &func, const QList<ProStringList> &args);
bool test(const QString &v, const QString &file, int line)
{ m_current.clear(); return evaluateConditional(QStringView(v), file, line) == ReturnTrue; }
bool test(const ProKey &func, const QList<ProStringList> &args);
bool isSet(const ProKey &v) const { return m_valuemapStack.front().contains(v); }
bool isEmpty(const ProKey &v) const;
ProStringList &values(const ProKey &v) { return valuesRef(v); }
int intValue(const ProKey &v, int defaultValue = 0) const;
const ProValueMap &variables() const { return m_valuemapStack.front(); }
ProValueMap &variables() { return m_valuemapStack.front(); }
bool isActiveConfig(const QString &config, bool regex = false)
{ return QMakeEvaluator::isActiveConfig(QStringView(config), regex); }
void dump() const;
using QMakeEvaluator::LoadFlags;
using QMakeEvaluator::VisitReturn;
using QMakeEvaluator::setExtraVars;
using QMakeEvaluator::setExtraConfigs;
using QMakeEvaluator::loadSpec;
using QMakeEvaluator::evaluateFeatureFile;
using QMakeEvaluator::evaluateConfigFeatures;
using QMakeEvaluator::evaluateExpression;
using QMakeEvaluator::propertyValue;
using QMakeEvaluator::values;
using QMakeEvaluator::first;
using QMakeEvaluator::isHostBuild;
using QMakeEvaluator::dirSep;
private:
static bool boolRet(VisitReturn vr);
};
/*!
* For variables that are supposed to contain a single int,
* this method returns the numeric value.
* Only the first value of the variable is taken into account.
* The string representation is assumed to look like a C int literal.
*/
inline int QMakeProject::intValue(const ProKey &v, int defaultValue) const
{
const ProString &str = first(v);
if (!str.isEmpty()) {
bool ok;
int i = str.toInt(&ok, 0);
if (ok)
return i;
}
return defaultValue;
}
QT_END_NAMESPACE
#endif // PROJECT_H

204
qmake/property.cpp Normal file
View File

@ -0,0 +1,204 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "property.h"
#include <qdir.h>
#include <qsettings.h>
#include <qtversion.h>
#include <qmakelibraryinfo.h>
#include <qstringlist.h>
#include <stdio.h>
namespace {
constexpr int PropSuccessRetCode = 0;
constexpr int PropFailRetCode = 101;
}
QT_BEGIN_NAMESPACE
static const struct {
const char *name;
int loc;
bool raw;
bool singular;
} propList[] = {
{ "QT_SYSROOT", QMakeLibraryInfo::SysrootPath, true, true },
{ "QT_INSTALL_PREFIX", QLibraryInfo::PrefixPath, false, false },
{ "QT_INSTALL_ARCHDATA", QLibraryInfo::ArchDataPath, false, false },
{ "QT_INSTALL_DATA", QLibraryInfo::DataPath, false, false },
{ "QT_INSTALL_DOCS", QLibraryInfo::DocumentationPath, false, false },
{ "QT_INSTALL_HEADERS", QLibraryInfo::HeadersPath, false, false },
{ "QT_INSTALL_LIBS", QLibraryInfo::LibrariesPath, false, false },
{ "QT_INSTALL_LIBEXECS", QLibraryInfo::LibraryExecutablesPath, false, false },
{ "QT_INSTALL_BINS", QLibraryInfo::BinariesPath, false, false },
{ "QT_INSTALL_TESTS", QLibraryInfo::TestsPath, false, false },
{ "QT_INSTALL_PLUGINS", QLibraryInfo::PluginsPath, false, false },
{ "QT_INSTALL_QML", QLibraryInfo::QmlImportsPath, false, false },
{ "QT_INSTALL_TRANSLATIONS", QLibraryInfo::TranslationsPath, false, false },
{ "QT_INSTALL_CONFIGURATION", QLibraryInfo::SettingsPath, false, false },
{ "QT_INSTALL_EXAMPLES", QLibraryInfo::ExamplesPath, false, false },
{ "QT_INSTALL_DEMOS", QLibraryInfo::ExamplesPath, false, false }, // Just backwards compat
{ "QT_HOST_PREFIX", QMakeLibraryInfo::HostPrefixPath, true, false },
{ "QT_HOST_DATA", QMakeLibraryInfo::HostDataPath, true, false },
{ "QT_HOST_BINS", QMakeLibraryInfo::HostBinariesPath, true, false },
{ "QT_HOST_LIBEXECS", QMakeLibraryInfo::HostLibraryExecutablesPath, true, false },
{ "QT_HOST_LIBS", QMakeLibraryInfo::HostLibrariesPath, true, false },
{ "QMAKE_SPEC", QMakeLibraryInfo::HostSpecPath, true, true },
{ "QMAKE_XSPEC", QMakeLibraryInfo::TargetSpecPath, true, true },
};
QMakeProperty::QMakeProperty() : settings(nullptr)
{
reload();
}
void QMakeProperty::reload()
{
QMakeLibraryInfo::reload();
for (unsigned i = 0; i < sizeof(propList)/sizeof(propList[0]); i++) {
QString name = QString::fromLatin1(propList[i].name);
if (!propList[i].singular) {
m_values[ProKey(name + "/src")] = QMakeLibraryInfo::rawLocation(
propList[i].loc, QMakeLibraryInfo::EffectiveSourcePaths);
m_values[ProKey(name + "/get")] = QMakeLibraryInfo::rawLocation(
propList[i].loc, QMakeLibraryInfo::EffectivePaths);
}
QString val = QMakeLibraryInfo::rawLocation(propList[i].loc, QMakeLibraryInfo::FinalPaths);
if (!propList[i].raw) {
m_values[ProKey(name + "/dev")] =
QMakeLibraryInfo::rawLocation(propList[i].loc, QMakeLibraryInfo::DevicePaths);
m_values[ProKey(name)] = QMakeLibraryInfo::path(propList[i].loc);
name += "/raw";
}
m_values[ProKey(name)] = val;
}
#ifdef QMAKE_VERSION_STR
m_values["QMAKE_VERSION"] = ProString(QMAKE_VERSION_STR);
#endif
#ifdef QT_VERSION_STR
m_values["QT_VERSION"] = ProString(QT_VERSION_STR);
#endif
}
QMakeProperty::~QMakeProperty()
{
delete settings;
settings = nullptr;
}
void QMakeProperty::initSettings()
{
if (!settings) {
settings = new QSettings(QSettings::UserScope, "QtProject", "QMake");
settings->setFallbacksEnabled(false);
}
}
ProString
QMakeProperty::value(const ProKey &vk)
{
ProString val = m_values.value(vk);
if (!val.isNull())
return val;
initSettings();
return settings->value(vk.toQString()).toString();
}
bool
QMakeProperty::hasValue(const ProKey &v)
{
return !value(v).isNull();
}
void
QMakeProperty::setValue(QString var, const QString &val)
{
initSettings();
settings->setValue(var, val);
}
void
QMakeProperty::remove(const QString &var)
{
initSettings();
settings->remove(var);
}
int QMakeProperty::queryProperty(const QStringList &optionProperties,
const PropertyPrinter &printer)
{
QList<QPair<QString, QString>> output;
int ret = PropSuccessRetCode;
if (optionProperties.isEmpty()) {
initSettings();
const auto keys = settings->childKeys();
for (const QString &key : keys) {
QString val = settings->value(key).toString();
output.append({ key, val });
}
QStringList specialProps;
for (unsigned i = 0; i < sizeof(propList) / sizeof(propList[0]); i++)
specialProps.append(QString::fromLatin1(propList[i].name));
#ifdef QMAKE_VERSION_STR
specialProps.append("QMAKE_VERSION");
#endif
#ifdef QT_VERSION_STR
specialProps.append("QT_VERSION");
#endif
for (const QString &prop : std::as_const(specialProps)) {
ProString val = value(ProKey(prop));
ProString pval = value(ProKey(prop + "/raw"));
ProString gval = value(ProKey(prop + "/get"));
ProString sval = value(ProKey(prop + "/src"));
ProString dval = value(ProKey(prop + "/dev"));
output.append({ prop, val.toQString() });
if (!pval.isEmpty() && pval != val)
output.append({ prop + "/raw", pval.toQString() });
if (!gval.isEmpty() && gval != (pval.isEmpty() ? val : pval))
output.append({ prop + "/get", gval.toQString() });
if (!sval.isEmpty() && sval != gval)
output.append({ prop + "/src", sval.toQString() });
if (!dval.isEmpty() && dval != pval)
output.append({ prop + "/dev", dval.toQString() });
}
} else {
for (const auto &prop : optionProperties) {
const ProKey pkey(prop);
if (!hasValue(pkey)) {
ret = PropFailRetCode;
output.append({ prop, QString("**Unknown**") });
} else {
output.append({ prop, value(pkey).toQString() });
}
}
}
printer(output);
return ret;
}
int QMakeProperty::setProperty(const QStringList &optionProperties)
{
for (auto it = optionProperties.cbegin(); it != optionProperties.cend(); ++it) {
QString var = (*it);
++it;
if (it == optionProperties.cend()) {
return PropFailRetCode;
}
if (!var.startsWith("."))
setValue(var, (*it));
}
return PropSuccessRetCode;
}
void QMakeProperty::unsetProperty(const QStringList &optionProperties)
{
for (auto it = optionProperties.cbegin(); it != optionProperties.cend(); ++it) {
QString var = (*it);
if (!var.startsWith("."))
remove(var);
}
}
QT_END_NAMESPACE

45
qmake/property.h Normal file
View File

@ -0,0 +1,45 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef PROPERTY_H
#define PROPERTY_H
#include "library/proitems.h"
#include "propertyprinter.h"
#include <qglobal.h>
#include <qstring.h>
#include <qhash.h>
QT_BEGIN_NAMESPACE
class QSettings;
class QMakeProperty final
{
QSettings *settings;
void initSettings();
QHash<ProKey, ProString> m_values;
public:
QMakeProperty();
~QMakeProperty();
void reload();
bool hasValue(const ProKey &);
ProString value(const ProKey &);
void setValue(QString, const QString &);
void remove(const QString &);
int queryProperty(const QStringList &optionProperties = QStringList(),
const PropertyPrinter &printer = qmakePropertyPrinter);
int setProperty(const QStringList &optionProperties);
void unsetProperty(const QStringList &optionProperties);
};
QT_END_NAMESPACE
#endif // PROPERTY_H

32
qmake/propertyprinter.cpp Normal file
View File

@ -0,0 +1,32 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "propertyprinter.h"
#include <iostream>
QT_BEGIN_NAMESPACE
void qmakePropertyPrinter(const QList<QPair<QString, QString>> &values)
{
// Assume single property request
if (values.size() == 1) {
std::cout << qPrintable(values.at(0).second) << std::endl;
return;
}
for (const auto &val : values) {
std::cout << qPrintable(val.first) << ":" << qPrintable(val.second) << std::endl;
}
}
void jsonPropertyPrinter(const QList<QPair<QString, QString>> &values)
{
std::cout << "{\n";
for (const auto &val : values) {
std::cout << "\"" << qPrintable(val.first) << "\":\"" << qPrintable(val.second) << "\",\n";
}
std::cout << "}\n";
}
QT_END_NAMESPACE

22
qmake/propertyprinter.h Normal file
View File

@ -0,0 +1,22 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef PROPERTYPRINTER_H
#define PROPERTYPRINTER_H
#include <qglobal.h>
#include <qlist.h>
#include <qpair.h>
#include <qstring.h>
#include <functional>
QT_BEGIN_NAMESPACE
using PropertyPrinter = std::function<void(const QList<QPair<QString, QString>> &)>;
void qmakePropertyPrinter(const QList<QPair<QString, QString>> &values);
void jsonPropertyPrinter(const QList<QPair<QString, QString>> &values);
QT_END_NAMESPACE
#endif // PROPERTYPRINTER_H

37
qmake/qmake_pch.h Normal file
View File

@ -0,0 +1,37 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMAKE_PCH_H
#define QMAKE_PCH_H
// for rand_s, _CRT_RAND_S must be #defined before #including stdlib.h.
// put it at the beginning so some indirect inclusion doesn't break it
#ifndef _CRT_RAND_S
#define _CRT_RAND_S
#endif
#include <qglobal.h>
#ifdef Q_OS_WIN
# define _POSIX_
# include <limits.h>
# undef _POSIX_
#endif
#include <stdio.h>
//#include "makefile.h"
//#include "meta.h"
#include <qfile.h>
//#include "winmakefile.h"
//#include <qtextstream.h>
//#include "project.h"
#include <qstring.h>
#include <qstringlist.h>
#include <qhash.h>
#include <time.h>
#include <stdlib.h>
//#include <qdir.h>
//#include "option.h"
QT_BEGIN_NAMESPACE
QT_END_NAMESPACE
#endif

273
qmake/qmakelibraryinfo.cpp Normal file
View File

@ -0,0 +1,273 @@
// Copyright (C) 2021 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmakelibraryinfo.h"
#include <qdir.h>
#include <qfile.h>
#include <qglobalstatic.h>
#include <qsettings.h>
#include <qscopedpointer.h>
#include <qstringlist.h>
#include <private/qlibraryinfo_p.h>
#include <utility>
QT_BEGIN_NAMESPACE
struct QMakeLibrarySettings
{
QMakeLibrarySettings() { load(); }
void load();
bool haveDevicePaths;
bool haveEffectiveSourcePaths;
bool haveEffectivePaths;
bool havePaths;
};
Q_GLOBAL_STATIC(QMakeLibrarySettings, qmake_library_settings)
void QMakeLibrarySettings::load()
{
QSettings *settings = QLibraryInfoPrivate::configuration();
if (settings) {
QStringList children = settings->childGroups();
haveDevicePaths = children.contains(QLatin1String("DevicePaths"));
haveEffectiveSourcePaths = children.contains(QLatin1String("EffectiveSourcePaths"));
haveEffectivePaths =
haveEffectiveSourcePaths || children.contains(QLatin1String("EffectivePaths"));
// Backwards compat: an existing but empty file is claimed to contain the Paths section.
havePaths = (!haveDevicePaths && !haveEffectivePaths
&& !children.contains(QLatin1String("Platforms")))
|| children.contains(QLatin1String("Paths"));
} else {
haveDevicePaths = false;
haveEffectiveSourcePaths = false;
haveEffectivePaths = false;
havePaths = false;
}
}
void QMakeLibraryInfo::reload()
{
QLibraryInfoPrivate::reload();
if (qmake_library_settings.exists())
qmake_library_settings->load();
}
bool QMakeLibraryInfo::haveGroup(PathGroup group)
{
QMakeLibrarySettings *ls = qmake_library_settings();
return ls
&& (group == EffectiveSourcePaths ? ls->haveEffectiveSourcePaths
: group == EffectivePaths ? ls->haveEffectivePaths
: group == DevicePaths ? ls->haveDevicePaths
: ls->havePaths);
}
void QMakeLibraryInfo::sysrootify(QString &path)
{
// Acceptable values for SysrootifyPrefixPath are "true" and "false"
if (!QVariant::fromValue(rawLocation(SysrootifyPrefixPath, FinalPaths)).toBool())
return;
const QString sysroot = rawLocation(SysrootPath, FinalPaths);
if (sysroot.isEmpty())
return;
if (path.size() > 2 && path.at(1) == QLatin1Char(':')
&& (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\'))) {
path.replace(0, 2, sysroot); // Strip out the drive on Windows targets
} else {
path.prepend(sysroot);
}
}
QString QMakeLibraryInfo::path(int loc)
{
QString ret = rawLocation(loc, QMakeLibraryInfo::FinalPaths);
// Automatically prepend the sysroot to target paths
if (loc < QMakeLibraryInfo::FirstHostPath)
sysrootify(ret);
return ret;
}
static QLibraryInfo::LibraryPath hostToTargetPathEnum(int loc)
{
static std::pair<int, QLibraryInfo::LibraryPath> mapping[] = {
{ QMakeLibraryInfo::HostBinariesPath, QLibraryInfo::BinariesPath },
{ QMakeLibraryInfo::HostLibraryExecutablesPath, QLibraryInfo::LibraryExecutablesPath },
{ QMakeLibraryInfo::HostLibrariesPath, QLibraryInfo::LibrariesPath },
{ QMakeLibraryInfo::HostDataPath, QLibraryInfo::DataPath },
{ QMakeLibraryInfo::HostPrefixPath, QLibraryInfo::PrefixPath }
};
for (size_t i = 0; i < sizeof(mapping) / sizeof(mapping[0]); ++i) {
if (mapping[i].first == loc)
return mapping[i].second;
}
qFatal("Unhandled host path %d in hostToTargetPathEnum.", loc);
Q_UNREACHABLE();
}
static QLibraryInfoPrivate::LocationInfo defaultLocationInfo(int loc)
{
QLibraryInfoPrivate::LocationInfo result;
if (loc < QMakeLibraryInfo::FirstHostPath) {
result = QLibraryInfoPrivate::locationInfo(static_cast<QLibraryInfo::LibraryPath>(loc));
} else if (loc <= QMakeLibraryInfo::LastHostPath) {
result = QLibraryInfoPrivate::locationInfo(hostToTargetPathEnum(loc));
result.key.prepend(QStringLiteral("Host"));
} else if (loc == QMakeLibraryInfo::SysrootPath) {
result.key = QStringLiteral("Sysroot");
} else if (loc == QMakeLibraryInfo::SysrootifyPrefixPath) {
result.key = QStringLiteral("SysrootifyPrefix");
} else if (loc == QMakeLibraryInfo::TargetSpecPath) {
result.key = QStringLiteral("TargetSpec");
} else if (loc == QMakeLibraryInfo::HostSpecPath) {
result.key = QStringLiteral("HostSpec");
}
return result;
}
static QString libraryInfoPath(QLibraryInfo::LibraryPath location)
{
return QLibraryInfoPrivate::path(location, QLibraryInfoPrivate::UsedFromQtBinDir);
}
static QString storedPath(int loc)
{
QString result;
if (loc < QMakeLibraryInfo::FirstHostPath) {
result = libraryInfoPath(static_cast<QLibraryInfo::LibraryPath>(loc));
} else if (loc <= QMakeLibraryInfo::LastHostPath) {
if (loc == QMakeLibraryInfo::HostDataPath) {
// Handle QT_HOST_DATADIR specially. It is not necessarily equal to QT_INSTALL_DATA.
result = QT_HOST_DATADIR;
} else {
result = libraryInfoPath(hostToTargetPathEnum(loc));
}
} else if (loc == QMakeLibraryInfo::SysrootPath) {
// empty result
} else if (loc == QMakeLibraryInfo::SysrootifyPrefixPath) {
result = QStringLiteral("false");
} else if (loc == QMakeLibraryInfo::TargetSpecPath) {
result = QT_TARGET_MKSPEC;
} else if (loc == QMakeLibraryInfo::HostSpecPath) {
result = QT_HOST_MKSPEC;
}
return result;
}
QString QMakeLibraryInfo::rawLocation(int loc, QMakeLibraryInfo::PathGroup group)
{
QString ret;
bool fromConf = false;
// Logic for choosing the right data source: if EffectivePaths are requested
// and qt.conf with that section is present, use it, otherwise fall back to
// FinalPaths. For FinalPaths, use qt.conf if present and contains not only
// [EffectivePaths], otherwise fall back to builtins.
// EffectiveSourcePaths falls back to EffectivePaths.
// DevicePaths falls back to FinalPaths.
PathGroup orig_group = group;
if (QMakeLibraryInfo::haveGroup(group)
|| (group == EffectiveSourcePaths
&& (group = EffectivePaths, QMakeLibraryInfo::haveGroup(group)))
|| ((group == EffectivePaths || group == DevicePaths)
&& (group = FinalPaths, QMakeLibraryInfo::haveGroup(group)))
|| (group = orig_group, false)) {
fromConf = true;
QLibraryInfoPrivate::LocationInfo locinfo = defaultLocationInfo(loc);
if (!locinfo.key.isNull()) {
QSettings *config = QLibraryInfoPrivate::configuration();
Q_ASSERT(config != nullptr);
config->beginGroup(QLatin1String(group == DevicePaths ? "DevicePaths"
: group == EffectiveSourcePaths
? "EffectiveSourcePaths"
: group == EffectivePaths ? "EffectivePaths"
: "Paths"));
if (locinfo.fallbackKey.isNull()) {
ret = config->value(locinfo.key).toString();
} else {
QVariant v = config->value(locinfo.key);
if (!v.isValid())
v = config->value(locinfo.fallbackKey);
ret = v.toString();
}
if (ret.isEmpty()) {
if (loc == HostPrefixPath || loc == TargetSpecPath || loc == HostSpecPath
|| loc == SysrootifyPrefixPath || loc == QLibraryInfo::PrefixPath) {
fromConf = false;
} else {
ret = locinfo.defaultValue;
}
// The last case here is SysrootPath, which can be legitimately empty.
// All other keys have non-empty fallbacks to start with.
}
// TODO: Might be replaced by common for qmake and qtcore function
int startIndex = 0;
forever {
startIndex = ret.indexOf(QLatin1Char('$'), startIndex);
if (startIndex < 0)
break;
if (ret.size() < startIndex + 3)
break;
if (ret.at(startIndex + 1) != QLatin1Char('(')) {
startIndex++;
continue;
}
int endIndex = ret.indexOf(QLatin1Char(')'), startIndex + 2);
if (endIndex < 0)
break;
auto envVarName =
QStringView { ret }.mid(startIndex + 2, endIndex - startIndex - 2);
QString value =
QString::fromLocal8Bit(qgetenv(envVarName.toLocal8Bit().constData()));
ret.replace(startIndex, endIndex - startIndex + 1, value);
startIndex += value.size();
}
config->endGroup();
ret = QDir::fromNativeSeparators(ret);
}
}
if (!fromConf)
ret = storedPath(loc);
// These values aren't actually paths and thus need to be returned verbatim.
if (loc == TargetSpecPath || loc == HostSpecPath || loc == SysrootifyPrefixPath)
return ret;
if (!ret.isEmpty() && QDir::isRelativePath(ret)) {
QString baseDir;
if (loc == HostPrefixPath || loc == QLibraryInfo::PrefixPath || loc == SysrootPath) {
// We make the prefix/sysroot path absolute to the executable's directory.
// loc == PrefixPath while a sysroot is set would make no sense here.
// loc == SysrootPath only makes sense if qmake lives inside the sysroot itself.
QSettings *config = QLibraryInfoPrivate::configuration();
if (config != nullptr) {
baseDir = QFileInfo(config->fileName()).absolutePath();
}
} else if (loc >= FirstHostPath && loc <= LastHostPath) {
// We make any other host path absolute to the host prefix directory.
baseDir = rawLocation(HostPrefixPath, group);
} else {
// we make any other path absolute to the prefix directory
baseDir = rawLocation(QLibraryInfo::PrefixPath, group);
if (group == EffectivePaths)
sysrootify(baseDir);
}
ret = QDir::cleanPath(baseDir + QLatin1Char('/') + ret);
}
return ret;
}
QT_END_NAMESPACE

46
qmake/qmakelibraryinfo.h Normal file
View File

@ -0,0 +1,46 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMAKELIBRARYINFO_H
#define QMAKELIBRARYINFO_H
#include <qlibraryinfo.h>
#include <qstring.h>
#include <qstringlist.h>
QT_BEGIN_NAMESPACE
class QSettings;
struct QMakeLibraryInfo
{
static QString path(int loc);
/* This enum has to start after the last value in QLibraryInfo::LibraryPath(NOT SettingsPath!).
* See qconfig.cpp.in and QLibraryInfo for details.
* When adding enum values between FirstHostPath and LastHostPath, make sure to adjust
* the hostToTargetPathEnum(int) function.
*/
enum LibraryPathQMakeExtras {
HostBinariesPath = QLibraryInfo::TestsPath + 1,
FirstHostPath = HostBinariesPath,
HostLibraryExecutablesPath,
HostLibrariesPath,
HostDataPath,
HostPrefixPath,
LastHostPath = HostPrefixPath,
TargetSpecPath,
HostSpecPath,
SysrootPath,
SysrootifyPrefixPath
};
enum PathGroup { FinalPaths, EffectivePaths, EffectiveSourcePaths, DevicePaths };
static QString rawLocation(int loc, PathGroup group);
static void reload();
static bool haveGroup(PathGroup group);
static void sysrootify(QString &path);
};
QT_END_NAMESPACE
#endif // QMAKELIBRARYINFO_H