From 38be0d13830efd2d98281c645c3a60afe05ffece Mon Sep 17 00:00:00 2001 From: Qt by Nokia Date: Wed, 27 Apr 2011 12:05:43 +0200 Subject: Initial import from the monolithic Qt. This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12 --- qmake/CHANGES | 99 + qmake/Makefile.unix | 349 +++ qmake/Makefile.win32 | 218 ++ qmake/Makefile.win32-g++ | 360 +++ qmake/Makefile.win32-g++-sh | 359 +++ qmake/cachekeys.h | 132 + qmake/generators/integrity/gbuild.cpp | 442 +++ qmake/generators/integrity/gbuild.h | 74 + qmake/generators/mac/pbuilder_pbx.cpp | 1861 +++++++++++ qmake/generators/mac/pbuilder_pbx.h | 88 + qmake/generators/makefile.cpp | 3297 ++++++++++++++++++++ qmake/generators/makefile.h | 309 ++ qmake/generators/makefiledeps.cpp | 961 ++++++ qmake/generators/makefiledeps.h | 132 + qmake/generators/metamakefile.cpp | 572 ++++ qmake/generators/metamakefile.h | 82 + qmake/generators/projectgenerator.cpp | 511 +++ qmake/generators/projectgenerator.h | 71 + .../symbian/initprojectdeploy_symbian.cpp | 379 +++ .../generators/symbian/initprojectdeploy_symbian.h | 82 + qmake/generators/symbian/symbian_makefile.h | 105 + qmake/generators/symbian/symbiancommon.cpp | 1117 +++++++ qmake/generators/symbian/symbiancommon.h | 133 + qmake/generators/symbian/symmake.cpp | 1136 +++++++ qmake/generators/symbian/symmake.h | 149 + qmake/generators/symbian/symmake_abld.cpp | 523 ++++ qmake/generators/symbian/symmake_abld.h | 68 + qmake/generators/symbian/symmake_sbsv2.cpp | 760 +++++ qmake/generators/symbian/symmake_sbsv2.h | 84 + qmake/generators/unix/unixmake.cpp | 925 ++++++ qmake/generators/unix/unixmake.h | 84 + qmake/generators/unix/unixmake2.cpp | 1352 ++++++++ qmake/generators/win32/borland_bmake.cpp | 175 ++ qmake/generators/win32/borland_bmake.h | 68 + qmake/generators/win32/mingw_make.cpp | 516 +++ qmake/generators/win32/mingw_make.h | 88 + qmake/generators/win32/msbuild_objectmodel.cpp | 1940 ++++++++++++ qmake/generators/win32/msbuild_objectmodel.h | 192 ++ qmake/generators/win32/msvc_nmake.cpp | 361 +++ qmake/generators/win32/msvc_nmake.h | 78 + qmake/generators/win32/msvc_objectmodel.cpp | 2909 +++++++++++++++++ qmake/generators/win32/msvc_objectmodel.h | 1141 +++++++ qmake/generators/win32/msvc_vcproj.cpp | 1569 ++++++++++ qmake/generators/win32/msvc_vcproj.h | 151 + qmake/generators/win32/msvc_vcxproj.cpp | 74 + qmake/generators/win32/msvc_vcxproj.h | 70 + qmake/generators/win32/winmakefile.cpp | 895 ++++++ qmake/generators/win32/winmakefile.h | 96 + qmake/generators/xmloutput.cpp | 378 +++ qmake/generators/xmloutput.h | 244 ++ qmake/main.cpp | 197 ++ qmake/meta.cpp | 207 ++ qmake/meta.h | 103 + qmake/option.cpp | 813 +++++ qmake/option.h | 231 ++ qmake/project.cpp | 3170 +++++++++++++++++++ qmake/project.h | 196 ++ qmake/property.cpp | 251 ++ qmake/property.h | 72 + qmake/qmake.pri | 162 + qmake/qmake.pro | 36 + qmake/qmake_pch.h | 71 + 62 files changed, 33268 insertions(+) create mode 100644 qmake/CHANGES create mode 100644 qmake/Makefile.unix create mode 100644 qmake/Makefile.win32 create mode 100644 qmake/Makefile.win32-g++ create mode 100644 qmake/Makefile.win32-g++-sh create mode 100644 qmake/cachekeys.h create mode 100644 qmake/generators/integrity/gbuild.cpp create mode 100644 qmake/generators/integrity/gbuild.h create mode 100644 qmake/generators/mac/pbuilder_pbx.cpp create mode 100644 qmake/generators/mac/pbuilder_pbx.h create mode 100644 qmake/generators/makefile.cpp create mode 100644 qmake/generators/makefile.h create mode 100644 qmake/generators/makefiledeps.cpp create mode 100644 qmake/generators/makefiledeps.h create mode 100644 qmake/generators/metamakefile.cpp create mode 100644 qmake/generators/metamakefile.h create mode 100644 qmake/generators/projectgenerator.cpp create mode 100644 qmake/generators/projectgenerator.h create mode 100644 qmake/generators/symbian/initprojectdeploy_symbian.cpp create mode 100644 qmake/generators/symbian/initprojectdeploy_symbian.h create mode 100644 qmake/generators/symbian/symbian_makefile.h create mode 100644 qmake/generators/symbian/symbiancommon.cpp create mode 100644 qmake/generators/symbian/symbiancommon.h create mode 100644 qmake/generators/symbian/symmake.cpp create mode 100644 qmake/generators/symbian/symmake.h create mode 100644 qmake/generators/symbian/symmake_abld.cpp create mode 100644 qmake/generators/symbian/symmake_abld.h create mode 100644 qmake/generators/symbian/symmake_sbsv2.cpp create mode 100644 qmake/generators/symbian/symmake_sbsv2.h create mode 100644 qmake/generators/unix/unixmake.cpp create mode 100644 qmake/generators/unix/unixmake.h create mode 100644 qmake/generators/unix/unixmake2.cpp create mode 100644 qmake/generators/win32/borland_bmake.cpp create mode 100644 qmake/generators/win32/borland_bmake.h create mode 100644 qmake/generators/win32/mingw_make.cpp create mode 100644 qmake/generators/win32/mingw_make.h create mode 100644 qmake/generators/win32/msbuild_objectmodel.cpp create mode 100644 qmake/generators/win32/msbuild_objectmodel.h create mode 100644 qmake/generators/win32/msvc_nmake.cpp create mode 100644 qmake/generators/win32/msvc_nmake.h create mode 100644 qmake/generators/win32/msvc_objectmodel.cpp create mode 100644 qmake/generators/win32/msvc_objectmodel.h create mode 100644 qmake/generators/win32/msvc_vcproj.cpp create mode 100644 qmake/generators/win32/msvc_vcproj.h create mode 100644 qmake/generators/win32/msvc_vcxproj.cpp create mode 100644 qmake/generators/win32/msvc_vcxproj.h create mode 100644 qmake/generators/win32/winmakefile.cpp create mode 100644 qmake/generators/win32/winmakefile.h create mode 100644 qmake/generators/xmloutput.cpp create mode 100644 qmake/generators/xmloutput.h create mode 100644 qmake/main.cpp create mode 100644 qmake/meta.cpp create mode 100644 qmake/meta.h create mode 100644 qmake/option.cpp create mode 100644 qmake/option.h create mode 100644 qmake/project.cpp create mode 100644 qmake/project.h create mode 100644 qmake/property.cpp create mode 100644 qmake/property.h create mode 100644 qmake/qmake.pri create mode 100644 qmake/qmake.pro create mode 100644 qmake/qmake_pch.h (limited to 'qmake') diff --git a/qmake/CHANGES b/qmake/CHANGES new file mode 100644 index 0000000000..ee2927c7c7 --- /dev/null +++ b/qmake/CHANGES @@ -0,0 +1,99 @@ +2.10a - + + support for spaces in filenames. + +2.00a - + + Automatic feature loading (from CONFIG) has been added, this allows qmake to be + written using qmake language. + + The dependency generation system has been redesigned to be faster (as over time + it had gotten slower and slower as features crept in). This new system is used + for dependency calcuation and moc detection. + + qmake has been changed to allow features to be introduced without changes to + C++ sources (ie project files). + + DEPENDS supported to allow hard coded dependencies (when qmake's depend are inadaquate) + + $$sprintf() added to allow easy expansion of complex strings (using %1..%n) + + for() iteration has been introduced to do simple looping in a qmake script. + + CONFIG() test function added. + + $$basename() and $$dirname() added for file manipulation. + + warning() can be used in place of message() now. + + improved support for subdirs with .pro's listed + support for QT added to add functionality from Qt + +1.07a - + + support for precompiled headers added + +1.06b - + + support for reading and writing libtool (.la) files + support for reading pkgconfig (.pkg) files + + PWD added as an automatic variable to mean the directory + the file being parsed is in (this change required that the + directory be set to the file being parsed as well, function + tests that query relative paths will need to be relative the + file being parsed). + + persistant data cache introduced + +1.05a - + + caching of more information (speed ups) + $$list() added to be used as a lambda function in qmake + $$files() added to allow regular expression matching + $$fromfile() added to grab one single variable value from a parsed file + $$prompt() added to allow querying for user input from qmake + include() modified to support specifying which variables to import + equals() test added to test for equality + MSVC.net generator added [partial solution files in vcsubdirs] + +1.04a - + + subdirs supports multiple project files in a single directory. + +1.03a - + + New function $$system() to extract the value of a shell call. + +1.02a - + + Dependency / Mocable caching. qmake can cache these expensive operations with qmake_cache + CONFIG. + + The parser has been improved to cover more error cases, as well as more forgiving + + qmake now includes a special else scope to invert the previous test + + Ability to add user defined targets to UnixMakefiles. + +1.01a - + + New system for library linking. This system allows a user several different features: + + 1) libtool like library dependencies to static libraries build with qmake + 2) library dependencies, when on .pro depends on another library - it will + automatically build that other library (unix makefiles only) + 3) automatic detection of configurations for Qt, if CONFIG qt is specified + it will find the settings for the most recent Qt itself. + + Project Builder for Mac OS X is now a supported backend for qmake. + + qmake now offers a 'make uninstall' feature, to reverse the actions of a 'make install'. + + qmake can now do recursive searches in project-file mode (-r option). + +1.00a - + + First release, shipped with Qt 3.0. + + qmake ships with support for Unix make, MSVC (both dsp and nmake), Borland make. diff --git a/qmake/Makefile.unix b/qmake/Makefile.unix new file mode 100644 index 0000000000..9dbe035fc2 --- /dev/null +++ b/qmake/Makefile.unix @@ -0,0 +1,349 @@ +SOURCE_PATH = @SOURCE_PATH@ +BUILD_PATH = @BUILD_PATH@ +QTOBJS = @QMAKE_QTOBJS@ +QTSRCS = @QMAKE_QTSRCS@ +QMAKESPEC = @QMAKESPEC@ +LFLAGS = @QMAKE_LFLAGS@ + +#qmake code +OBJS=project.o property.o main.o makefile.o unixmake2.o unixmake.o \ + mingw_make.o option.o winmakefile.o projectgenerator.o \ + meta.o makefiledeps.o metamakefile.o xmloutput.o pbuilder_pbx.o \ + borland_bmake.o msvc_vcproj.o msvc_vcxproj.o msvc_nmake.o msvc_objectmodel.o msbuild_objectmodel.o \ + symmake.o initprojectdeploy_symbian.o symmake_abld.o symmake_sbsv2.o \ + symbiancommon.o registry.o epocroot.o gbuild.o + +#qt code +QOBJS=qtextcodec.o qutfcodec.o qstring.o qtextstream.o qiodevice.o qmalloc.o qglobal.o \ + qbytearray.o qbytearraymatcher.o qdatastream.o qbuffer.o qlist.o qfile.o \ + qfilesystementry.o qfilesystemengine_unix.o qfilesystemengine.o qfilesystemiterator_unix.o \ + qfsfileengine_unix.o qfsfileengine.o \ + qfsfileengine_iterator.o qregexp.o qvector.o qbitarray.o qdir.o qdiriterator.o quuid.o qhash.o \ + qfileinfo.o qdatetime.o qstringlist.o qabstractfileengine.o qtemporaryfile.o \ + qmap.o qmetatype.o qsettings.o qsystemerror.o qlibraryinfo.o qvariant.o qvsnprintf.o \ + qlocale.o qlocale_tools.o qlocale_unix.o qlinkedlist.o qurl.o qnumeric.o qcryptographichash.o \ + qxmlstream.o qxmlutils.o \ + $(QTOBJS) + + + + +#all sources, used for the depend target +DEPEND_SRC=project.cpp property.cpp meta.cpp main.cpp generators/makefile.cpp generators/unix/unixmake2.cpp \ + generators/unix/unixmake.cpp generators/win32/winmakefile.cpp generators/projectgenerator.cpp \ + generators/mac/pbuilder_pbx.cpp generators/mac/xmloutput.cpp generators/metamakefile.cpp \ + generators/makefiledeps.cpp option.cpp generators/win32/mingw_make.cpp generators/makefile.cpp \ + generators/win32/msvc_vcproj.cpp generators/win32/msvc_vcxproj.cpp generators/win32/msvc_objectmodel.cpp generators/win32/msbuild_objectmodel.cpp generators/win32/msbuild_objectmodel.cpp generators/win32/msvc_nmake.cpp generators/win32/borland_bmake.cpp \ + generators/symbian/symmake.cpp generators/symbian/initprojectdeploy_symbian.cpp \ + $(SOURCE_PATH)/tools/shared/windows/registry.cpp \ + $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp \ + generators/symbian/symmake_abld.cpp generators/symbian/symmake_sbsv2.cpp \ + generaters/symbian/symbiancommon.cpp \ + generators/integrity/gbuild.cpp \ + $(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp $(SOURCE_PATH)/src/corelib/codecs/qutfcodec.cpp \ + $(SOURCE_PATH)/src/corelib/tools/qstring.cpp $(SOURCE_PATH)/src/corelib/io/qfile.cpp \ + $(SOURCE_PATH)/src/corelib/io/qtextstream.cpp $(SOURCE_PATH)/src/corelib/io/qiodevice.cpp \ + $(SOURCE_PATH)/src/corelib/global/qmalloc.cpp \ + $(SOURCE_PATH)/src/corelib/global/qglobal.cpp $(SOURCE_PATH)/src/corelib/tools/qregexp.cpp \ + $(SOURCE_PATH)/src/corelib/tools/qbytearray.cpp $(SOURCE_PATH)/src/corelib/tools/qbytearraymatcher.cpp \ + $(SOURCE_PATH)/src/corelib/io/qdatastream.cpp $(SOURCE_PATH)/src/corelib/io/qbuffer.cpp \ + $(SOURCE_PATH)/src/corelib/io/qfilesystementry.cpp $(SOURCE_PATH)/src/corelib/io/qfilesystemengine_unix.cpp \ + $(SOURCE_PATH)/src/corelib/io/qfilesystemengine_mac.cpp \ + $(SOURCE_PATH)/src/corelib/io/qfilesystemengine.cpp $(SOURCE_PATH)/src/corelib/io/qfilesystemiterator_unix.cpp \ + $(SOURCE_PATH)/src/corelib/io/qfsfileengine_unix.cpp $(SOURCE_PATH)/src/corelib/io/qabstractfileengine.cpp \ + $(SOURCE_PATH)/src/corelib/io/qfsfileengine_iterator.cpp \ + $(SOURCE_PATH)/src/corelib/io/qfsfileengine.cpp $(SOURCE_PATH)/src/corelib/tools/qlist.cpp \ + $(SOURCE_PATH)/src/corelib/tools/qvector.cpp $(SOURCE_PATH)/src/corelib/tools/qbitarray.cpp \ + $(SOURCE_PATH)/src/corelib/io/qdiriterator.cpp \ + $(SOURCE_PATH)/src/corelib/io/qdir.cpp $(SOURCE_PATH)/src/corelib/plugin/quuid.cpp \ + $(SOURCE_PATH)/src/corelib/io/qfileinfo.cpp $(SOURCE_PATH)/src/corelib/tools/qdatetime.cpp \ + $(SOURCE_PATH)/src/corelib/tools/qstringlist.cpp $(SOURCE_PATH)/src/corelib/tools/qmap.cpp \ + $(SOURCE_PATH)/src/corelib/global/qconfig.cpp $(SOURCE_PATH)/src/corelib/io/qurl.cpp \ + $(SOURCE_PATH)/src/corelib/tools/qlocale.cpp \ + $(SOURCE_PATH)/src/corelib/tools/qlocale_tools.cpp \ + $(SOURCE_PATH)/src/corelib/tools/qlocale_unix.cpp \ + $(SOURCE_PATH)/src/corelib/tools/qlinkedlist.cpp \ + $(SOURCE_PATH)/src/corelib/tools/qhash.cpp $(SOURCE_PATH)/src/corelib/kernel/qcore_mac.cpp \ + $(SOURCE_PATH)/src/corelib/io/qtemporaryfile.cpp $(SOURCE_PATH)/src/corelib/kernel/qmetatype.cpp \ + $(SOURCE_PATH)/src/corelib/io/qsettings.cpp $(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp \ + $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp $(SOURCE_PATH)/src/corelib/tools/qcryptographichash.cpp \ + $(SOURCE_PATH)/src/corelib/tools/qvsnprintf.cpp $(SOURCE_PATH)/src/corelib/global/qnumeric.cpp \ + $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp \ + $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp \ + $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp \ + $(QTSRCS) + +CPPFLAGS = -g -I. -Igenerators -Igenerators/unix -Igenerators/win32 \ + -Igenerators/mac -Igenerators/symbian -Igenerators/integrity \ + -I$(BUILD_PATH)/include -I$(BUILD_PATH)/include/QtCore \ + -I$(BUILD_PATH)/src/corelib/global -I$(BUILD_PATH)/src/corelib/xml \ + -I$(SOURCE_PATH)/tools/shared \ + -DQT_NO_PCRE \ + -DQT_BUILD_QMAKE -DQT_BOOTSTRAPPED -DQLIBRARYINFO_EPOCROOT \ + -DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_NO_COMPONENT -DQT_NO_STL \ + -DQT_NO_COMPRESS -I$(QMAKESPEC) -DHAVE_QCONFIG_CPP -DQT_NO_THREAD -DQT_NO_QOBJECT \ + -DQT_NO_GEOM_VARIANT -DQT_NO_DEPRECATED $(OPENSOURCE_CXXFLAGS) + +CXXFLAGS = @QMAKE_CXXFLAGS@ $(CPPFLAGS) + +first all: $(BUILD_PATH)/bin/qmake +qmake: $(BUILD_PATH)/bin/qmake + +$(BUILD_PATH)/bin/qmake: $(OBJS) $(QOBJS) + $(CXX) -o "$@" $(OBJS) $(QOBJS) $(LFLAGS) + +clean:: + rm -f $(OBJS) $(QOBJS) + +distclean:: clean + rm -rf $(BUILD_PATH)/bin/qmake .deps + +depend: + makedepend -D__MAKEDEPEND__ $(CPPFLAGS) $(DEPEND_SRC) + +# don't use optimization for these +qtextstream.o: $(SOURCE_PATH)/src/corelib/io/qtextstream.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qtextstream.cpp + +qvariant.o: $(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp + +qsettings.o: $(SOURCE_PATH)/src/corelib/io/qsettings.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qsettings.cpp + +qsystemerror.o: $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp + +qlibraryinfo.o: $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp + +qnumeric.o: $(SOURCE_PATH)/src/corelib/global/qnumeric.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qnumeric.cpp + +qsettings_mac.o: $(SOURCE_PATH)/src/corelib/io/qsettings_mac.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qsettings_mac.cpp + +qiodevice.o: $(SOURCE_PATH)/src/corelib/io/qiodevice.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qiodevice.cpp + +qmalloc.o: $(SOURCE_PATH)/src/corelib/global/qmalloc.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qmalloc.cpp + +qglobal.o: $(SOURCE_PATH)/src/corelib/global/qglobal.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qglobal.cpp + +qbytearray.o: $(SOURCE_PATH)/src/corelib/tools/qbytearray.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qbytearray.cpp + +qvsnprintf.o: $(SOURCE_PATH)/src/corelib/tools/qvsnprintf.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qvsnprintf.cpp + +qbytearraymatcher.o: $(SOURCE_PATH)/src/corelib/tools/qbytearraymatcher.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qbytearraymatcher.cpp + +qmetatype.o: $(SOURCE_PATH)/src/corelib/kernel/qmetatype.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/kernel/qmetatype.cpp + +qcore_mac.o: $(SOURCE_PATH)/src/corelib/kernel/qcore_mac.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/kernel/qcore_mac.cpp + +qurl.o: $(SOURCE_PATH)/src/corelib/io/qurl.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qurl.cpp + +qutfcodec.o: $(SOURCE_PATH)/src/corelib/codecs/qutfcodec.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/codecs/qutfcodec.cpp + +qtextcodec.o: $(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp + +qstring.o: $(SOURCE_PATH)/src/corelib/tools/qstring.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qstring.cpp + +qlocale.o: $(SOURCE_PATH)/src/corelib/tools/qlocale.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlocale.cpp + +qlocale_tools.o: $(SOURCE_PATH)/src/corelib/tools/qlocale_tools.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlocale_tools.cpp + +qlocale_unix.o: $(SOURCE_PATH)/src/corelib/tools/qlocale_unix.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlocale_unix.cpp + +qdatastream.o: $(SOURCE_PATH)/src/corelib/io/qdatastream.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qdatastream.cpp + +qbuffer.o: $(SOURCE_PATH)/src/corelib/io/qbuffer.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qbuffer.cpp + +qlist.o: $(SOURCE_PATH)/src/corelib/tools/qlist.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlist.cpp + +qfile.o: $(SOURCE_PATH)/src/corelib/io/qfile.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfile.cpp + +qfilesystementry.o: $(SOURCE_PATH)/src/corelib/io/qfilesystementry.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystementry.cpp + +qfilesystemengine.o: $(SOURCE_PATH)/src/corelib/io/qfilesystemengine.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystemengine.cpp + +qfilesystemengine_unix.o: $(SOURCE_PATH)/src/corelib/io/qfilesystemengine_unix.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystemengine_unix.cpp + +qfilesystemengine_mac.o: $(SOURCE_PATH)/src/corelib/io/qfilesystemengine_mac.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystemengine_mac.cpp + +qfilesystemiterator_unix.o: $(SOURCE_PATH)/src/corelib/io/qfilesystemiterator_unix.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystemiterator_unix.cpp + +qfsfileengine.o: $(SOURCE_PATH)/src/corelib/io/qfsfileengine.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfsfileengine.cpp + +qfsfileengine_iterator.o: $(SOURCE_PATH)/src/corelib/io/qfsfileengine_iterator.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfsfileengine_iterator.cpp + +qfsfileengine_unix.o: $(SOURCE_PATH)/src/corelib/io/qfsfileengine_unix.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfsfileengine_unix.cpp + +qabstractfileengine.o: $(SOURCE_PATH)/src/corelib/io/qabstractfileengine.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qabstractfileengine.cpp + +qtemporaryfile.o: $(SOURCE_PATH)/src/corelib/io/qtemporaryfile.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qtemporaryfile.cpp + +qregexp.o: $(SOURCE_PATH)/src/corelib/tools/qregexp.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qregexp.cpp + +qvector.o: $(SOURCE_PATH)/src/corelib/tools/qvector.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qvector.cpp + +qbitarray.o: $(SOURCE_PATH)/src/corelib/tools/qbitarray.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qbitarray.cpp + +qdir.o: $(SOURCE_PATH)/src/corelib/io/qdir.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qdir.cpp + +qdiriterator.o: $(SOURCE_PATH)/src/corelib/io/qdiriterator.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qdiriterator.cpp + +quuid.o: $(SOURCE_PATH)/src/corelib/plugin/quuid.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/plugin/quuid.cpp + +qfileinfo.o: $(SOURCE_PATH)/src/corelib/io/qfileinfo.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfileinfo.cpp + +qdatetime.o: $(SOURCE_PATH)/src/corelib/tools/qdatetime.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qdatetime.cpp + +qstringlist.o: $(SOURCE_PATH)/src/corelib/tools/qstringlist.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qstringlist.cpp + +qmap.o: $(SOURCE_PATH)/src/corelib/tools/qmap.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qmap.cpp + +qhash.o: $(SOURCE_PATH)/src/corelib/tools/qhash.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qhash.cpp + +qlinkedlist.o: $(SOURCE_PATH)/src/corelib/tools/qlinkedlist.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlinkedlist.cpp + +winmakefile.o: generators/win32/winmakefile.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/win32/winmakefile.cpp + +project.o: project.cpp project.h option.h + $(CXX) -c -o $@ $(CXXFLAGS) project.cpp + +property.o: property.cpp project.h option.h + $(CXX) -c -o $@ $(CXXFLAGS) property.cpp + +meta.o: meta.cpp project.h option.h + $(CXX) -c -o $@ $(CXXFLAGS) meta.cpp + +main.o: main.cpp project.h + $(CXX) -c -o $@ $(CXXFLAGS) main.cpp + +option.o: option.cpp option.h $(BUILD_PATH)/src/corelib/global/qconfig.cpp + $(CXX) -c -o $@ $(CXXFLAGS) option.cpp + +qcryptographichash.o: $(SOURCE_PATH)/src/corelib/tools/qcryptographichash.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qcryptographichash.cpp + +metamakefile.o: generators/metamakefile.cpp generators/symbian/symbian_makefile.h + $(CXX) -c -o $@ $(CXXFLAGS) generators/metamakefile.cpp + +xmloutput.o: generators/xmloutput.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/xmloutput.cpp + +makefiledeps.o: generators/makefiledeps.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/makefiledeps.cpp + +makefile.o: generators/makefile.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/makefile.cpp + +unixmake.o: generators/unix/unixmake.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/unix/unixmake.cpp + +unixmake2.o: generators/unix/unixmake2.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/unix/unixmake2.cpp + +borland_bmake.o: generators/win32/borland_bmake.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/win32/borland_bmake.cpp + +mingw_make.o: generators/win32/mingw_make.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/win32/mingw_make.cpp + +msvc_objectmodel.o: generators/win32/msvc_objectmodel.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/win32/msvc_objectmodel.cpp + +msvc_vcproj.o: generators/win32/msvc_vcproj.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/win32/msvc_vcproj.cpp + +msbuild_objectmodel.o: generators/win32/msbuild_objectmodel.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/win32/msbuild_objectmodel.cpp + +msvc_vcxproj.o: generators/win32/msvc_vcxproj.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/win32/msvc_vcxproj.cpp + +msvc_nmake.o: generators/win32/msvc_nmake.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/win32/msvc_nmake.cpp + +pbuilder_pbx.o: generators/mac/pbuilder_pbx.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/mac/pbuilder_pbx.cpp + +symmake.o: generators/symbian/symmake.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/symbian/symmake.cpp + +symmake_abld.o: generators/symbian/symmake_abld.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/symbian/symmake_abld.cpp + +symmake_sbsv2.o: generators/symbian/symmake_sbsv2.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/symbian/symmake_sbsv2.cpp + +symbiancommon.o: generators/symbian/symbiancommon.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/symbian/symbiancommon.cpp + +initprojectdeploy_symbian.o: generators/symbian/initprojectdeploy_symbian.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/symbian/initprojectdeploy_symbian.cpp + +registry.o: $(SOURCE_PATH)/tools/shared/windows/registry.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/windows/registry.cpp + +epocroot.o: $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + +gbuild.o: generators/integrity/gbuild.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/integrity/gbuild.cpp + +projectgenerator.o: generators/projectgenerator.cpp + $(CXX) -c -o $@ $(CXXFLAGS) generators/projectgenerator.cpp + +qxmlstream.o: $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp + +qxmlutils.o: $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp + +#default rules +.cpp.o: + $(CXX) -c -o $@ $(CXXFLAGS) $< + +# DO NOT DELETE THIS LINE -- make depend depends on it diff --git a/qmake/Makefile.win32 b/qmake/Makefile.win32 new file mode 100644 index 0000000000..6fd393993e --- /dev/null +++ b/qmake/Makefile.win32 @@ -0,0 +1,218 @@ +!IF "$(QMAKESPEC)" == "win32-msvc" || "$(QMAKESPEC)" == "win32-msvc.net" || "$(QMAKESPEC)" == "win32-msvc2002" || "$(QMAKESPEC)" == "win32-msvc2003" || "$(QMAKESPEC)" == "win32-msvc2005" || "$(QMAKESPEC)" == "win32-msvc2008" || "$(QMAKESPEC)" == "win32-msvc2010" || "$(QMAKESPEC)" == "win32-icc" + +!if "$(SOURCE_PATH)" == "" +SOURCE_PATH = .. +!endif +!if "$(BUILD_PATH)" == "" +BUILD_PATH = .. +!endif + +# +# specific stuff for NMake and ICC +# +!if "$(QMAKESPEC)" == "win32-icc" +CXX = icl +LINK = link +CFLAGS = /Zc:forScope +!else +CXX = cl +LINK = link +!endif + +# +# specific stuff for VS2005 +# +!if "$(QMAKESPEC)" == "win32-msvc2005" +CFLAGS_EXTRA = /Zc:wchar_t- +!elseif "$(QMAKESPEC)" == "win32-msvc2008" || "$(QMAKESPEC)" == "win32-msvc2010" +CFLAGS_EXTRA = /MP +!endif + +CFLAGS_BARE = -c -Fo./ \ + -W3 -nologo -O2 \ + $(CFLAGS_EXTRA) \ + -I. -Igenerators -Igenerators\unix -Igenerators\win32 -Igenerators\mac -Igenerators\symbian -Igenerators\integrity \ + -I$(BUILD_PATH)\include -I$(BUILD_PATH)\include\QtCore \ + -I$(SOURCE_PATH)\include -I$(SOURCE_PATH)\include\QtCore \ + -I$(BUILD_PATH)\src\corelib\global \ + -I$(BUILD_PATH)\src\corelib\xml \ + -I$(SOURCE_PATH)\mkspecs\$(QMAKESPEC) \ + -I$(SOURCE_PATH)\tools\shared \ + -DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_LITE_COMPONENT -DQT_NODLL -DQT_NO_STL \ + -DQT_NO_COMPRESS -DUNICODE -DHAVE_QCONFIG_CPP -DQT_BUILD_QMAKE -DQT_NO_THREAD \ + -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT -DQT_NO_DATASTREAM -DQT_NO_PCRE -DQT_BOOTSTRAPPED \ + -DQLIBRARYINFO_EPOCROOT +CFLAGS = -Yuqmake_pch.h -FIqmake_pch.h -Fpqmake_pch.pch $(CFLAGS_BARE) $(CFLAGS) + +CXXFLAGS_BARE = $(CFLAGS_BARE) +CXXFLAGS = $(CFLAGS) + +LFLAGS = +LIBS = ole32.lib advapi32.lib +LINKQMAKE = $(LINK) $(LFLAGS) -OUT:qmake.exe $(OBJS) $(QTOBJS) $(LIBS) +ADDCLEAN = vc60.pdb vc70.pdb qmake.pdb qmake.ilk + +!ELSE +!ERROR Unsupported compiler for this Makefile +!ENDIF + +#qmake code +OBJS = project.obj main.obj makefile.obj unixmake.obj unixmake2.obj mingw_make.obj \ + option.obj winmakefile.obj projectgenerator.obj property.obj meta.obj \ + makefiledeps.obj metamakefile.obj xmloutput.obj pbuilder_pbx.obj \ + borland_bmake.obj msvc_nmake.obj msvc_vcproj.obj msvc_vcxproj.obj \ + msvc_objectmodel.obj msbuild_objectmodel.obj symmake.obj initprojectdeploy_symbian.obj \ + symmake_abld.obj symmake_sbsv2.obj symbiancommon.obj registry.obj epocroot.obj \ + gbuild.obj + +!IFDEF QMAKE_OPENSOURCE_EDITION +CFLAGS = $(CFLAGS) -DQMAKE_OPENSOURCE_EDITION +!ENDIF + +#qt code +QTOBJS= \ + qbitarray.obj \ + qbuffer.obj \ + qcryptographichash.obj \ + qfilesystementry.obj \ + qfilesystemengine.obj \ + qfilesystemengine_win.obj \ + qfilesystemiterator_win.obj \ + qfsfileengine.obj \ + qfsfileengine_iterator.obj \ + qbytearray.obj \ + qvsnprintf.obj \ + qbytearraymatcher.obj \ + qdatetime.obj \ + qdir.obj \ + qdiriterator.obj \ + qfile.obj \ + qtemporaryfile.obj \ + qabstractfileengine.obj \ + qfsfileengine_win.obj \ + qsystemlibrary.obj \ + qfileinfo.obj \ + qglobal.obj \ + qhash.obj \ + qiodevice.obj \ + qlist.obj \ + qlinkedlist.obj \ + qlocale.obj \ + qlocale_tools.obj \ + qlocale_win.obj \ + qmalloc.obj \ + qmap.obj \ + qregexp.obj \ + qtextcodec.obj \ + qutfcodec.obj \ + qstring.obj \ + qstringlist.obj \ + qsystemerror.obj \ + qtextstream.obj \ + qdatastream.obj \ + quuid.obj \ + qvector.obj \ + qsettings.obj \ + qlibraryinfo.obj \ + qvariant.obj \ + qurl.obj \ + qsettings_win.obj \ + qmetatype.obj \ + qxmlstream.obj \ + qxmlutils.obj \ + qnumeric.obj + + +first all: qmake.exe + +qmake.exe: $(OBJS) $(QTOBJS) + $(LINKQMAKE) qmake_pch.obj + -copy qmake.exe $(BUILD_PATH)\bin\qmake.exe + +clean:: + -del $(QTOBJS) + -del $(OBJS) + -del qmake_pch.obj + -del qmake_pch.pch + -del qsystemlibrary.obj + -del vc60.pdb + -del vc70.pdb + -del qmake.pdb + -del qmake.ilk + -del qmake.tds + +distclean:: clean + -del qmake + +.c.obj: + $(CXX) $(CFLAGS) $< + +.cpp.obj: + $(CXX) $(CXXFLAGS) $< + +.cc.obj: + $(CXX) $(CXXFLAGS) $< + +.cxx.obj: + $(CXX) $(CXXFLAGS) $< + +$(OBJS): qmake_pch.obj + +qsystemlibrary.obj: $(SOURCE_PATH)\src\corelib\plugin\qsystemlibrary.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)\src\corelib\plugin\qsystemlibrary.cpp + +$(QTOBJS): qmake_pch.obj + +qmake_pch.obj: + $(CXX) $(CXXFLAGS_BARE) -c -Yc -Fpqmake_pch.pch -TP qmake_pch.h + +{$(SOURCE_PATH)\qmake\generators\mac}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\qmake\generators\symbian}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\qmake\generators\integrity}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\qmake\generators\unix}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\qmake\generators\win32}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\qmake\generators}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\qmake}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\src\3rdparty\md5}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\src\corelib\codecs}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\src\corelib\global}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\src\corelib\io}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\src\corelib\kernel}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\src\corelib\plugin}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\src\corelib\tools}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\src\corelib\xml}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\tools\shared\symbian}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< + +{$(SOURCE_PATH)\tools\shared\windows}.cpp{}.obj:: + $(CXX) $(CXXFLAGS) $< diff --git a/qmake/Makefile.win32-g++ b/qmake/Makefile.win32-g++ new file mode 100644 index 0000000000..d40dc29a9b --- /dev/null +++ b/qmake/Makefile.win32-g++ @@ -0,0 +1,360 @@ +ifeq "$(SOURCE_PATH)" "" +SOURCE_PATH = .. +endif + +#cmd version + +ifeq "$(BUILD_PATH)" "" +BUILD_PATH = .. +endif + +# +# specific stuff for mingw g++ make +# +CXX = g++ +CFLAGS = -c -o$@ -O \ + -I. -Igenerators -Igenerators/unix \ + -Igenerators/win32 -Igenerators/mac \ + -Igenerators/symbian -Igenerators/integrity \ + -I$(BUILD_PATH)/include -I$(BUILD_PATH)/include/QtCore \ + -I$(SOURCE_PATH)/include -I$(SOURCE_PATH)/include/QtCore \ + -I$(BUILD_PATH)/src/corelib/global \ + -I$(BUILD_PATH)/src/corelib/xml \ + -I$(SOURCE_PATH)/mkspecs/win32-g++ \ + -I$(SOURCE_PATH)/tools/shared \ + -DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_LITE_COMPONENT -DQT_NO_PCRE \ + -DQT_NODLL -DQT_NO_STL -DQT_NO_COMPRESS -DUNICODE -DHAVE_QCONFIG_CPP \ + -DQT_BUILD_QMAKE -DQT_NO_THREAD -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT -DQT_NO_DATASTREAM \ + -DQT_BOOTSTRAPPED -DQLIBRARYINFO_EPOCROOT +CXXFLAGS = $(CFLAGS) +LFLAGS = -static-libgcc -static-libstdc++ -s +LIBS = -lole32 -luuid -ladvapi32 -lkernel32 +LINKQMAKE = g++ $(LFLAGS) -o qmake.exe $(OBJS) $(QTOBJS) $(LIBS) +ADDCLEAN = + + +#qmake code +OBJS = project.o main.o makefile.o unixmake.o unixmake2.o mingw_make.o \ + option.o winmakefile.o projectgenerator.o property.o meta.o \ + makefiledeps.o metamakefile.o xmloutput.o pbuilder_pbx.o \ + borland_bmake.o msvc_nmake.o msvc_vcproj.o msvc_vcxproj.o \ + msvc_objectmodel.o msbuild_objectmodel.o symmake.o initprojectdeploy_symbian.o \ + symmake_abld.o symmake_sbsv2.o symbiancommon.o registry.o epocroot.o gbuild.o + +ifdef QMAKE_OPENSOURCE_EDITION +CFLAGS += -DQMAKE_OPENSOURCE_EDITION +endif + +#qt code +QTOBJS= \ + qbitarray.o \ + qbuffer.o \ + qbytearray.o \ + qcryptographichash.o \ + qvsnprintf.o \ + qbytearraymatcher.o \ + qconfig.o \ + qdatetime.o \ + qdir.o \ + qdiriterator.o \ + qfile.o \ + qtemporaryfile.o \ + qfileinfo.o \ + qabstractfileengine.o \ + qfilesystementry.o \ + qfilesystemengine.o \ + qfilesystemengine_win.o \ + qfilesystemiterator_win.o \ + qfsfileengine.o \ + qfsfileengine_iterator.o \ + qfsfileengine_win.o \ + qglobal.o \ + qhash.o \ + qiodevice.o \ + qlibraryinfo.o \ + qlist.o \ + qlinkedlist.o \ + qlocale.o \ + qlocale_tools.o \ + qlocale_win.o \ + qmalloc.o \ + qmap.o \ + qregexp.o \ + qtextcodec.o \ + qutfcodec.o \ + qstring.o \ + qstringlist.o \ + qsystemerror.o \ + qsystemlibrary.o \ + qtextstream.o \ + quuid.o \ + qvector.o \ + qurl.o \ + qsettings.o \ + qsettings_win.o \ + qvariant.o \ + qmetatype.o \ + qxmlstream.o \ + qxmlutils.o \ + qnumeric.o + + +qmake.exe: $(OBJS) $(QTOBJS) + $(LINKQMAKE) + -copy qmake.exe $(BUILD_PATH)\bin\qmake.exe + +Makefile: Makefile.win32-g++ + @echo "Out of date, please rerun configure" + +clean:: + -del $(OBJS) $(QTOBJS) $(ADDCLEAN) + +distclean:: clean + -del qmake + +.c.o: + $(CXX) $(CFLAGS) $< + +.cpp.o: + $(CXX) $(CXXFLAGS) $< + +qconfig.o: $(BUILD_PATH)/src/corelib/global/qconfig.cpp + $(CXX) $(CXXFLAGS) $(BUILD_PATH)/src/corelib/global/qconfig.cpp + +qsettings_win.o: $(SOURCE_PATH)/src/corelib/io/qsettings_win.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qsettings_win.cpp + +qsettings.o: $(SOURCE_PATH)/src/corelib/io/qsettings.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qsettings.cpp + +qvariant.o: $(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp + +qurl.o: $(SOURCE_PATH)/src/corelib/io/qurl.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qurl.cpp + +qtextstream.o: $(SOURCE_PATH)/src/corelib/io/qtextstream.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qtextstream.cpp + +qdatastream.o: $(SOURCE_PATH)/src/corelib/io/qdatastream.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qdatastream.cpp + +qiodevice.o: $(SOURCE_PATH)/src/corelib/io/qiodevice.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qiodevice.cpp + +qlibraryinfo.o: $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp + +qnumeric.o: $(SOURCE_PATH)/src/corelib/global/qnumeric.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qnumeric.cpp + +qmalloc.o: $(SOURCE_PATH)/src/corelib/global/qmalloc.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qmalloc.cpp + +qglobal.o: $(SOURCE_PATH)/src/corelib/global/qglobal.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qglobal.cpp + +qhash.o: $(SOURCE_PATH)/src/corelib/tools/qhash.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qhash.cpp + +qbytearray.o: $(SOURCE_PATH)/src/corelib/tools/qbytearray.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qbytearray.cpp + +qcryptographichash.o: $(SOURCE_PATH)/src/corelib/tools/qcryptographichash.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qcryptographichash.cpp + +qvsnprintf.o: $(SOURCE_PATH)/src/corelib/tools/qvsnprintf.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qvsnprintf.cpp + +qbytearraymatcher.o: $(SOURCE_PATH)/src/corelib/tools/qbytearraymatcher.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qbytearraymatcher.cpp + +qutfcodec.o: $(SOURCE_PATH)/src/corelib/codecs/qutfcodec.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/codecs/qutfcodec.cpp + +qstring.o: $(SOURCE_PATH)/src/corelib/tools/qstring.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qstring.cpp + +qlocale.o: $(SOURCE_PATH)/src/corelib/tools/qlocale.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlocale.cpp + +qlocale_tools.o: $(SOURCE_PATH)/src/corelib/tools/qlocale_tools.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlocale_tools.cpp + +qlocale_win.o: $(SOURCE_PATH)/src/corelib/tools/qlocale_win.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlocale_win.cpp + +quuid.o: $(SOURCE_PATH)/src/corelib/plugin/quuid.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/plugin/quuid.cpp + +qbuffer.o: $(SOURCE_PATH)/src/corelib/io/qbuffer.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qbuffer.cpp + +qlist.o: $(SOURCE_PATH)/src/corelib/tools/qlist.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlist.cpp + +qlinkedlist.o: $(SOURCE_PATH)/src/corelib/tools/qlinkedlist.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlinkedlist.cpp + +qfile.o: $(SOURCE_PATH)/src/corelib/io/qfile.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfile.cpp + +qtemporaryfile.o: $(SOURCE_PATH)/src/corelib/io/qtemporaryfile.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qtemporaryfile.cpp + +qabstractfileengine.o: $(SOURCE_PATH)/src/corelib/io/qabstractfileengine.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qabstractfileengine.cpp + +qfilesystementry.o: $(SOURCE_PATH)/src/corelib/io/qfilesystementry.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystementry.cpp + +qfilesystemengine.o: $(SOURCE_PATH)/src/corelib/io/qfilesystemengine.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystemengine.cpp + +qfilesystemengine_win.o: $(SOURCE_PATH)/src/corelib/io/qfilesystemengine_win.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystemengine_win.cpp + +qfilesystemiterator_win.o: $(SOURCE_PATH)/src/corelib/io/qfilesystemiterator_win.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystemiterator_win.cpp + +qfsfileengine_win.o: $(SOURCE_PATH)/src/corelib/io/qfsfileengine_win.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfsfileengine_win.cpp + +qfsfileengine.o: $(SOURCE_PATH)/src/corelib/io/qfsfileengine.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfsfileengine.cpp + +qfsfileengine_iterator.o: $(SOURCE_PATH)/src/corelib/io/qfsfileengine_iterator.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfsfileengine_iterator.cpp + +qtextcodec.o: $(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp + +qregexp.o: $(SOURCE_PATH)/src/corelib/tools/qregexp.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qregexp.cpp + +qvector.o: $(SOURCE_PATH)/src/corelib/tools/qvector.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qvector.cpp + +qbitarray.o: $(SOURCE_PATH)/src/corelib/tools/qbitarray.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qbitarray.cpp + +qdir.o: $(SOURCE_PATH)/src/corelib/io/qdir.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qdir.cpp + +qdiriterator.o: $(SOURCE_PATH)/src/corelib/io/qdiriterator.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qdiriterator.cpp + +qmetatype.o: $(SOURCE_PATH)/src/corelib/kernel/qmetatype.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/kernel/qmetatype.cpp + +qfileinfo.o: $(SOURCE_PATH)/src/corelib/io/qfileinfo.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfileinfo.cpp + +qdatetime.o: $(SOURCE_PATH)/src/corelib/tools/qdatetime.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qdatetime.cpp + +qstringlist.o: $(SOURCE_PATH)/src/corelib/tools/qstringlist.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qstringlist.cpp + +qsystemerror.o: $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp + +qsystemlibrary.o: $(SOURCE_PATH)/src/corelib/plugin/qsystemlibrary.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/plugin/qsystemlibrary.cpp + +qmap.o: $(SOURCE_PATH)/src/corelib/tools/qmap.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qmap.cpp + +makefile.o: $(SOURCE_PATH)/qmake/generators/makefile.cpp + $(CXX) $(CXXFLAGS) generators/makefile.cpp + +unixmake.o: $(SOURCE_PATH)/qmake/generators/unix/unixmake.cpp + $(CXX) $(CXXFLAGS) generators/unix/unixmake.cpp + +unixmake2.o: $(SOURCE_PATH)/qmake/generators/unix/unixmake2.cpp + $(CXX) $(CXXFLAGS) generators/unix/unixmake2.cpp + +winmakefile.o: $(SOURCE_PATH)/qmake/generators/win32/winmakefile.cpp + $(CXX) $(CXXFLAGS) generators/win32/winmakefile.cpp + +borland_bmake.o: $(SOURCE_PATH)/qmake/generators/win32/borland_bmake.cpp + $(CXX) $(CXXFLAGS) generators/win32/borland_bmake.cpp + +mingw_make.o: $(SOURCE_PATH)/qmake/generators/win32/mingw_make.cpp + $(CXX) $(CXXFLAGS) generators/win32/mingw_make.cpp + +msvc_nmake.o: $(SOURCE_PATH)/qmake/generators/win32/msvc_nmake.cpp + $(CXX) $(CXXFLAGS) generators/win32/msvc_nmake.cpp + +msvc_vcproj.o: $(SOURCE_PATH)/qmake/generators/win32/msvc_vcproj.cpp + $(CXX) $(CXXFLAGS) generators/win32/msvc_vcproj.cpp + +msvc_objectmodel.o: $(SOURCE_PATH)/qmake/generators/win32/msvc_objectmodel.cpp + $(CXX) $(CXXFLAGS) generators/win32/msvc_objectmodel.cpp + +msvc_vcxproj.o: $(SOURCE_PATH)/qmake/generators/win32/msvc_vcxproj.cpp + $(CXX) $(CXXFLAGS) generators/win32/msvc_vcxproj.cpp + +msbuild_objectmodel.o: $(SOURCE_PATH)/qmake/generators/win32/msbuild_objectmodel.cpp + $(CXX) $(CXXFLAGS) generators/win32/msbuild_objectmodel.cpp + +symmake.o: $(SOURCE_PATH)/qmake/generators/symbian/symmake.cpp + $(CXX) $(CXXFLAGS) generators/symbian/symmake.cpp + +symmake_abld.o: $(SOURCE_PATH)/qmake/generators/symbian/symmake_abld.cpp + $(CXX) $(CXXFLAGS) generators/symbian/symmake_abld.cpp + +symmake_sbsv2.o: $(SOURCE_PATH)/qmake/generators/symbian/symmake_sbsv2.cpp + $(CXX) $(CXXFLAGS) generators/symbian/symmake_sbsv2.cpp + +symbiancommon.o: $(SOURCE_PATH)/qmake/generators/symbian/symbiancommon.cpp + $(CXX) $(CXXFLAGS) generators/symbian/symbiancommon.cpp + +initprojectdeploy_symbian.o: $(SOURCE_PATH)/qmake/generators/symbian/initprojectdeploy_symbian.cpp + $(CXX) $(CXXFLAGS) generators/symbian/initprojectdeploy_symbian.cpp + +registry.o: $(SOURCE_PATH)/tools/shared/windows/registry.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/windows/registry.cpp + +epocroot.o: $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + +gbuild.o: $(SOURCE_PATH)/qmake/generators/integrity/gbuild.cpp + $(CXX) $(CXXFLAGS) generators/integrity/gbuild.cpp + +project.o: $(SOURCE_PATH)/qmake/project.cpp $(SOURCE_PATH)/qmake/project.h $(SOURCE_PATH)/qmake/option.h + $(CXX) $(CXXFLAGS) project.cpp + +meta.o: $(SOURCE_PATH)/qmake/meta.cpp $(SOURCE_PATH)/qmake/project.h $(SOURCE_PATH)/qmake/option.h + $(CXX) $(CXXFLAGS) meta.cpp + +main.o: $(SOURCE_PATH)/qmake/main.cpp $(SOURCE_PATH)/qmake/project.h + $(CXX) $(CXXFLAGS) main.cpp + +option.o: $(SOURCE_PATH)/qmake/option.cpp $(SOURCE_PATH)/qmake/option.h + $(CXX) $(CXXFLAGS) option.cpp + +property.o: $(SOURCE_PATH)/qmake/property.cpp $(SOURCE_PATH)/qmake/project.h $(SOURCE_PATH)/qmake/option.h + $(CXX) $(CXXFLAGS) property.cpp + +projectgenerator.o: $(SOURCE_PATH)/qmake/generators/projectgenerator.cpp + $(CXX) $(CXXFLAGS) generators/projectgenerator.cpp + +pbuilder_pbx.o: $(SOURCE_PATH)/qmake/generators/mac/pbuilder_pbx.cpp + $(CXX) $(CXXFLAGS) generators/mac/pbuilder_pbx.cpp + +makefiledeps.o: $(SOURCE_PATH)/qmake/generators/makefiledeps.cpp + $(CXX) $(CXXFLAGS) generators/makefiledeps.cpp + +metamakefile.o: $(SOURCE_PATH)/qmake/generators/metamakefile.cpp $(SOURCE_PATH)/qmake/generators/symbian/symbian_makefile.h + $(CXX) $(CXXFLAGS) generators/metamakefile.cpp + +xmloutput.o: $(SOURCE_PATH)/qmake/generators/xmloutput.cpp + $(CXX) $(CXXFLAGS) generators/xmloutput.cpp + +qxmlstream.o: $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp + +qxmlutils.o: $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp + diff --git a/qmake/Makefile.win32-g++-sh b/qmake/Makefile.win32-g++-sh new file mode 100644 index 0000000000..49ccfbcc77 --- /dev/null +++ b/qmake/Makefile.win32-g++-sh @@ -0,0 +1,359 @@ +ifeq "$(SOURCE_PATH)" "" +SOURCE_PATH = .. +endif + +#sh version + +ifeq "$(BUILD_PATH)" "" +BUILD_PATH = .. +endif + +# +# specific stuff for mingw g++ make +# +CXX = g++ +CFLAGS = -c -o$@ -O \ + -I. -Igenerators -Igenerators/unix \ + -Igenerators/win32 -Igenerators/mac \ + -Igenerators/symbian -Igenerators/integrity \ + -I$(BUILD_PATH)/include -I$(BUILD_PATH)/include/QtCore \ + -I$(SOURCE_PATH)/include -I$(SOURCE_PATH)/include/QtCore \ + -I$(BUILD_PATH)/src/corelib/global \ + -I$(BUILD_PATH)/src/corelib/xml \ + -I$(SOURCE_PATH)/mkspecs/win32-g++ \ + -I$(SOURCE_PATH)/tools/shared \ + -DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_LITE_COMPONENT -DQT_NO_PCRE \ + -DQT_NODLL -DQT_NO_STL -DQT_NO_COMPRESS -DUNICODE -DHAVE_QCONFIG_CPP \ + -DQT_BUILD_QMAKE -DQT_NO_THREAD -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT -DQT_NO_DATASTREAM \ + -DQT_BOOTSTRAPPED -DQLIBRARYINFO_EPOCROOT +CXXFLAGS = $(CFLAGS) +LFLAGS = -static-libgcc -static-libstdc++ -s +LIBS = -lole32 -luuid -ladvapi32 -lkernel32 +LINKQMAKE = g++ $(LFLAGS) -o qmake.exe $(OBJS) $(QTOBJS) $(LIBS) +ADDCLEAN = + + +#qmake code +OBJS = project.o main.o makefile.o unixmake.o unixmake2.o mingw_make.o \ + option.o winmakefile.o projectgenerator.o property.o meta.o \ + makefiledeps.o metamakefile.o xmloutput.o pbuilder_pbx.o \ + borland_bmake.o msvc_nmake.o msvc_vcproj.o msvc_vcxproj.o \ + msvc_objectmodel.o msbuild_objectmodel.o symmake.o initprojectdeploy_symbian.o \ + symmake_abld.o symmake_sbsv2.o symbiancommon.o registry.o epocroot.o gbuild.o + +ifdef QMAKE_OPENSOURCE_EDITION +CFLAGS += -DQMAKE_OPENSOURCE_EDITION +endif + +#qt code +QTOBJS= \ + qbitarray.o \ + qbuffer.o \ + qbytearray.o \ + qcryptographichash.o \ + qvsnprintf.o \ + qbytearraymatcher.o \ + qconfig.o \ + qdatetime.o \ + qdir.o \ + qdiriterator.o \ + qfile.o \ + qtemporaryfile.o \ + qfileinfo.o \ + qabstractfileengine.o \ + qfilesystementry.o \ + qfilesystemengine.o \ + qfilesystemengine_win.o \ + qfilesystemiterator_win.o \ + qfsfileengine.o \ + qfsfileengine_iterator.o \ + qfsfileengine_win.o \ + qglobal.o \ + qhash.o \ + qiodevice.o \ + qlibraryinfo.o \ + qlist.o \ + qlinkedlist.o \ + qlocale.o \ + qlocale_tools.o \ + qlocale_win.o \ + qmalloc.o \ + qmap.o \ + qregexp.o \ + qtextcodec.o \ + qutfcodec.o \ + qstring.o \ + qstringlist.o \ + qsystemlibrary.o \ + qsystemerror.o \ + qtextstream.o \ + quuid.o \ + qvector.o \ + qurl.o \ + qsettings.o \ + qsettings_win.o \ + qvariant.o \ + qmetatype.o \ + qxmlstream.o \ + qxmlutils.o \ + qnumeric.o + +qmake.exe: $(OBJS) $(QTOBJS) + $(LINKQMAKE) + -cp qmake.exe $(BUILD_PATH)/bin/qmake.exe + +Makefile: Makefile.win32-g++-sh + @echo "Out of date, please rerun configure" + +clean:: + -del $(OBJS) $(QTOBJS) $(ADDCLEAN) + +distclean:: clean + -del qmake + +.c.o: + $(CXX) $(CFLAGS) $< + +.cpp.o: + $(CXX) $(CXXFLAGS) $< + +qconfig.o: $(BUILD_PATH)/src/corelib/global/qconfig.cpp + $(CXX) $(CXXFLAGS) $(BUILD_PATH)/src/corelib/global/qconfig.cpp + +qsettings_win.o: $(SOURCE_PATH)/src/corelib/io/qsettings_win.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qsettings_win.cpp + +qsettings.o: $(SOURCE_PATH)/src/corelib/io/qsettings.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qsettings.cpp + +qvariant.o: $(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp + +qurl.o: $(SOURCE_PATH)/src/corelib/io/qurl.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qurl.cpp + +qtextstream.o: $(SOURCE_PATH)/src/corelib/io/qtextstream.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qtextstream.cpp + +qdatastream.o: $(SOURCE_PATH)/src/corelib/io/qdatastream.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qdatastream.cpp + +qiodevice.o: $(SOURCE_PATH)/src/corelib/io/qiodevice.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qiodevice.cpp + +qlibraryinfo.o: $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp + +qnumeric.o: $(SOURCE_PATH)/src/corelib/global/qnumeric.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qnumeric.cpp + +qmalloc.o: $(SOURCE_PATH)/src/corelib/global/qmalloc.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qmalloc.cpp + +qglobal.o: $(SOURCE_PATH)/src/corelib/global/qglobal.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qglobal.cpp + +qhash.o: $(SOURCE_PATH)/src/corelib/tools/qhash.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qhash.cpp + +qbytearray.o: $(SOURCE_PATH)/src/corelib/tools/qbytearray.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qbytearray.cpp + +qcryptographichash.o: $(SOURCE_PATH)/src/corelib/tools/qcryptographichash.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qcryptographichash.cpp + +qvsnprintf.o: $(SOURCE_PATH)/src/corelib/tools/qvsnprintf.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qvsnprintf.cpp + +qbytearraymatcher.o: $(SOURCE_PATH)/src/corelib/tools/qbytearraymatcher.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qbytearraymatcher.cpp + +qutfcodec.o: $(SOURCE_PATH)/src/corelib/codecs/qutfcodec.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/codecs/qutfcodec.cpp + +qstring.o: $(SOURCE_PATH)/src/corelib/tools/qstring.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qstring.cpp + +qlocale.o: $(SOURCE_PATH)/src/corelib/tools/qlocale.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlocale.cpp + +qlocale_tools.o: $(SOURCE_PATH)/src/corelib/tools/qlocale_tools.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlocale_tools.cpp + +qlocale_win.o: $(SOURCE_PATH)/src/corelib/tools/qlocale_win.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlocale_win.cpp + +quuid.o: $(SOURCE_PATH)/src/corelib/plugin/quuid.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/plugin/quuid.cpp + +qbuffer.o: $(SOURCE_PATH)/src/corelib/io/qbuffer.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qbuffer.cpp + +qlist.o: $(SOURCE_PATH)/src/corelib/tools/qlist.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlist.cpp + +qlinkedlist.o: $(SOURCE_PATH)/src/corelib/tools/qlinkedlist.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qlinkedlist.cpp + +qfile.o: $(SOURCE_PATH)/src/corelib/io/qfile.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfile.cpp + +qtemporaryfile.o: $(SOURCE_PATH)/src/corelib/io/qtemporaryfile.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qtemporaryfile.cpp + +qabstractfileengine.o: $(SOURCE_PATH)/src/corelib/io/qabstractfileengine.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qabstractfileengine.cpp + +qfilesystementry.o: $(SOURCE_PATH)/src/corelib/io/qfilesystementry.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystementry.cpp + +qfilesystemengine.o: $(SOURCE_PATH)/src/corelib/io/qfilesystemengine.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystemengine.cpp + +qfilesystemengine_win.o: $(SOURCE_PATH)/src/corelib/io/qfilesystemengine_win.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystemengine_win.cpp + +qfilesystemiterator_win.o: $(SOURCE_PATH)/src/corelib/io/qfilesystemiterator_win.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfilesystemiterator_win.cpp + +qfsfileengine_win.o: $(SOURCE_PATH)/src/corelib/io/qfsfileengine_win.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfsfileengine_win.cpp + +qfsfileengine.o: $(SOURCE_PATH)/src/corelib/io/qfsfileengine.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfsfileengine.cpp + +qfsfileengine_iterator.o: $(SOURCE_PATH)/src/corelib/io/qfsfileengine_iterator.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfsfileengine_iterator.cpp + +qtextcodec.o: $(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp + +qregexp.o: $(SOURCE_PATH)/src/corelib/tools/qregexp.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qregexp.cpp + +qvector.o: $(SOURCE_PATH)/src/corelib/tools/qvector.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qvector.cpp + +qbitarray.o: $(SOURCE_PATH)/src/corelib/tools/qbitarray.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qbitarray.cpp + +qdir.o: $(SOURCE_PATH)/src/corelib/io/qdir.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qdir.cpp + +qdiriterator.o: $(SOURCE_PATH)/src/corelib/io/qdiriterator.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qdiriterator.cpp + +qmetatype.o: $(SOURCE_PATH)/src/corelib/kernel/qmetatype.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/kernel/qmetatype.cpp + +qfileinfo.o: $(SOURCE_PATH)/src/corelib/io/qfileinfo.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/io/qfileinfo.cpp + +qdatetime.o: $(SOURCE_PATH)/src/corelib/tools/qdatetime.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qdatetime.cpp + +qstringlist.o: $(SOURCE_PATH)/src/corelib/tools/qstringlist.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qstringlist.cpp + +qsystemerror.o: $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp + +qsystemlibrary.o: $(SOURCE_PATH)/src/corelib/plugin/qsystemlibrary.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/plugin/qsystemlibrary.cpp + +qmap.o: $(SOURCE_PATH)/src/corelib/tools/qmap.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/tools/qmap.cpp + +makefile.o: $(SOURCE_PATH)/qmake/generators/makefile.cpp + $(CXX) $(CXXFLAGS) generators/makefile.cpp + +unixmake.o: $(SOURCE_PATH)/qmake/generators/unix/unixmake.cpp + $(CXX) $(CXXFLAGS) generators/unix/unixmake.cpp + +unixmake2.o: $(SOURCE_PATH)/qmake/generators/unix/unixmake2.cpp + $(CXX) $(CXXFLAGS) generators/unix/unixmake2.cpp + +winmakefile.o: $(SOURCE_PATH)/qmake/generators/win32/winmakefile.cpp + $(CXX) $(CXXFLAGS) generators/win32/winmakefile.cpp + +borland_bmake.o: $(SOURCE_PATH)/qmake/generators/win32/borland_bmake.cpp + $(CXX) $(CXXFLAGS) generators/win32/borland_bmake.cpp + +mingw_make.o: $(SOURCE_PATH)/qmake/generators/win32/mingw_make.cpp + $(CXX) $(CXXFLAGS) generators/win32/mingw_make.cpp + +msvc_nmake.o: $(SOURCE_PATH)/qmake/generators/win32/msvc_nmake.cpp + $(CXX) $(CXXFLAGS) generators/win32/msvc_nmake.cpp + +msvc_vcproj.o: $(SOURCE_PATH)/qmake/generators/win32/msvc_vcproj.cpp + $(CXX) $(CXXFLAGS) generators/win32/msvc_vcproj.cpp + +msvc_objectmodel.o: $(SOURCE_PATH)/qmake/generators/win32/msvc_objectmodel.cpp + $(CXX) $(CXXFLAGS) generators/win32/msvc_objectmodel.cpp + +msvc_vcxproj.o: $(SOURCE_PATH)/qmake/generators/win32/msvc_vcxproj.cpp + $(CXX) $(CXXFLAGS) generators/win32/msvc_vcxproj.cpp + +msbuild_objectmodel.o: $(SOURCE_PATH)/qmake/generators/win32/msbuild_objectmodel.cpp + $(CXX) $(CXXFLAGS) generators/win32/msbuild_objectmodel.cpp + +symmake.o: $(SOURCE_PATH)/qmake/generators/symbian/symmake.cpp + $(CXX) $(CXXFLAGS) generators/symbian/symmake.cpp + +symmake_abld.o: $(SOURCE_PATH)/qmake/generators/symbian/symmake_abld.cpp + $(CXX) $(CXXFLAGS) generators/symbian/symmake_abld.cpp + +symmake_sbsv2.o: $(SOURCE_PATH)/qmake/generators/symbian/symmake_sbsv2.cpp + $(CXX) $(CXXFLAGS) generators/symbian/symmake_sbsv2.cpp + +symbiancommon.o: $(SOURCE_PATH)/qmake/generators/symbian/symbiancommon.cpp + $(CXX) $(CXXFLAGS) generators/symbian/symbiancommon.cpp + +initprojectdeploy_symbian.o: $(SOURCE_PATH)/qmake/generators/symbian/initprojectdeploy_symbian.cpp + $(CXX) $(CXXFLAGS) generators/symbian/initprojectdeploy_symbian.cpp + +registry.o: $(SOURCE_PATH)/tools/shared/windows/registry.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/windows/registry.cpp + +epocroot.o: $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + +gbuild.o: $(SOURCE_PATH)/qmake/generators/integrity/gbuild.cpp + $(CXX) $(CXXFLAGS) generators/integrity/gbuild.cpp + + +project.o: $(SOURCE_PATH)/qmake/project.cpp $(SOURCE_PATH)/qmake/project.h $(SOURCE_PATH)/qmake/option.h + $(CXX) $(CXXFLAGS) project.cpp + +meta.o: $(SOURCE_PATH)/qmake/meta.cpp $(SOURCE_PATH)/qmake/project.h $(SOURCE_PATH)/qmake/option.h + $(CXX) $(CXXFLAGS) meta.cpp + +main.o: $(SOURCE_PATH)/qmake/main.cpp $(SOURCE_PATH)/qmake/project.h + $(CXX) $(CXXFLAGS) main.cpp + +option.o: $(SOURCE_PATH)/qmake/option.cpp $(SOURCE_PATH)/qmake/option.h + $(CXX) $(CXXFLAGS) option.cpp + +property.o: $(SOURCE_PATH)/qmake/property.cpp $(SOURCE_PATH)/qmake/project.h $(SOURCE_PATH)/qmake/option.h + $(CXX) $(CXXFLAGS) property.cpp + +projectgenerator.o: $(SOURCE_PATH)/qmake/generators/projectgenerator.cpp + $(CXX) $(CXXFLAGS) generators/projectgenerator.cpp + +pbuilder_pbx.o: $(SOURCE_PATH)/qmake/generators/mac/pbuilder_pbx.cpp + $(CXX) $(CXXFLAGS) generators/mac/pbuilder_pbx.cpp + +makefiledeps.o: $(SOURCE_PATH)/qmake/generators/makefiledeps.cpp + $(CXX) $(CXXFLAGS) generators/makefiledeps.cpp + +metamakefile.o: $(SOURCE_PATH)/qmake/generators/metamakefile.cpp $(SOURCE_PATH)/qmake/generators/symbian/symbian_makefile.h + $(CXX) $(CXXFLAGS) generators/metamakefile.cpp + +xmloutput.o: $(SOURCE_PATH)/qmake/generators/xmloutput.cpp + $(CXX) $(CXXFLAGS) generators/xmloutput.cpp + +qxmlstream.o: $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp + +qxmlutils.o: $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp diff --git a/qmake/cachekeys.h b/qmake/cachekeys.h new file mode 100644 index 0000000000..b29e4f211f --- /dev/null +++ b/qmake/cachekeys.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CACHEKEYS_H +#define CACHEKEYS_H + +#include "project.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// ------------------------------------------------------------------------------------------------- +struct FixStringCacheKey +{ + mutable uint 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 uint hashCode() const { + if(!hash) + hash = qHash(string) | qHash(flags) /*| qHash(pwd)*/; + return hash; + } +}; +inline uint qHash(const FixStringCacheKey &f) { return f.hashCode(); } + +// ------------------------------------------------------------------------------------------------- +struct FileInfoCacheKey +{ + mutable uint 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 uint hashCode() const { + if(!hash) + hash = qHash(file) /*| qHash(pwd)*/; + return hash; + } + inline bool isRelativePath(const QString &file) { + int length = file.length(); + if (!length) + return true; + + const QChar c0 = file.at(0); + const QChar c1 = length >= 2 ? file.at(1) : QChar(0); + return !(c0 == QLatin1Char('/') + || c0 == QLatin1Char('\\') + || (c0.isLetter() && c1 == QLatin1Char(':')) + || (c0 == QLatin1Char('/') && c1 == QLatin1Char('/')) + || (c0 == QLatin1Char('\\') && c1 == QLatin1Char('\\'))); + } +}; +inline uint qHash(const FileInfoCacheKey &f) { return f.hashCode(); } + +// ------------------------------------------------------------------------------------------------- +template +inline void qmakeDeleteCacheClear(void *i) { delete reinterpret_cast(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 diff --git a/qmake/generators/integrity/gbuild.cpp b/qmake/generators/integrity/gbuild.cpp new file mode 100644 index 0000000000..17bd8f0e5a --- /dev/null +++ b/qmake/generators/integrity/gbuild.cpp @@ -0,0 +1,442 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "gbuild.h" +#include "option.h" +#include "meta.h" +#include +#include +#include +#include +#include +#include +#ifdef Q_OS_UNIX +# include +# include +#endif + +QT_BEGIN_NAMESPACE + +unsigned int dllbase = 0x01000000; +#define DLLOFFSET 0x600000 + +GBuildMakefileGenerator::GBuildMakefileGenerator() : MakefileGenerator() +{ + nativebins << "moc" << "rcc" << "uic" << "bootstrap"; +} + +bool +GBuildMakefileGenerator::write() +{ + QStringList tmp; + QString filename(Option::output.fileName()); + QString pathtoremove(qmake_getpwd()); + QString relpath(pathtoremove); + QString strtarget(project->first("TARGET")); + bool isnativebin = nativebins.contains(strtarget); + relpath.replace(Option::output_dir, ""); + + /* correct output for non-prl, non-recursive case */ + QString outname(qmake_getpwd()); + outname += QDir::separator(); + outname += fileInfo(Option::output.fileName()).baseName(); + outname += projectSuffix(); + Option::output.close(); + Option::output.setFileName(outname); + MakefileGenerator::openOutput(Option::output, QString()); + + if (strtarget != fileInfo(project->projectFile()).baseName()) { + QString gpjname(strtarget); + QString outputName(qmake_getpwd()); + outputName += QDir::separator(); + outputName += fileInfo(project->projectFile()).baseName(); + outputName += projectSuffix(); + QFile f(outputName); + f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate); + QTextStream t(&f); + t << "#!gbuild\n"; + t << "[Project]\n"; + t << gpjname << projectSuffix() << "\n"; + if ((project->first("TEMPLATE") == "lib") + && project->isActiveConfig("shared")) + t << gpjname << "_shared" << projectSuffix() << "\n"; + t.flush(); + gpjname += projectSuffix(); + Option::output.close(); + Option::output.setFileName(gpjname); + MakefileGenerator::openOutput(Option::output, QString()); + } + + if ((project->first("TEMPLATE") == "app") + && (!isnativebin)) { + QTextStream t(&Option::output); + QString intname(strtarget); + intname += ".int"; + /* this is for bulding an INTEGRITY application. + * generate the .int integrate file and the .gpj INTEGRITY Application + * project file, then go on with regular files */ + t << "#!gbuild" << "\n"; + t << "[INTEGRITY Application]" << "\n"; + t << "\t:binDirRelative=.\n"; + t << "\t-o " << strtarget << "\n"; + t << intname << "\n"; + t << strtarget << "_app" << projectSuffix() << "\n"; + t.flush(); + + /* generate integrate file */ + QFile f(intname); + f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate); + QTextStream ti(&f); + ti << "# This is a file automatically generated by qmake" << "\n"; + ti << "# Modifications will be lost next time you run qmake" << "\n"; + ti << "Kernel" << "\n"; + ti << "\tFilename\tDynamicDownload" << "\n"; + ti << "EndKernel" << "\n" << "\n"; + ti << "AddressSpace" << "\n"; + ti << "\tName\t" << strtarget << "\n"; + ti << "\tFilename\t" << strtarget << "_app" << "\n"; + ti << "\tMemoryPoolSize\t0x100000" << "\n"; + ti << "\tLanguage\tC++" << "\n"; + /* FIXME : heap size is huge to be big enough for every example + * it should probably be tailored for each example, btu there is no + * good way to guess that */ + ti << "\tHeapSize\t0x00D00000" << "\n"; + ti << "\tTask\tInitial" << "\n"; + ti << "\t\tStackSize\t0x30000" << "\n"; + ti << "\tEndTask" << "\n"; + ti << "EndAddressSpace" << "\n"; + ti.flush(); + + /* change current project file to _app.gpj and continue + * generation */ + filename.insert(filename.lastIndexOf("."), "_app"); + Option::output.close(); + Option::output.setFileName(filename); + MakefileGenerator::openOutput(Option::output, QString()); + } else if ((project->first("TEMPLATE") == "lib") + && project->isActiveConfig("shared")) { + QString gpjname(strtarget); + gpjname += "_shared"; + gpjname += projectSuffix(); + QFile f(gpjname); + f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate); + QTextStream t(&f); + t << "#!gbuild\n" + "[Program]\n" + "\t-A libINTEGRITY.so\n" + "\t-A libc.so\n" + "\t-A libscxx.so\n" + "\t-A libQtCore.so\n" + "\t-e __ghsbegin_text\n" + "\t-startfile=-\n" + "\t:syslibraries=-\n" + "\t-Onolink\n"; + t << "\t-o lib" << strtarget << ".so\n"; + t << "\t-l" << strtarget << "\n"; + t << "\t-extractall=-l" << strtarget << "\n"; + t << "\t:outputDir=work/" << filename.section(QDir::separator(), 0, -1).remove(".gpj") << "\n"; + t << strtarget << "_shared.ld\n"; + t << "$(__OS_DIR)/intlib/sharedobjbssinit.c\n"; + t.flush(); + + QFile fl(strtarget + "_shared.ld"); + fl.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate); + QTextStream tl(&fl); + tl << "CONSTANTS {\n" + " __INTEGRITY_MinPageAlign = 16K\n" + " __INTEGRITY_MaxPageAlign = 16K\n" + " __INTEGRITY_LibCBaseAddress = \n"; + tl << dllbase << "\n"; + tl << "}\n" + "-sec\n" + "{\n" + " .picbase __INTEGRITY_LibCBaseAddress :\n" + " .text :\n" + " .syscall :\n" + " .intercall :\n" + " .interfunc :\n" + " .secinfo :\n" + " .rodata align(16) :\n" + " .fixaddr :\n" + " .fixtype :\n" + " .rombeg :\n" + " .textchecksum :\n" + " // The above sections may be large. Leave a bigger gap for large pages.\n" + " .pidbase align(__INTEGRITY_MaxPageAlign) :\n" + " .sdabase :\n" + " .data :\n" + " .toc :\n" + " .opd :\n" + " .datachecksum :\n" + " .bss align(__INTEGRITY_MinPageAlign) :\n" + " .heap :\n" + "}\n"; + tl.flush(); + dllbase += DLLOFFSET; + } + + warn_msg(WarnParser, Option::output.fileName().toAscii()); + QTextStream t(&Option::output); + QString primaryTarget(project->values("QMAKE_CXX").at(0)); + + pathtoremove += QDir::separator(); + filename.remove(qmake_getpwd()); + + //HEADER + t << "#!gbuild" << "\n"; + + /* find the architecture out of the compiler name */ + if (filename.endsWith("projects.gpj")) { + primaryTarget.remove(0, 5); + t << "macro QT_BUILD_DIR=%expand_path(.)\n"; + t << "macro __OS_DIR=" << project->values("INTEGRITY_DIR").first() << "\n"; + t << "primaryTarget=" << primaryTarget << "_integrity.tgt" << "\n"; + t << "customization=util/integrity/qt.bod\n"; + } + /* project type */ + if (project->first("TEMPLATE") == "app") { + t << "[Program]" << "\n"; + if (isnativebin) { + t << "\t:binDir=bin\n"; + t << "\t-o " << strtarget << "\n"; + } else { + t << "\t:binDirRelative=.\n"; + t << "\t-o " << strtarget << "_app\n"; + } + } else if (project->first("TEMPLATE") == "lib") { + t << "[Library]" << "\n"; + t << "\t:binDir=lib" << "\n"; + t << "\t-o lib" << strtarget << ".a" << "\n"; + } else if (project->first("TEMPLATE") == "subdirs") + t << "[Project]" << "\n"; + else + t << project->first("TEMPLATE") << "\n"; + + /* compilations options */ + t << "\t:sourceDir=." << "\n"; + + t << "\t:outputDir=work" << relpath << "\n"; + if (filename.endsWith("projects.gpj")) { + t << "\t:sourceDir=work\n"; + t << "\t-Iwork\n"; + t << "\t-Llib\n"; + t << "\t"; + QStringList &l = project->values("QMAKE_CXXFLAGS"); + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + if ((*it).startsWith("-")) + t << "\n" << "\t" << (*it); + else + t << " " << (*it); + } + t << "\n"; + } + t << "\n"; + + t << varGlue("DEFINES", "\t-D", "\n\t-D", "\n"); + + t << "\t-I.\n\t-I" << specdir() << "\n"; + t << varGlue("INCLUDEPATH", "\t-I", "\n\t-I", "\n"); + t << "\t--cxx_include_directory .\n\t--cxx_include_directory " << specdir() << "\n"; + t << varGlue("INCLUDEPATH", "\t--cxx_include_directory ", "\n\t--cxx_include_directory ", "\n"); + + if (project->first("TEMPLATE") == "app") { + /* include linker flags if it's an application */ + QString src[] = { "QMAKE_LFLAGS", "QMAKE_FRAMEWORKPATH_FLAGS", "QMAKE_LIBDIR_FLAGS", "QMAKE_LIBS", "LIBS", QString() }; + for (int i = 0; !src[i].isNull(); i++) { + /* skip target libraries for native tools */ + if (isnativebin && (i == 0)) + continue; + t << "\t"; + QStringList &l = project->values(src[i]); + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + if ((*it).startsWith("-")) + t << "\n" << "\t" << (*it); + else + t << " " << (*it); + } + t << "\n"; + } + } + + /* first subdirectories/subprojects */ + { + QStringList &l = project->values("SUBDIRS"); + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + QString gpjname((*it)); + /* avoid native tools */ + if (nativebins.contains(gpjname.section("_", -1))) + continue; + if (!project->first((*it) + ".subdir").isEmpty()) + gpjname = project->first((*it) + ".subdir"); + else + gpjname.replace("_", QDir::separator()); + gpjname += QDir::separator() + gpjname.section(QDir::separator(), -1); + gpjname += projectSuffix(); + /* make relative */ + if (!project->values("QT_SOURCE_TREE").isEmpty()) { + gpjname.replace(project->values("QT_SOURCE_TREE").first() + QDir::separator(), ""); + } + t << gpjname << "\n"; + } + } + + { + QStringList &l = project->values("RESOURCES"); + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + QString tmpstr((*it).replace(pathtoremove, "")); + t << tmpstr << "\t[Qt Resource]\n"; + tmpstr = tmpstr.section(".", -2, -1).section(QDir::separator(), -1); + tmpstr.remove(".qrc"); + t << "\t-name " << tmpstr << "\n"; + tmpstr.insert(tmpstr.lastIndexOf(QDir::separator()) + 1, "qrc_"); + tmpstr.append(".cpp"); + t << "\t-o work/" << tmpstr << "\n"; + } + } + { + QStringList &l = project->values("FORMS"); + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + QString tmpstr((*it).replace(pathtoremove, "")); + t << tmpstr << "\t[Qt Dialog]\n"; + tmpstr = tmpstr.section(".", 0, 0).section(QDir::separator(), -1); + tmpstr.insert(tmpstr.lastIndexOf(QDir::separator()) + 1, "ui_"); + tmpstr.remove(".ui"); + tmpstr.append(".h"); + t << "\t-o work/" << tmpstr << "\n"; + } + } + + /* source files for this project */ + QString src[] = { "HEADERS", "SOURCES", QString() }; + for (int i = 0; !src[i].isNull(); i++) { + QStringList &l = project->values(src[i]); + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + if ((*it).isEmpty()) + continue; + /* native tools aren't preprocessed */ + if (!isnativebin) + t << writeOne((*it), pathtoremove); + else + t << (*it).remove(pathtoremove) << "\n"; + } + } + t << "\n"; + + { + QStringList &l = project->values("GENERATED_SOURCES"); + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + t << "work/" << (*it).section(QDir::separator(), -1) << "\n"; + } + } + + return true; +} + +QString GBuildMakefileGenerator::writeOne(QString filename, QString pathtoremove) +{ + QString s(""); + s += filename.remove(pathtoremove); + if (filename.endsWith(Option::h_ext.first())) { + QString corename(filename.section(QDir::separator(), -1)); + corename.remove(Option::h_ext.first()); + corename.append(Option::cpp_ext.first()); + corename.prepend(Option::h_moc_mod); + s += "\t[MOC/Qt Header]\n"; + s += "\t-o "; + s += "work/"; + s += corename; + s += "\n"; + } else if (filename.section(QDir::separator(), -1).startsWith("qrc_")) { + QString tmpstr(filename.section("/", -1).section(".", 0, -1).remove("qrc_").remove(".cpp")); + s += "\n\t:depends="; + s += tmpstr; + s += ".qrc"; + s += "\n"; + } else if (filename.endsWith(Option::cpp_ext.first())) { + QString tmpstr(filename.section("/", -1)); +// QString moctool(project->values("QMAKE_MOC").first()); + QString filepath(pathtoremove); + if (!project->values("QT_SOURCE_TREE").isEmpty()) { + filepath.remove(project->values("QT_SOURCE_TREE").first()); + filepath.remove(0, 1); + } +// if (!project->values("QT_BUILD_TREE").isEmpty()) { +// moctool.remove(project->values("QT_BUILD_TREE").first()); +// moctool.remove(0, 1); +// } + s += "\n\t:preexecShellSafe='${QT_BUILD_DIR}/bin/moc "; +// s += moctool; +// s += " "; + s += varGlue("DEFINES", "-D", " -D", " "); + s += varGlue("INCLUDEPATH", "-I", " -I", " "); + s += filepath; + s += filename; + s += " -o "; + tmpstr.replace(Option::cpp_ext.first(), Option::cpp_moc_ext); + s += "work/"; + s += tmpstr; + s += "\n"; + } else + s += "\n"; + return s; +} + +bool +GBuildMakefileGenerator::openOutput(QFile &file, const QString &build) const +{ + debug_msg(1, "file is %s", file.fileName().toLatin1().constData()); + QFileInfo fi(file); + if (fi.filePath().isEmpty()) + file.setFileName(qmake_getpwd() + QDir::separator() + file.fileName()); + if (!file.fileName().endsWith(projectSuffix())) { + QString outputName(file.fileName()); + outputName += QDir::separator(); + outputName += fileInfo(project->projectFile()).baseName(); + outputName += projectSuffix(); + warn_msg(WarnParser, outputName.toAscii()); + file.setFileName(outputName); + } + debug_msg(1, "file is %s", file.fileName().toLatin1().constData()); + bool ret = MakefileGenerator::openOutput(file, QString()); + return ret; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/integrity/gbuild.h b/qmake/generators/integrity/gbuild.h new file mode 100644 index 0000000000..6b5b1a0a91 --- /dev/null +++ b/qmake/generators/integrity/gbuild.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GBUILD_H +#define GBUILD_H + +#include "makefile.h" + +QT_BEGIN_NAMESPACE + +class GBuildMakefileGenerator : public MakefileGenerator +{ + virtual bool write(); + + QString projectSuffix() const { return QString(".gpj"); }; + QString writeOne(QString filename, QString pathtoremove = ""); + +public: + GBuildMakefileGenerator(); + ~GBuildMakefileGenerator(); + + virtual bool supportsMetaBuild() { return false; } + virtual bool openOutput(QFile &, const QString &) const; +protected: + bool doPrecompiledHeaders() const { return false; } + virtual bool doDepends() const { return true; } + QStringList nativebins; + +}; + +inline GBuildMakefileGenerator::~GBuildMakefileGenerator() +{ } + +QT_END_NAMESPACE + +#endif // GBUILD_H diff --git a/qmake/generators/mac/pbuilder_pbx.cpp b/qmake/generators/mac/pbuilder_pbx.cpp new file mode 100644 index 0000000000..19667cdbe4 --- /dev/null +++ b/qmake/generators/mac/pbuilder_pbx.cpp @@ -0,0 +1,1861 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pbuilder_pbx.h" +#include "option.h" +#include "meta.h" +#include +#include +#include +#include +#include +#include +#ifdef Q_OS_UNIX +# include +# include +#endif +#ifdef Q_OS_DARWIN +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +//#define GENERATE_AGGREGRATE_SUBDIR + +// Note: this is fairly hacky, but it does the job... + +static QString qtMD5(const QByteArray &src) +{ + QByteArray digest = QCryptographicHash::hash(src, QCryptographicHash::Md5); + return QString::fromLatin1(digest.toHex()); +} + +ProjectBuilderMakefileGenerator::ProjectBuilderMakefileGenerator() : UnixMakefileGenerator() +{ + +} + +bool +ProjectBuilderMakefileGenerator::writeMakefile(QTextStream &t) +{ + writingUnixMakefileGenerator = false; + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + /* for now just dump, I need to generated an empty xml or something.. */ + fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n", + var("QMAKE_FAILED_REQUIREMENTS").toLatin1().constData()); + return true; + } + + project->values("MAKEFILE").clear(); + project->values("MAKEFILE").append("Makefile"); + if(project->first("TEMPLATE") == "app" || project->first("TEMPLATE") == "lib") + return writeMakeParts(t); + else if(project->first("TEMPLATE") == "subdirs") + return writeSubDirs(t); + return false; +} + +struct ProjectBuilderSubDirs { + QMakeProject *project; + QString subdir; + bool autoDelete; + ProjectBuilderSubDirs(QMakeProject *p, QString s, bool a=true) : project(p), subdir(s), autoDelete(a) { } + ~ProjectBuilderSubDirs() { + if(autoDelete) + delete project; + } +}; + +bool +ProjectBuilderMakefileGenerator::writeSubDirs(QTextStream &t) +{ + if(project->isActiveConfig("generate_pbxbuild_makefile")) { + QString mkwrap = fileFixify(pbx_dir + Option::dir_sep + ".." + Option::dir_sep + project->first("MAKEFILE"), + qmake_getpwd()); + QFile mkwrapf(mkwrap); + if(mkwrapf.open(QIODevice::WriteOnly | QIODevice::Text)) { + debug_msg(1, "pbuilder: Creating file: %s", mkwrap.toLatin1().constData()); + QTextStream mkwrapt(&mkwrapf); + writingUnixMakefileGenerator = true; + UnixMakefileGenerator::writeSubDirs(mkwrapt); + writingUnixMakefileGenerator = false; + } + } + + //HEADER + const int pbVersion = pbuilderVersion(); + t << "// !$*UTF8*$!" << "\n" + << "{" << "\n" + << "\t" << writeSettings("archiveVersion", "1", SettingsNoQuote) << ";" << "\n" + << "\t" << "classes = {" << "\n" << "\t" << "};" << "\n" + << "\t" << writeSettings("objectVersion", QString::number(pbVersion), SettingsNoQuote) << ";" << "\n" + << "\t" << "objects = {" << endl; + + //SUBDIRS + QList pb_subdirs; + pb_subdirs.append(new ProjectBuilderSubDirs(project, QString(), false)); + QString oldpwd = qmake_getpwd(); + QMap groups; + for(int pb_subdir = 0; pb_subdir < pb_subdirs.size(); ++pb_subdir) { + ProjectBuilderSubDirs *pb = pb_subdirs[pb_subdir]; + const QStringList subdirs = pb->project->values("SUBDIRS"); + for(int subdir = 0; subdir < subdirs.count(); subdir++) { + QString tmp = subdirs[subdir]; + if(!pb->project->isEmpty(tmp + ".file")) + tmp = pb->project->first(tmp + ".file"); + else if(!pb->project->isEmpty(tmp + ".subdir")) + tmp = pb->project->first(tmp + ".subdir"); + if(fileInfo(tmp).isRelative() && !pb->subdir.isEmpty()) { + QString subdir = pb->subdir; + if(!subdir.endsWith(Option::dir_sep)) + subdir += Option::dir_sep; + tmp = subdir + tmp; + } + QFileInfo fi(fileInfo(Option::fixPathToLocalOS(tmp, true))); + if(fi.exists()) { + if(fi.isDir()) { + QString profile = tmp; + if(!profile.endsWith(Option::dir_sep)) + profile += Option::dir_sep; + profile += fi.baseName() + Option::pro_ext; + fi = QFileInfo(profile); + } + QMakeProject tmp_proj; + QString dir = fi.path(), fn = fi.fileName(); + if(!dir.isEmpty()) { + if(!qmake_setpwd(dir)) + fprintf(stderr, "Cannot find directory: %s\n", dir.toLatin1().constData()); + } + if(tmp_proj.read(fn)) { + if(Option::debug_level) { + debug_msg(1, "Dumping all variables:"); + QMap &vars = tmp_proj.variables(); + for(QMap::Iterator it = vars.begin(); + it != vars.end(); ++it) { + if(it.key().left(1) != "." && !it.value().isEmpty()) + debug_msg(1, "%s: %s === %s", fn.toLatin1().constData(), it.key().toLatin1().constData(), + it.value().join(" :: ").toLatin1().constData()); + } + } + if(tmp_proj.first("TEMPLATE") == "subdirs") { + QMakeProject *pp = new QMakeProject(&tmp_proj); + pp->read(0); + pb_subdirs += new ProjectBuilderSubDirs(pp, dir); + } else if(tmp_proj.first("TEMPLATE") == "app" || tmp_proj.first("TEMPLATE") == "lib") { + QString pbxproj = qmake_getpwd() + Option::dir_sep + tmp_proj.first("TARGET") + projectSuffix(); + if(!exists(pbxproj)) { + warn_msg(WarnLogic, "Ignored (not found) '%s'", pbxproj.toLatin1().constData()); + goto nextfile; // # Dirty! + } + const QString project_key = keyFor(pbxproj + "_PROJECTREF"); + project->values("QMAKE_PBX_SUBDIRS") += pbxproj; + //PROJECTREF + { + bool in_root = true; + QString name = qmake_getpwd(); + if(project->isActiveConfig("flat")) { + QString flat_file = fileFixify(name, oldpwd, Option::output_dir, FileFixifyRelative); + if(flat_file.indexOf(Option::dir_sep) != -1) { + QStringList dirs = flat_file.split(Option::dir_sep); + name = dirs.back(); + } + } else { + QString flat_file = fileFixify(name, oldpwd, Option::output_dir, FileFixifyRelative); + if(QDir::isRelativePath(flat_file) && flat_file.indexOf(Option::dir_sep) != -1) { + QString last_grp("QMAKE_SUBDIR_PBX_HEIR_GROUP"); + QStringList dirs = flat_file.split(Option::dir_sep); + name = dirs.back(); + for(QStringList::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) { + QString new_grp(last_grp + Option::dir_sep + (*dir_it)), new_grp_key(keyFor(new_grp)); + if(dir_it == dirs.begin()) { + if(!groups.contains(new_grp)) + project->values("QMAKE_SUBDIR_PBX_GROUPS").append(new_grp_key); + } else { + if(!groups[last_grp].contains(new_grp_key)) + groups[last_grp] += new_grp_key; + } + last_grp = new_grp; + } + groups[last_grp] += project_key; + in_root = false; + } + } + if(in_root) + project->values("QMAKE_SUBDIR_PBX_GROUPS") += project_key; + t << "\t\t" << project_key << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("lastKnownFileType", "wrapper.pb-project") << ";" << "\n" + << "\t\t\t" << writeSettings("name", escapeFilePath(tmp_proj.first("TARGET") + projectSuffix())) << ";" << "\n" + << "\t\t\t" << writeSettings("path", pbxproj) << ";" << "\n" + << "\t\t\t" << writeSettings("refType", "0", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("sourceTree", "") << ";" << "\n" + << "\t\t" << "};" << "\n"; + //WRAPPER + t << "\t\t" << keyFor(pbxproj + "_WRAPPER") << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXReferenceProxy", SettingsNoQuote) << ";" << "\n"; + if(tmp_proj.first("TEMPLATE") == "app") { + t << "\t\t\t" << writeSettings("fileType", "wrapper.application") << ";" << "\n" + << "\t\t\t" << writeSettings("path", tmp_proj.first("TARGET") + ".app") << ";" << "\n"; + } else { + t << "\t\t\t" << writeSettings("fileType", "compiled.mach-o.dylib") << ";" << "\n" + << "\t\t\t" << writeSettings("path", tmp_proj.first("TARGET") + ".dylib") << ";" << "\n"; + } + t << "\t\t\t" << writeSettings("refType", "3", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("remoteRef", keyFor(pbxproj + "_WRAPPERREF")) << ";" << "\n" + << "\t\t\t" << writeSettings("sourceTree", "BUILT_PRODUCTS_DIR", SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};" << "\n"; + t << "\t\t" << keyFor(pbxproj + "_WRAPPERREF") << " = {" << "\n" + << "\t\t\t" << writeSettings("containerPortal", project_key) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXContainerItemProxy", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("proxyType", "2") << ";" << "\n" +// << "\t\t\t" << writeSettings("remoteGlobalIDString", keyFor(pbxproj + "QMAKE_PBX_REFERENCE")) << ";" << "\n" + << "\t\t\t" << writeSettings("remoteGlobalIDString", keyFor(pbxproj + "QMAKE_PBX_REFERENCE!!!")) << ";" << "\n" + << "\t\t\t" << writeSettings("remoteInfo", tmp_proj.first("TARGET")) << ";" << "\n" + << "\t\t" << "};" << "\n"; + //PRODUCTGROUP + t << "\t\t" << keyFor(pbxproj + "_PRODUCTGROUP") << " = {" << "\n" + << "\t\t\t" << writeSettings("children", project->values(pbxproj + "_WRAPPER"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", "Products") << ";" << "\n" + << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("sourceTree", "") << ";" << "\n" + << "\t\t" << "};" << "\n"; + } +#ifdef GENERATE_AGGREGRATE_SUBDIR + //TARGET (for aggregate) + { + //container + const QString container_proxy = keyFor(pbxproj + "_CONTAINERPROXY"); + t << "\t\t" << container_proxy << " = {" << "\n" + << "\t\t\t" << writeSettings("containerPortal", project_key) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXContainerItemProxy", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("proxyType", "1") << ";" << "\n" + << "\t\t\t" << writeSettings("remoteGlobalIDString", keyFor(pbxproj + "QMAKE_PBX_TARGET")) << ";" << "\n" + << "\t\t\t" << writeSettings("remoteInfo", tmp_proj.first("TARGET")) << ";" << "\n" + << "\t\t" << "};" << "\n"; + //targetref + t << "\t\t" << keyFor(pbxproj + "_TARGETREF") << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXTargetDependency", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", fixForOutput(tmp_proj.first("TARGET") +" (from " + tmp_proj.first("TARGET") + projectSuffix() + ")")) << ";" << "\n" + << "\t\t\t" << writeSettings("targetProxy", container_proxy) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } +#endif + } + } + nextfile: + qmake_setpwd(oldpwd); + } + } + } + qDeleteAll(pb_subdirs); + pb_subdirs.clear(); + + for(QMap::Iterator grp_it = groups.begin(); grp_it != groups.end(); ++grp_it) { + t << "\t\t" << keyFor(grp_it.key()) << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("children", grp_it.value(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("name", escapeFilePath(grp_it.key().section(Option::dir_sep, -1))) << ";" << "\n" + << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + + //DUMP EVERYTHING THAT TIES THE ABOVE TOGETHER + //BUILDSTYLE + QString active_buildstyle; + for(int as_release = 0; as_release < 2; as_release++) + { + QMap settings; + settings.insert("COPY_PHASE_STRIP", (as_release ? "YES" : "NO")); + if(as_release) + settings.insert("GCC_GENERATE_DEBUGGING_SYMBOLS", "NO"); + if(project->isActiveConfig("sdk") && !project->isEmpty("QMAKE_MAC_SDK")) + settings.insert("SDKROOT", project->first("QMAKE_MAC_SDK")); + { + const QStringList &l = project->values("QMAKE_MAC_XCODE_SETTINGS"); + for(int i = 0; i < l.size(); ++i) { + QString name = l.at(i); + const QString value = project->values(name + QLatin1String(".value")).join(QString(Option::field_sep)); + if(!project->isEmpty(name + QLatin1String(".name"))) + name = project->values(name + QLatin1String(".name")).first(); + settings.insert(name, value); + } + } + + QString name; + if(pbVersion >= 42) + name = (as_release ? "Release" : "Debug"); + else + name = (as_release ? "Deployment" : "Development"); + if(pbVersion >= 42) { + QString key = keyFor("QMAKE_SUBDIR_PBX_BUILDCONFIG_" + name); + project->values("QMAKE_SUBDIR_PBX_BUILDCONFIGS").append(key); + t << "\t\t" << key << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "XCBuildConfiguration", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << "buildSettings = {" << "\n"; + for(QMap::Iterator set_it = settings.begin(); set_it != settings.end(); ++set_it) + t << "\t\t\t\t" << writeSettings(set_it.key(), set_it.value()) << ";" << "\n"; + t << "\t\t\t" << "};" << "\n" + << "\t\t\t" << writeSettings("name", name) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + + QString key = keyFor("QMAKE_SUBDIR_PBX_BUILDSTYLE_" + name); + if(project->isActiveConfig("debug") != (bool)as_release) { + project->values("QMAKE_SUBDIR_PBX_BUILDSTYLES").append(key); + active_buildstyle = name; + } else if(pbVersion >= 42) { + project->values("QMAKE_SUBDIR_PBX_BUILDSTYLES").append(key); + } + t << "\t\t" << key << " = {" << "\n" + << "\t\t\t" << writeSettings("buildRules", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << "buildSettings = {" << "\n"; + for(QMap::Iterator set_it = settings.begin(); set_it != settings.end(); ++set_it) + t << "\t\t\t\t" << writeSettings(set_it.key(), set_it.value()) << ";\n"; + t << "\t\t\t" << "};" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXBuildStyle", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", name) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + if(pbVersion >= 42) { + t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_BUILDCONFIG_LIST") << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "XCConfigurationList", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("buildConfigurations", project->values("QMAKE_SUBDIR_PBX_BUILDCONFIGS"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("defaultConfigurationIsVisible", "0", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("defaultConfigurationIsName", active_buildstyle) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + +#ifdef GENERATE_AGGREGRATE_SUBDIR + //target + t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_AGGREGATE_TARGET") << " = {" << "\n" + << "\t\t\t" << writeSettings("buildPhases", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << "buildSettings = {" << "\n" + << "\t\t\t\t" << writeSettings("PRODUCT_NAME", project->values("TARGET").first()) << ";" << "\n" + << "\t\t\t" << "};" << "\n"; + { + QStringList dependencies; + const QStringList &qmake_subdirs = project->values("QMAKE_PBX_SUBDIRS"); + for(int i = 0; i < qmake_subdirs.count(); i++) + dependencies += keyFor(qmake_subdirs[i] + "_TARGETREF"); + t << "\t\t\t" << writeSettings("dependencies", dependencies, SettingsAsList, 4) << ";" << "\n" + } + t << "\t\t\t" << writeSettings("isa", "PBXAggregateTarget", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", project->values("TARGET").first()) << ";" << "\n" + << "\t\t\t" << writeSettings("productName", project->values("TARGET").first()) << ";" << "\n" + << "\t\t" << "};" << "\n"; +#endif + + //ROOT_GROUP + t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_ROOT_GROUP") << " = {" << "\n" + << "\t\t\t" << writeSettings("children", project->values("QMAKE_SUBDIR_PBX_GROUPS"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("sourceTree", "") << ";" << "\n" + << "\t\t" << "};" << "\n"; + + + //ROOT + t << "\t\t" << keyFor("QMAKE_SUBDIR_PBX_ROOT") << " = {" << "\n" + << "\t\t\t" << "buildSettings = {" << "\n" + << "\t\t\t" << "};" << "\n" + << "\t\t\t" << writeSettings("buildStyles", project->values("QMAKE_SUBDIR_PBX_BUILDSTYLES"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXProject", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("mainGroup", keyFor("QMAKE_SUBDIR_PBX_ROOT_GROUP")) << ";" << "\n" + << "\t\t\t" << writeSettings("projectDirPath", QStringList()) << ";" << "\n"; + if(pbVersion >= 42) + t << "\t\t\t" << writeSettings("buildConfigurationList", keyFor("QMAKE_SUBDIR_PBX_BUILDCONFIG_LIST")) << ";" << "\n"; + t << "\t\t\t" << "projectReferences = (" << "\n"; + { + QStringList &qmake_subdirs = project->values("QMAKE_PBX_SUBDIRS"); + for(int i = 0; i < qmake_subdirs.count(); i++) { + QString subdir = qmake_subdirs[i]; + t << "\t\t\t\t" << "{" << "\n" + << "\t\t\t\t\t" << writeSettings("ProductGroup", keyFor(subdir + "_PRODUCTGROUP")) << ";" << "\n" + << "\t\t\t\t\t" << writeSettings("ProjectRef", keyFor(subdir + "_PROJECTREF")) << ";" << "\n" + << "\t\t\t\t" << "}," << "\n"; + } + } + t << "\t\t\t" << ");" << "\n" + << "\t\t\t" << writeSettings("targets", +#ifdef GENERATE_AGGREGRATE_SUBDIR + project->values("QMAKE_SUBDIR_AGGREGATE_TARGET"), +#else + QStringList(), +#endif + SettingsAsList, 4) << ";" << "\n" + << "\t\t" << "};" << "\n"; + + //FOOTER + t << "\t" << "};" << "\n" + << "\t" << writeSettings("rootObject", keyFor("QMAKE_SUBDIR_PBX_ROOT")) << ";" << "\n" + << "}" << endl; + + return true; +} + +class ProjectBuilderSources +{ + bool buildable, object_output; + QString key, group, compiler; +public: + ProjectBuilderSources(const QString &key, bool buildable=false, const QString &group=QString(), const QString &compiler=QString(), bool producesObject=false); + QStringList files(QMakeProject *project) const; + inline bool isBuildable() const { return buildable; } + inline QString keyName() const { return key; } + inline QString groupName() const { return group; } + inline QString compilerName() const { return compiler; } + inline bool isObjectOutput(const QString &file) const { + bool ret = object_output; + for(int i = 0; !ret && i < Option::c_ext.size(); ++i) { + if(file.endsWith(Option::c_ext.at(i))) { + ret = true; + break; + } + } + for(int i = 0; !ret && i < Option::cpp_ext.size(); ++i) { + if(file.endsWith(Option::cpp_ext.at(i))) { + ret = true; + break; + } + } + return ret; + } +}; + +ProjectBuilderSources::ProjectBuilderSources(const QString &k, bool b, + const QString &g, const QString &c, bool o) : buildable(b), object_output(o), key(k), group(g), compiler(c) +{ + if(group.isNull()) { + if(k == "SOURCES") + group = "Sources"; + else if(k == "HEADERS") + group = "Headers"; + else if(k == "QMAKE_INTERNAL_INCLUDED_FILES") + group = "Sources [qmake]"; + else if(k == "GENERATED_SOURCES" || k == "GENERATED_FILES") + group = "Temporary Sources"; + else + fprintf(stderr, "No group available for %s!\n", k.toLatin1().constData()); + } +} + +QStringList +ProjectBuilderSources::files(QMakeProject *project) const +{ + QStringList ret = project->values(key); + if(key == "QMAKE_INTERNAL_INCLUDED_FILES") { + QString pfile = project->projectFile(); + if(pfile != "(stdin)") + ret.prepend(pfile); + for(int i = 0; i < ret.size(); ++i) { + QStringList newret; + if(!ret.at(i).endsWith(Option::prf_ext)) + newret.append(ret.at(i)); + ret = newret; + } + } + if(key == "SOURCES" && project->first("TEMPLATE") == "app" && !project->isEmpty("ICON")) + ret.append(project->first("ICON")); + return ret; +} + + +bool +ProjectBuilderMakefileGenerator::writeMakeParts(QTextStream &t) +{ + QStringList tmp; + bool did_preprocess = false; + + //HEADER + const int pbVersion = pbuilderVersion(); + t << "// !$*UTF8*$!" << "\n" + << "{" << "\n" + << "\t" << writeSettings("archiveVersion", "1", SettingsNoQuote) << ";" << "\n" + << "\t" << "classes = {" << "\n" << "\t" << "};" << "\n" + << "\t" << writeSettings("objectVersion", QString::number(pbVersion), SettingsNoQuote) << ";" << "\n" + << "\t" << "objects = {" << endl; + + //MAKE QMAKE equivelant + if(!project->isActiveConfig("no_autoqmake") && project->projectFile() != "(stdin)") { + QString mkfile = pbx_dir + Option::dir_sep + "qt_makeqmake.mak"; + QFile mkf(mkfile); + if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) { + writingUnixMakefileGenerator = true; + debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData()); + QTextStream mkt(&mkf); + writeHeader(mkt); + mkt << "QMAKE = " << var("QMAKE_QMAKE") << endl; + writeMakeQmake(mkt); + mkt.flush(); + mkf.close(); + writingUnixMakefileGenerator = false; + } + QString phase_key = keyFor("QMAKE_PBX_MAKEQMAKE_BUILDPHASE"); + mkfile = fileFixify(mkfile, qmake_getpwd()); + project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key); + t << "\t\t" << phase_key << " = {" << "\n" + << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("files", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("generatedFileNames", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", "Qt Qmake") << ";" << "\n" + << "\t\t\t" << writeSettings("neededFileNames", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << ";" << "\n" + << "\t\t\t" << writeSettings("shellScript", fixForOutput("make -C " + escapeFilePath(qmake_getpwd()) + " -f '" + escapeFilePath(mkfile) + "'")) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + + //DUMP SOURCES + QMap groups; + QList sources; + sources.append(ProjectBuilderSources("SOURCES", true)); + sources.append(ProjectBuilderSources("GENERATED_SOURCES", true)); + sources.append(ProjectBuilderSources("GENERATED_FILES")); + sources.append(ProjectBuilderSources("HEADERS")); + sources.append(ProjectBuilderSources("QMAKE_INTERNAL_INCLUDED_FILES")); + if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) { + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + QString tmp_out = project->first((*it) + ".output"); + if(project->isEmpty((*it) + ".output")) + continue; + QString name = (*it); + if(!project->isEmpty((*it) + ".name")) + name = project->first((*it) + ".name"); + const QStringList &inputs = project->values((*it) + ".input"); + for(int input = 0; input < inputs.size(); ++input) { + if(project->isEmpty(inputs.at(input))) + continue; + bool duplicate = false; + for(int i = 0; i < sources.size(); ++i) { + if(sources.at(i).keyName() == inputs.at(input)) { + duplicate = true; + break; + } + } + if(!duplicate) { + bool isObj = project->values((*it) + ".CONFIG").indexOf("no_link") == -1; + const QStringList &outputs = project->values((*it) + ".variable_out"); + for(int output = 0; output < outputs.size(); ++output) { + if(outputs.at(output) != "OBJECT") { + isObj = false; + break; + } + } + sources.append(ProjectBuilderSources(inputs.at(input), true, + QString("Sources [") + name + "]", (*it), isObj)); + } + } + } + } + for(int source = 0; source < sources.size(); ++source) { + QStringList &src_list = project->values("QMAKE_PBX_" + sources.at(source).keyName()); + QStringList &root_group_list = project->values("QMAKE_PBX_GROUPS"); + + QStringList files = fileFixify(sources.at(source).files(project)); + for(int f = 0; f < files.count(); ++f) { + QString file = files[f]; + if(file.length() >= 2 && (file[0] == '"' || file[0] == '\'') && file[(int) file.length()-1] == file[0]) + file = file.mid(1, file.length()-2); + if(!sources.at(source).compilerName().isNull() && + !verifyExtraCompiler(sources.at(source).compilerName(), file)) + continue; + if(file.endsWith(Option::prl_ext)) + continue; + + bool in_root = true; + QString src_key = keyFor(file), name = file; + if(project->isActiveConfig("flat")) { + QString flat_file = fileFixify(file, qmake_getpwd(), Option::output_dir, FileFixifyRelative); + if(flat_file.indexOf(Option::dir_sep) != -1) { + QStringList dirs = flat_file.split(Option::dir_sep); + name = dirs.back(); + } + } else { + QString flat_file = fileFixify(file, qmake_getpwd(), Option::output_dir, FileFixifyRelative); + if(QDir::isRelativePath(flat_file) && flat_file.indexOf(Option::dir_sep) != -1) { + QString last_grp("QMAKE_PBX_" + sources.at(source).groupName() + "_HEIR_GROUP"); + QStringList dirs = flat_file.split(Option::dir_sep); + name = dirs.back(); + dirs.pop_back(); //remove the file portion as it will be added via src_key + for(QStringList::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) { + QString new_grp(last_grp + Option::dir_sep + (*dir_it)), new_grp_key(keyFor(new_grp)); + if(dir_it == dirs.begin()) { + if(!src_list.contains(new_grp_key)) + src_list.append(new_grp_key); + } else { + if(!groups[last_grp].contains(new_grp_key)) + groups[last_grp] += new_grp_key; + } + last_grp = new_grp; + } + groups[last_grp] += src_key; + in_root = false; + } + } + if(in_root) + src_list.append(src_key); + //source reference + t << "\t\t" << src_key << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", escapeFilePath(name)) << ";" << "\n" + << "\t\t\t" << writeSettings("path", escapeFilePath(file)) << ";" << "\n" + << "\t\t\t" << writeSettings("refType", QString::number(reftypeForFile(file)), SettingsNoQuote) << ";" << "\n"; + if(pbVersion >= 38) { + QString filetype; + for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) { + if(file.endsWith((*cppit))) { + filetype = "sourcecode.cpp.cpp"; + break; + } + } + if(!filetype.isNull()) + t << "\t\t\t" << writeSettings("lastKnownFileType", filetype) << ";" << "\n"; + } + t << "\t\t" << "};" << "\n"; + if(sources.at(source).isBuildable()) { //build reference + QString build_key = keyFor(file + ".BUILDABLE"); + t << "\t\t" << build_key << " = {" << "\n" + << "\t\t\t" << writeSettings("fileRef", src_key) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << "settings = {" << "\n" + << "\t\t\t\t" << writeSettings("ATTRIBUTES", QStringList(), SettingsAsList, 5) << ";" << "\n" + << "\t\t\t" << "};" << "\n" + << "\t\t" << "};" << "\n"; + if(sources.at(source).isObjectOutput(file)) + project->values("QMAKE_PBX_OBJ").append(build_key); + } + } + if(!src_list.isEmpty()) { + QString group_key = keyFor(sources.at(source).groupName()); + if(root_group_list.indexOf(group_key) == -1) + root_group_list += group_key; + + QStringList &group = groups[sources.at(source).groupName()]; + for(int src = 0; src < src_list.size(); ++src) { + if(group.indexOf(src_list.at(src)) == -1) + group += src_list.at(src); + } + } + } + for(QMap::Iterator grp_it = groups.begin(); grp_it != groups.end(); ++grp_it) { + t << "\t\t" << keyFor(grp_it.key()) << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("children", grp_it.value(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("name", escapeFilePath(grp_it.key().section(Option::dir_sep, -1))) << ";" << "\n" + << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + + //PREPROCESS BUILDPHASE (just a makefile) + { + QString mkfile = pbx_dir + Option::dir_sep + "qt_preprocess.mak"; + QFile mkf(mkfile); + if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) { + writingUnixMakefileGenerator = true; + did_preprocess = true; + debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData()); + QTextStream mkt(&mkf); + writeHeader(mkt); + mkt << "MOC = " << Option::fixPathToTargetOS(var("QMAKE_MOC")) << endl; + mkt << "UIC = " << Option::fixPathToTargetOS(var("QMAKE_UIC")) << endl; + mkt << "LEX = " << var("QMAKE_LEX") << endl; + mkt << "LEXFLAGS = " << var("QMAKE_LEXFLAGS") << endl; + mkt << "YACC = " << var("QMAKE_YACC") << endl; + mkt << "YACCFLAGS = " << var("QMAKE_YACCFLAGS") << endl; + mkt << "DEFINES = " + << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ") + << varGlue("DEFINES","-D"," -D","") << endl; + mkt << "INCPATH = " << "-I" << specdir(); + if(!project->isActiveConfig("no_include_pwd")) { + QString pwd = escapeFilePath(fileFixify(qmake_getpwd())); + if(pwd.isEmpty()) + pwd = "."; + mkt << " -I" << pwd; + } + { + const QStringList &incs = project->values("INCLUDEPATH"); + for(QStringList::ConstIterator incit = incs.begin(); incit != incs.end(); ++incit) + mkt << " " << "-I" << escapeFilePath((*incit)); + } + if(!project->isEmpty("QMAKE_FRAMEWORKPATH_FLAGS")) + mkt << " " << var("QMAKE_FRAMEWORKPATH_FLAGS"); + mkt << endl; + mkt << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl; + mkt << "MOVE = " << var("QMAKE_MOVE") << endl << endl; + mkt << "IMAGES = " << varList("QMAKE_IMAGE_COLLECTION") << endl; + mkt << "PARSERS ="; + if(!project->isEmpty("YACCSOURCES")) { + QStringList &yaccs = project->values("YACCSOURCES"); + for(QStringList::Iterator yit = yaccs.begin(); yit != yaccs.end(); ++yit) { + QFileInfo fi(fileInfo((*yit))); + mkt << " " << fi.path() << Option::dir_sep << fi.baseName() + << Option::yacc_mod << Option::cpp_ext.first(); + } + } + if(!project->isEmpty("LEXSOURCES")) { + QStringList &lexs = project->values("LEXSOURCES"); + for(QStringList::Iterator lit = lexs.begin(); lit != lexs.end(); ++lit) { + QFileInfo fi(fileInfo((*lit))); + mkt << " " << fi.path() << Option::dir_sep << fi.baseName() + << Option::lex_mod << Option::cpp_ext.first(); + } + } + mkt << "\n"; + mkt << "preprocess: $(PARSERS) compilers" << endl; + mkt << "clean preprocess_clean: parser_clean compiler_clean" << endl << endl; + mkt << "parser_clean:" << "\n"; + if(!project->isEmpty("YACCSOURCES") || !project->isEmpty("LEXSOURCES")) + mkt << "\t-rm -f $(PARSERS)" << "\n"; + writeExtraTargets(mkt); + if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) { + mkt << "compilers:"; + const QStringList &compilers = project->values("QMAKE_EXTRA_COMPILERS"); + for(int compiler = 0; compiler < compilers.size(); ++compiler) { + QString tmp_out = project->first(compilers.at(compiler) + ".output"); + if(project->isEmpty(compilers.at(compiler) + ".output")) + continue; + const QStringList &inputs = project->values(compilers.at(compiler) + ".input"); + for(int input = 0; input < inputs.size(); ++input) { + if(project->isEmpty(inputs.at(input))) + continue; + const QStringList &files = project->values(inputs.at(input)); + for(int file = 0, added = 0; file < files.size(); ++file) { + if(!verifyExtraCompiler(compilers.at(compiler), files.at(file))) + continue; + if(added && !(added % 3)) + mkt << "\\\n\t"; + ++added; + const QString file_name = fileFixify(files.at(file), Option::output_dir, Option::output_dir); + mkt << " " << replaceExtraCompilerVariables(tmp_out, file_name, QString()); + } + } + } + mkt << endl; + writeExtraCompilerTargets(mkt); + writingUnixMakefileGenerator = false; + } + mkt.flush(); + mkf.close(); + } + mkfile = fileFixify(mkfile, qmake_getpwd()); + QString phase_key = keyFor("QMAKE_PBX_PREPROCESS_TARGET"); +// project->values("QMAKE_PBX_BUILDPHASES").append(phase_key); + project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key); + t << "\t\t" << phase_key << " = {" << "\n" + << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("files", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("generatedFileNames", fixListForOutput("QMAKE_PBX_OBJ"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", "Qt Preprocessors") << ";" << "\n" + << "\t\t\t" << writeSettings("neededFileNames", fixListForOutput("QMAKE_PBX_OBJ"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << ";" << "\n" + << "\t\t\t" << writeSettings("shellScript", fixForOutput("make -C " + escapeFilePath(qmake_getpwd()) + " -f '" + escapeFilePath(mkfile) + "'")) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + + //SOURCE BUILDPHASE + if(!project->isEmpty("QMAKE_PBX_OBJ")) { + QString grp = "Build Sources", key = keyFor(grp); + project->values("QMAKE_PBX_BUILDPHASES").append(key); + t << "\t\t" << key << " = {" << "\n" + << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("files", fixListForOutput("QMAKE_PBX_OBJ"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXSourcesBuildPhase", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", grp) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + + if(!project->isActiveConfig("staticlib")) { //DUMP LIBRARIES + QStringList &libdirs = project->values("QMAKE_PBX_LIBPATHS"), + &frameworkdirs = project->values("QMAKE_FRAMEWORKPATH"); + QString libs[] = { "QMAKE_LFLAGS", "QMAKE_LIBDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS", + "QMAKE_LIBS", QString() }; + for(int i = 0; !libs[i].isNull(); i++) { + tmp = project->values(libs[i]); + for(int x = 0; x < tmp.count();) { + bool remove = false; + QString library, name, opt = tmp[x].trimmed(); + if(opt.length() >= 2 && (opt[0] == '"' || opt[0] == '\'') && + opt[(int) opt.length()-1] == opt[0]) + opt = opt.mid(1, opt.length()-2); + if(opt.startsWith("-L")) { + QString r = opt.right(opt.length() - 2); + fixForOutput(r); + libdirs.append(r); + } else if(opt == "-prebind") { + project->values("QMAKE_DO_PREBINDING").append("TRUE"); + remove = true; + } else if(opt.startsWith("-l")) { + name = opt.right(opt.length() - 2); + QString lib("lib" + name); + for(QStringList::Iterator lit = libdirs.begin(); lit != libdirs.end(); ++lit) { + if(project->isActiveConfig("link_prl")) { + /* This isn't real nice, but it is real useful. This looks in a prl + for what the library will ultimately be called so we can stick it + in the ProjectFile. If the prl format ever changes (not likely) then + this will not really work. However, more concerning is that it will + encode the version number in the Project file which might be a bad + things in days to come? --Sam + */ + QString lib_file = (*lit) + Option::dir_sep + lib; + if(QMakeMetaInfo::libExists(lib_file)) { + QMakeMetaInfo libinfo; + if(libinfo.readLib(lib_file)) { + if(!libinfo.isEmpty("QMAKE_PRL_TARGET")) { + library = (*lit) + Option::dir_sep + libinfo.first("QMAKE_PRL_TARGET"); + debug_msg(1, "pbuilder: Found library (%s) via PRL %s (%s)", + opt.toLatin1().constData(), lib_file.toLatin1().constData(), library.toLatin1().constData()); + remove = true; + } + } + } + } + if(!remove) { + QString extns[] = { ".dylib", ".so", ".a", QString() }; + for(int n = 0; !remove && !extns[n].isNull(); n++) { + QString tmp = (*lit) + Option::dir_sep + lib + extns[n]; + if(exists(tmp)) { + library = tmp; + debug_msg(1, "pbuilder: Found library (%s) via %s", + opt.toLatin1().constData(), library.toLatin1().constData()); + remove = true; + } + } + } + } + } else if(opt.startsWith("-F")) { + QString r; + if(opt.size() > 2) { + r = opt.right(opt.length() - 2); + } else { + if(x == tmp.count()-1) + break; + r = tmp[++x]; + } + if(!r.isEmpty()) { + fixForOutput(r); + frameworkdirs.append(r); + } + } else if(opt == "-framework") { + if(x == tmp.count()-1) + break; + const QString framework = tmp[x+1]; + QStringList fdirs = frameworkdirs; + fdirs << "/System/Library/Frameworks/" << "/Library/Frameworks/"; + for(int fdir = 0; fdir < fdirs.count(); fdir++) { + if(exists(fdirs[fdir] + QDir::separator() + framework + ".framework")) { + tmp.removeAt(x); + remove = true; + library = fdirs[fdir] + Option::dir_sep + framework + ".framework"; + break; + } + } + } else if(opt.left(1) != "-") { + if(exists(opt)) { + remove = true; + library = opt; + } + } + if(!library.isEmpty()) { + const int slsh = library.lastIndexOf(Option::dir_sep); + if(name.isEmpty()) { + if(slsh != -1) + name = library.right(library.length() - slsh - 1); + } + if(slsh != -1) { + const QString path = QFileInfo(library.left(slsh)).absoluteFilePath(); + if(!path.isEmpty() && !libdirs.contains(path)) + libdirs += path; + } + library = fileFixify(library); + QString key = keyFor(library); + bool is_frmwrk = (library.endsWith(".framework")); + t << "\t\t" << key << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", (is_frmwrk ? "PBXFrameworkReference" : "PBXFileReference"), SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", escapeFilePath(name)) << ";" << "\n" + << "\t\t\t" << writeSettings("path", escapeFilePath(library)) << ";" << "\n" + << "\t\t\t" << writeSettings("refType", QString::number(reftypeForFile(library)), SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};" << "\n"; + project->values("QMAKE_PBX_LIBRARIES").append(key); + QString build_key = keyFor(library + ".BUILDABLE"); + t << "\t\t" << build_key << " = {" << "\n" + << "\t\t\t" << writeSettings("fileRef", key) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << "settings = {" << "\n" + << "\t\t\t" << "};" << "\n" + << "\t\t" << "};" << "\n"; + project->values("QMAKE_PBX_BUILD_LIBRARIES").append(build_key); + } + if(remove) + tmp.removeAt(x); + else + x++; + } + project->values(libs[i]) = tmp; + } + } + //SUBLIBS BUILDPHASE (just another makefile) + if(!project->isEmpty("SUBLIBS")) { + QString mkfile = pbx_dir + Option::dir_sep + "qt_sublibs.mak"; + QFile mkf(mkfile); + if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) { + writingUnixMakefileGenerator = true; + debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData()); + QTextStream mkt(&mkf); + writeHeader(mkt); + mkt << "SUBLIBS= "; + tmp = project->values("SUBLIBS"); + for(int i = 0; i < tmp.count(); i++) + t << "tmp/lib" << tmp[i] << ".a "; + t << endl << endl; + mkt << "sublibs: $(SUBLIBS)" << endl << endl; + tmp = project->values("SUBLIBS"); + for(int i = 0; i < tmp.count(); i++) + t << "tmp/lib" << tmp[i] << ".a" << ":\n\t" + << var(QString("MAKELIB") + tmp[i]) << endl << endl; + mkt.flush(); + mkf.close(); + writingUnixMakefileGenerator = false; + } + QString phase_key = keyFor("QMAKE_PBX_SUBLIBS_BUILDPHASE"); + mkfile = fileFixify(mkfile, qmake_getpwd()); + project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key); + t << "\t\t" << phase_key << " = {" << "\n" + << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("files", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("generatedFileNames", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXShellScriptBuildPhase", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", "Qt Sublibs") << ";" << "\n" + << "\t\t\t" << writeSettings("neededFileNames", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("shellPath", "/bin/sh") << "\n" + << "\t\t\t" << writeSettings("shellScript", fixForOutput("make -C " + escapeFilePath(qmake_getpwd()) + " -f '" + escapeFilePath(mkfile) + "'")) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + //LIBRARY BUILDPHASE + if(!project->isEmpty("QMAKE_PBX_LIBRARIES")) { + tmp = project->values("QMAKE_PBX_LIBRARIES"); + if(!tmp.isEmpty()) { + QString grp("External Frameworks and Libraries"), key = keyFor(grp); + project->values("QMAKE_PBX_GROUPS").append(key); + t << "\t\t" << key << " = {" << "\n" + << "\t\t\t" << writeSettings("children", project->values("QMAKE_PBX_LIBRARIES"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", escapeFilePath(grp)) << ";" << "\n" + << "\t\t\t" << writeSettings("path", QStringList()) << ";" << "\n" + << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + } + { + QString grp("Frameworks & Libraries"), key = keyFor(grp); + project->values("QMAKE_PBX_BUILDPHASES").append(key); + t << "\t\t" << key << " = {" << "\n" + << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("files", project->values("QMAKE_PBX_BUILD_LIBRARIES"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXFrameworksBuildPhase", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", escapeFilePath(grp)) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + if(project->isActiveConfig("app_bundle") && project->first("TEMPLATE") == "app") { //BUNDLE RESOURCES + QString grp("Bundle Resources"), key = keyFor(grp); + project->values("QMAKE_PBX_BUILDPHASES").append(key); + t << "\t\t" << key << " = {" << "\n" + << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << "files = (" << "\n"; + if(!project->isEmpty("ICON")) { + QString icon = project->first("ICON"); + if(icon.length() >= 2 && (icon[0] == '"' || icon[0] == '\'') && icon[(int)icon.length()-1] == icon[0]) + icon = icon.mid(1, icon.length()-2); + t << "\t\t\t\t" << keyFor(icon + ".BUILDABLE") << ",\n"; + } + t << "\t\t\t" << ");" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXResourcesBuildPhase", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", escapeFilePath(grp)) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + { //INSTALL BUILDPHASE (copy) + QString phase_key = keyFor("QMAKE_PBX_TARGET_COPY_PHASE"); + QString destDir = Option::output_dir; + if (!project->isEmpty("QMAKE_ORIG_DESTDIR")) + destDir = project->first("QMAKE_ORIG_DESTDIR"); + destDir = fixForOutput(destDir); + destDir = fileInfo(Option::fixPathToLocalOS(destDir)).absoluteFilePath(); + project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key); + t << "\t\t" << phase_key << " = {\n" + << "\t\t\t" << writeSettings("name", "Project Copy") << ";" << "\n" + << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("dstPath", escapeFilePath(destDir)) << ";" << "\n" + << "\t\t\t" << writeSettings("dstSubfolderSpec", "0", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("files", keyFor("QMAKE_PBX_TARGET_COPY_FILE"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXCopyFilesBuildPhase", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};\n" + << "\t\t" << keyFor("QMAKE_PBX_TARGET_COPY_FILE") << " = {\n" + << "\t\t\t" << writeSettings("fileRef", keyFor(pbx_dir + "QMAKE_PBX_REFERENCE")) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << "settings = {\n" + << "\t\t\t" << "};\n" + << "\t\t" << "};\n"; + } + //BUNDLE_DATA BUILDPHASE (copy) + if(!project->isEmpty("QMAKE_BUNDLE_DATA")) { + QStringList bundle_file_refs; + //all bundle data + const QStringList &bundle_data = project->values("QMAKE_BUNDLE_DATA"); + for(int i = 0; i < bundle_data.count(); i++) { + QStringList pbx_files; + //all files + const QStringList &files = project->values(bundle_data[i] + ".files"); + for(int file = 0; file < files.count(); file++) { + QString file_ref_key = keyFor("QMAKE_PBX_BUNDLE_COPY_FILE_REF." + bundle_data[i] + "-" + files[file]); + bundle_file_refs += file_ref_key; + t << "\t\t" << file_ref_key << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("path", escapeFilePath(files[file])) << ";" << "\n" + << "\t\t\t" << writeSettings("refType", QString::number(reftypeForFile(files[file])), SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};" << "\n"; + QString copy_file_key = keyFor("QMAKE_PBX_BUNDLE_COPY_FILE." + bundle_data[i] + "-" + files[file]); + pbx_files += copy_file_key; + t << "\t\t" << copy_file_key << " = {\n" + << "\t\t\t" << writeSettings("fileRef", file_ref_key) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXBuildFile", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << "settings = {\n" + << "\t\t\t" << "}" << ";" << "\n" + << "\t\t" << "}" << ";" << "\n"; + } + //the phase + QString phase_key = keyFor("QMAKE_PBX_BUNDLE_COPY." + bundle_data[i]); + QString path; + if(!project->isEmpty(bundle_data[i] + ".version")) { + //### + } + path += project->first(bundle_data[i] + ".path"); + project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").append(phase_key); + t << "\t\t" << phase_key << " = {\n" + << "\t\t\t" << writeSettings("name", "Bundle Copy [" + bundle_data[i] + "]") << ";" << "\n" + << "\t\t\t" << writeSettings("buildActionMask", "2147483647", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("dstPath", escapeFilePath(path)) << ";" << "\n" + << "\t\t\t" << writeSettings("dstSubfolderSpec", "1", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("files", pbx_files, SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXCopyFilesBuildPhase", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("runOnlyForDeploymentPostprocessing", "0", SettingsNoQuote) << ";" << "\n" + << "\t\t" << "}" << ";" << "\n"; + } + QString bundle_copy_key = keyFor("QMAKE_PBX_BUNDLE_COPY"); + project->values("QMAKE_PBX_GROUPS").append(bundle_copy_key); + t << "\t\t" << bundle_copy_key << " = {" << "\n" + << "\t\t\t" << writeSettings("children", bundle_file_refs, SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", "Source [bundle data]") << ";" << "\n" + << "\t\t\t" << writeSettings("path", QStringList()) << ";" << "\n" + << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + + if(/*pbVersion >= 38 &&*/ !project->isEmpty("QMAKE_PBX_PRESCRIPT_BUILDPHASES") && 0) { + // build reference + t << "\t\t" << keyFor("QMAKE_PBX_PRESCRIPT_BUILDREFERENCE") << " = {" << "\n" + << "\t\t\t" << writeSettings("includeInIndex", "0") << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("path", "preprocessor.out") << ";" << "\n" + << "\t\t\t" << writeSettings("refType", "3", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("sourceTree", "BUILT_PRODUCTS_DIR", SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};" << "\n"; + project->values("QMAKE_PBX_PRODUCTS").append(keyFor("QMAKE_PBX_PRESCRIPTS_BUILDREFERENCE")); + //build phase + t << "\t\t" << keyFor("QMAKE_PBX_PRESCRIPTS_BUILDPHASE") << " = {" << "\n" + << "\t\t\t" << writeSettings("buildPhases", project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("buildRules", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("buildSettings", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("dependencies", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXNativeTarget", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", "Qt Preprocessor Steps") << ";" << "\n" + << "\t\t\t" << writeSettings("productName", "Qt Preprocessor Steps") << ";" << "\n" + << "\t\t\t" << writeSettings("productReference", keyFor("QMAKE_PBX_PRESCRIPTS_BUILDREFERENCE")) << ";" << "\n"; + if(!project->isEmpty("QMAKE_PBX_PRODUCT_TYPE")) + t << "\t\t\t" << writeSettings("productType", project->first("QMAKE_PBX_PRODUCT_TYPE")) << ";" << "\n"; + else + t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.tool") << ";" << "\n"; + t << "\t\t" << "};" << "\n"; + //dependency + t << "\t\t" << keyFor("QMAKE_PBX_PRESCRIPTS_DEPENDENCY") << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXTargetDependency", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("target", keyFor("QMAKE_PBX_PRESCRIPTS_BUILDPHASE")) << ";" << "\n" + << "\t\t" << "};" << "\n"; + project->values("QMAKE_PBX_TARGET_DEPENDS").append(keyFor("QMAKE_PBX_PRESCRIPTS_DEPENDENCY")); + project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES").clear(); //these are already consumed above + } + + //DUMP EVERYTHING THAT TIES THE ABOVE TOGETHER + //ROOT_GROUP + t << "\t\t" << keyFor("QMAKE_PBX_ROOT_GROUP") << " = {" << "\n" + << "\t\t\t" << writeSettings("children", project->values("QMAKE_PBX_GROUPS"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", escapeFilePath(project->first("QMAKE_ORIG_TARGET"))) << ";" << "\n" + << "\t\t\t" << writeSettings("path", QStringList()) << ";" << "\n" + << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};" << "\n"; + //REFERENCE + project->values("QMAKE_PBX_PRODUCTS").append(keyFor(pbx_dir + "QMAKE_PBX_REFERENCE")); + t << "\t\t" << keyFor(pbx_dir + "QMAKE_PBX_REFERENCE") << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXFileReference", SettingsNoQuote) << ";" << "\n"; + if(project->first("TEMPLATE") == "app") { + QString targ = project->first("QMAKE_ORIG_TARGET"); + if(project->isActiveConfig("bundle") && !project->isEmpty("QMAKE_BUNDLE_EXTENSION")) { + if(!project->isEmpty("QMAKE_BUNDLE_NAME")) + targ = project->first("QMAKE_BUNDLE_NAME"); + targ += project->first("QMAKE_BUNDLE_EXTENSION"); + if(!project->isEmpty("QMAKE_PBX_BUNDLE_TYPE")) + t << "\t\t\t" << writeSettings("explicitFileType", project->first("QMAKE_PBX_BUNDLE_TYPE")) + ";" << "\n"; + } else if(project->isActiveConfig("app_bundle")) { + if(!project->isEmpty("QMAKE_APPLICATION_BUNDLE_NAME")) + targ = project->first("QMAKE_APPLICATION_BUNDLE_NAME"); + targ += ".app"; + t << "\t\t\t" << writeSettings("explicitFileType", "wrapper.application") << ";" << "\n"; + } else { + t << "\t\t\t" << writeSettings("explicitFileType", "wrapper.executable") << ";" << "\n"; + } + QString app = (!project->isEmpty("DESTDIR") ? project->first("DESTDIR") + project->first("QMAKE_ORIG_TARGET") : + qmake_getpwd()) + Option::dir_sep + targ; + t << "\t\t\t" << writeSettings("path", escapeFilePath(targ)) << ";" << "\n"; + } else { + QString lib = project->first("QMAKE_ORIG_TARGET"); + if(project->isActiveConfig("staticlib")) { + lib = project->first("TARGET"); + } else if(!project->isActiveConfig("lib_bundle")) { + if(project->isActiveConfig("plugin")) + lib = project->first("TARGET"); + else + lib = project->first("TARGET_"); + } + int slsh = lib.lastIndexOf(Option::dir_sep); + if(slsh != -1) + lib = lib.right(lib.length() - slsh - 1); + if(project->isActiveConfig("bundle") && !project->isEmpty("QMAKE_BUNDLE_EXTENSION")) { + if(!project->isEmpty("QMAKE_BUNDLE_NAME")) + lib = project->first("QMAKE_BUNDLE_NAME"); + lib += project->first("QMAKE_BUNDLE_EXTENSION"); + if(!project->isEmpty("QMAKE_PBX_BUNDLE_TYPE")) + t << "\t\t\t" << writeSettings("explicitFileType", project->first("QMAKE_PBX_BUNDLE_TYPE")) << ";" << "\n"; + } else if(!project->isActiveConfig("staticlib") && project->isActiveConfig("lib_bundle")) { + if(!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME")) + lib = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME"); + lib += ".framework"; + t << "\t\t\t" << writeSettings("explicitFileType", "wrapper.framework") << ";" << "\n"; + } else { + t << "\t\t\t" << writeSettings("explicitFileType", "compiled.mach-o.dylib") << ";" << "\n"; + } + t << "\t\t\t" << writeSettings("path", escapeFilePath(lib)) << ";" << "\n"; + } + t << "\t\t\t" << writeSettings("refType", "3", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("sourceTree", "BUILT_PRODUCTS_DIR", SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};" << "\n"; + { //Products group + QString grp("Products"), key = keyFor(grp); + project->values("QMAKE_PBX_GROUPS").append(key); + t << "\t\t" << key << " = {" << "\n" + << "\t\t\t" << writeSettings("children", project->values("QMAKE_PBX_PRODUCTS"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXGroup", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("name", "Products") << ";" << "\n" + << "\t\t\t" << writeSettings("refType", "4", SettingsNoQuote) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + //TARGET + QString target_key = keyFor(pbx_dir + "QMAKE_PBX_TARGET"); + project->values("QMAKE_PBX_TARGETS").append(target_key); + t << "\t\t" << target_key << " = {" << "\n" + << "\t\t\t" << writeSettings("buildPhases", project->values("QMAKE_PBX_PRESCRIPT_BUILDPHASES") + project->values("QMAKE_PBX_BUILDPHASES"), + SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << "buildSettings = {" << "\n"; + QString cCompiler = project->first("QMAKE_CC"); + if (!cCompiler.isEmpty()) { + t << "\t\t\t\t" << writeSettings("CC", fixForOutput(findProgram(cCompiler))) << ";" << "\n"; + } + cCompiler = project->first("QMAKE_CXX"); + if (!cCompiler.isEmpty()) { + t << "\t\t\t\t" << writeSettings("CPLUSPLUS", fixForOutput(findProgram(cCompiler))) << ";" << "\n"; + } + + t << "\t\t\t\t" << writeSettings("HEADER_SEARCH_PATHS", fixListForOutput("INCLUDEPATH") + QStringList(fixForOutput(specdir())), SettingsAsList, 5) << ";" << "\n" + << "\t\t\t\t" << writeSettings("LIBRARY_SEARCH_PATHS", fixListForOutput("QMAKE_PBX_LIBPATHS"), SettingsAsList, 5) << ";" << "\n" + << "\t\t\t\t" << writeSettings("OPTIMIZATION_CFLAGS", QStringList(), SettingsAsList, 5) << ";" << "\n"; + { + QStringList cflags = fixListForOutput("QMAKE_CFLAGS"); + const QStringList &prl_defines = project->values("PRL_EXPORT_DEFINES"); + for(int i = 0; i < prl_defines.size(); ++i) + cflags += "-D" + prl_defines.at(i); + const QStringList &defines = project->values("DEFINES"); + for(int i = 0; i < defines.size(); ++i) + cflags += "-D" + defines.at(i); + t << "\t\t\t\t" << writeSettings("OTHER_CFLAGS", cflags, SettingsAsList, 5) << ";" << "\n"; + } + { + QStringList cxxflags = fixListForOutput("QMAKE_CXXFLAGS"); + const QStringList &prl_defines = project->values("PRL_EXPORT_DEFINES"); + for(int i = 0; i < prl_defines.size(); ++i) + cxxflags += "-D" + prl_defines.at(i); + const QStringList &defines = project->values("DEFINES"); + for(int i = 0; i < defines.size(); ++i) + cxxflags += "-D" + defines.at(i); + t << "\t\t\t\t" << writeSettings("OTHER_CPLUSPLUSFLAGS", cxxflags, SettingsAsList, 5) << ";" << "\n"; + } + t << "\t\t\t\t" << writeSettings("LEXFLAGS", fixListForOutput("QMAKE_LEXFLAGS")) << ";" << "\n" + << "\t\t\t\t" << writeSettings("YACCFLAGS", fixListForOutput("QMAKE_YACCFLAGS")) << ";" << "\n" + << "\t\t\t\t" << writeSettings("OTHER_REZFLAGS", QStringList()) << ";" << "\n" + << "\t\t\t\t" << writeSettings("SECTORDER_FLAGS", QStringList()) << ";" << "\n" + << "\t\t\t\t" << writeSettings("WARNING_CFLAGS", QStringList()) << ";" << "\n" + << "\t\t\t\t" << writeSettings("PREBINDING", QStringList((project->isEmpty("QMAKE_DO_PREBINDING") ? "NO" : "YES")), SettingsNoQuote) << ";" << "\n"; + if(!project->isEmpty("PRECOMPILED_HEADER")) { + if(pbVersion >= 38) { + t << "\t\t\t\t" << writeSettings("GCC_PRECOMPILE_PREFIX_HEADER", "YES") << ";" << "\n" + << "\t\t\t\t" << writeSettings("GCC_PREFIX_HEADER", escapeFilePath(project->first("PRECOMPILED_HEADER"))) << ";" << "\n"; + } else { + t << "\t\t\t\t" << writeSettings("PRECOMPILE_PREFIX_HEADER", "YES") << ";" << "\n" + << "\t\t\t\t" << writeSettings("PREFIX_HEADER", escapeFilePath(project->first("PRECOMPILED_HEADER"))) << ";" << "\n"; + } + } + if((project->first("TEMPLATE") == "app" && project->isActiveConfig("app_bundle")) || + (project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") && + project->isActiveConfig("lib_bundle"))) { + QString plist = fileFixify(project->first("QMAKE_INFO_PLIST")); + if(plist.isEmpty()) + plist = specdir() + QDir::separator() + "Info.plist." + project->first("TEMPLATE"); + if(exists(plist)) { + QFile plist_in_file(plist); + if(plist_in_file.open(QIODevice::ReadOnly)) { + QTextStream plist_in(&plist_in_file); + QString plist_in_text = plist_in.readAll(); + plist_in_text = plist_in_text.replace("@ICON@", + (project->isEmpty("ICON") ? QString("") : project->first("ICON").section(Option::dir_sep, -1))); + if(project->first("TEMPLATE") == "app") { + plist_in_text = plist_in_text.replace("@EXECUTABLE@", project->first("QMAKE_ORIG_TARGET")); + } else { + plist_in_text = plist_in_text.replace("@LIBRARY@", project->first("QMAKE_ORIG_TARGET")); + } + if (!project->values("VERSION").isEmpty()) { + plist_in_text = plist_in_text.replace("@SHORT_VERSION@", project->first("VER_MAJ") + "." + + project->first("VER_MIN")); + } + plist_in_text = plist_in_text.replace("@TYPEINFO@", + (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? QString::fromLatin1("????") : + project->first("QMAKE_PKGINFO_TYPEINFO").left(4))); + QFile plist_out_file("Info.plist"); + if(plist_out_file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream plist_out(&plist_out_file); + plist_out << plist_in_text; + t << "\t\t\t\t" << writeSettings("INFOPLIST_FILE", "Info.plist") << ";" << "\n"; + } + } + } + } +#if 1 + t << "\t\t\t\t" << writeSettings("BUILD_ROOT", escapeFilePath(qmake_getpwd())) << ";" << "\n"; +#endif + if(!project->isActiveConfig("staticlib")) { + t << "\t\t\t\t" << writeSettings("OTHER_LDFLAGS", + fixListForOutput("SUBLIBS") + + fixListForOutput("QMAKE_LFLAGS") + + fixListForOutput("QMAKE_LIBDIR_FLAGS") + + fixListForOutput("QMAKE_FRAMEWORKPATH_FLAGS") + + fixListForOutput("QMAKE_LIBS"), + SettingsAsList, 6) << ";" << "\n"; + } + if(!project->isEmpty("DESTDIR")) { + QString dir = project->first("DESTDIR"); + if (QDir::isRelativePath(dir)) + dir.prepend(qmake_getpwd() + Option::dir_sep); + t << "\t\t\t\t" << writeSettings("INSTALL_DIR", dir) << ";" << "\n"; + } + if (project->first("TEMPLATE") == "lib") { + t << "\t\t\t\t" << writeSettings("INSTALL_PATH", QStringList()) << ";" << "\n"; + } + if(!project->isEmpty("VERSION") && project->first("VERSION") != "0.0.0") { + t << "\t\t\t\t" << writeSettings("DYLIB_CURRENT_VERSION", project->first("VER_MAJ")+"."+project->first("VER_MIN")+"."+project->first("VER_PAT")) << ";" << "\n"; + if(project->isEmpty("COMPAT_VERSION")) + t << "\t\t\t\t" << writeSettings("DYLIB_COMPATIBILITY_VERSION", project->first("VER_MAJ")+"."+project->first("VER_MIN")) << ";" << "\n"; + if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") && + project->isActiveConfig("lib_bundle")) + t << "\t\t\t\t" << writeSettings("FRAMEWORK_VERSION", project->first("QMAKE_FRAMEWORK_VERSION")) << ";" << "\n"; + } + if(!project->isEmpty("COMPAT_FRAMEWORKPATH")) + t << "\t\t\t\t" << writeSettings("FRAMEWORK_SEARCH_PATHS", fixListForOutput("QMAKE_FRAMEWORKPATH"), SettingsAsList, 5) << ";" << "\n"; + if(!project->isEmpty("COMPAT_VERSION")) + t << "\t\t\t\t" << writeSettings("DYLIB_COMPATIBILITY_VERSION", project->first("COMPAT_VERSION")) << ";" << "\n"; + if(!project->isEmpty("QMAKE_MACOSX_DEPLOYMENT_TARGET")) + t << "\t\t\t\t" << writeSettings("MACOSX_DEPLOYMENT_TARGET", project->first("QMAKE_MACOSX_DEPLOYMENT_TARGET")) << ";" << "\n"; + if(pbVersion >= 38) { + if(!project->isEmpty("OBJECTS_DIR")) + t << "\t\t\t\t" << writeSettings("OBJROOT", fixForOutput(project->first("OBJECTS_DIR"))) << ";" << "\n"; + } +#if 0 + if(!project->isEmpty("DESTDIR")) + t << "\t\t\t\t" << writeSettings("SYMROOT", fixForOutput(project->first("DESTDIR"))) << ";" << "\n"; + else + t << "\t\t\t\t" << writeSettings("SYMROOT", fixForOutput(qmake_getpwd())) << ";" << "\n"; +#endif + { + QStringList archs; + if(project->isActiveConfig("x86")) + archs += "i386"; + if(project->isActiveConfig("ppc")) { + if(!archs.isEmpty()) + archs += " "; + archs += "ppc"; + } + if(project->isActiveConfig("ppc64")) { + if(!archs.isEmpty()) + archs += " "; + archs += "ppc64"; + } + if(project->isActiveConfig("x86_64")) { + if(!archs.isEmpty()) + archs += " "; + archs += "x86_64"; + } + if(!archs.isEmpty()) + t << "\t\t\t\t" << writeSettings("ARCHS", archs) << ";" << "\n"; + + } + if(project->first("TEMPLATE") == "app") { + if(pbVersion < 38 && project->isActiveConfig("app_bundle")) + t << "\t\t\t\t" << writeSettings("WRAPPER_SUFFIX", "app") << ";" << "\n"; + t << "\t\t\t\t" << writeSettings("PRODUCT_NAME", fixForOutput(project->first("QMAKE_ORIG_TARGET"))) << ";" << "\n"; + } else { + if(!project->isActiveConfig("plugin") && project->isActiveConfig("staticlib")) { + t << "\t\t\t\t" << writeSettings("LIBRARY_STYLE", "STATIC") << ";" << "\n"; + } else { + t << "\t\t\t\t" << writeSettings("LIBRARY_STYLE", "DYNAMIC") << ";" << "\n"; + } + QString lib = project->first("QMAKE_ORIG_TARGET"); + if(!project->isActiveConfig("lib_bundle") && !project->isActiveConfig("staticlib")) + lib.prepend("lib"); + t << "\t\t\t\t" << writeSettings("PRODUCT_NAME", escapeFilePath(lib)) << ";" << "\n"; + } + tmp = project->values("QMAKE_PBX_VARS"); + for(int i = 0; i < tmp.count(); i++) { + QString var = tmp[i], val = qgetenv(var.toLatin1()); + if(val.isEmpty() && var == "TB") + val = "/usr/bin/"; + t << "\t\t\t\t" << writeSettings(var, escapeFilePath(val)) << ";" << "\n"; + } + t << "\t\t\t" << "};" << "\n" + << "\t\t\t" << "conditionalBuildSettings = {" << "\n" + << "\t\t\t" << "};" << "\n" + << "\t\t\t" << writeSettings("dependencies", project->values("QMAKE_PBX_TARGET_DEPENDS"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("productReference", keyFor(pbx_dir + "QMAKE_PBX_REFERENCE")) << ";" << "\n" + << "\t\t\t" << writeSettings("shouldUseHeadermap", "1", SettingsNoQuote) << ";" << "\n"; + if(pbVersion >= 38) + t << "\t\t\t" << writeSettings("isa", "PBXNativeTarget", SettingsNoQuote) << ";" << "\n"; + if(project->first("TEMPLATE") == "app") { + if(!project->isActiveConfig("app_bundle")) { + if(pbVersion >= 38) { + if(!project->isEmpty("QMAKE_PBX_PRODUCT_TYPE")) + t << "\t\t\t" << writeSettings("productType", project->first("QMAKE_PBX_PRODUCT_TYPE")) << ";" << "\n"; + else + t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.tool") << ";" << "\n"; + } else { + t << "\t\t\t" << writeSettings("isa", "PBXToolTarget", SettingsNoQuote) << ";" << "\n"; + } + } else { + if(pbVersion >= 38) { + if(!project->isEmpty("QMAKE_PBX_PRODUCT_TYPE")) + t << "\t\t\t" << writeSettings("productType", project->first("QMAKE_PBX_PRODUCT_TYPE")) << ";" << "\n"; + else + t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.application") << ";" << "\n"; + } else { + t << "\t\t\t" << writeSettings("isa", "PBXApplicationTarget", SettingsNoQuote) << ";" << "\n"; + } + t << "\t\t\t" << "productSettingsXML = \""; + bool read_plist = false; + if(exists("Info.plist")) { + QFile plist("Info.plist"); + if (plist.open(QIODevice::ReadOnly)) { + read_plist = true; + QTextStream stream(&plist); + while(!stream.atEnd()) + t << stream.readLine().replace('"', "\\\"") << endl; + } + } + if(!read_plist) { + t << "" << "\n" + << "\t\t\t\t" << "" << "\n" + << "\t\t\t\t" << "" << "\n" + << "\t\t\t\t" << "" << "\n" + << "\t\t\t\t\t" << "CFBundleDevelopmentRegion" << "\n" + << "\t\t\t\t\t" << "English" << "\n" + << "\t\t\t\t\t" << "CFBundleExecutable" << "\n" + << "\t\t\t\t\t" << "" << project->first("QMAKE_ORIG_TARGET") << "" << "\n" + << "\t\t\t\t\t" << "CFBundleIconFile" << "\n" + << "\t\t\t\t\t" << "" << var("ICON").section(Option::dir_sep, -1) << "" << "\n" + << "\t\t\t\t\t" << "CFBundleInfoDictionaryVersion" << "\n" + << "\t\t\t\t\t" << "6.0" << "\n" + << "\t\t\t\t\t" << "CFBundlePackageType" << "\n" + << "\t\t\t\t\t" << "APPL" << "\n" + << "\t\t\t\t\t" << "CFBundleSignature" << "\n" + << "\t\t\t\t\t" << "" + << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? QString::fromLatin1("????") : + project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << "" << "\n" + << "\t\t\t\t\t" << "CFBundleVersion" << "\n" + << "\t\t\t\t\t" << "0.1" << "\n" + << "\t\t\t\t\t" << "CSResourcesFileMapped" << "\n" + << "\t\t\t\t\t" << "" << "\n" + << "\t\t\t\t" << "" << "\n" + << "\t\t\t\t" << ""; + } + t << "\";" << "\n"; + } + t << "\t\t\t" << writeSettings("name", escapeFilePath(project->first("QMAKE_ORIG_TARGET"))) << ";" << "\n" + << "\t\t\t" << writeSettings("productName", escapeFilePath(project->first("QMAKE_ORIG_TARGET"))) << ";" << "\n"; + } else { + QString lib = project->first("QMAKE_ORIG_TARGET"); + if(!project->isActiveConfig("lib_bundle") && !project->isActiveConfig("staticlib")) + lib.prepend("lib"); + t << "\t\t\t" << writeSettings("name", escapeFilePath(lib)) << ";" << "\n" + << "\t\t\t" << writeSettings("productName", escapeFilePath(lib)) << ";" << "\n"; + if(pbVersion >= 38) { + if(!project->isEmpty("QMAKE_PBX_PRODUCT_TYPE")) + t << "\t\t\t" << writeSettings("productType", project->first("QMAKE_PBX_PRODUCT_TYPE")) << ";" << "\n"; + else if(project->isActiveConfig("staticlib")) + t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.library.static") << ";" << "\n"; + else if(project->isActiveConfig("lib_bundle")) + t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.framework") << ";" << "\n"; + else + t << "\t\t\t" << writeSettings("productType", "com.apple.product-type.library.dynamic") << ";" << "\n"; + } else { + t << "\t\t\t" << writeSettings("isa", "PBXLibraryTarget", SettingsNoQuote) << ";" << "\n"; + } + } + t << "\t\t\t" << writeSettings("startupPath", "<>") << ";" << "\n"; + if(!project->isEmpty("DESTDIR")) + t << "\t\t\t" << writeSettings("productInstallPath", escapeFilePath(project->first("DESTDIR"))) << ";" << "\n"; + t << "\t\t" << "};" << "\n"; + //DEBUG/RELEASE + QString active_buildstyle; + for(int as_release = 0; as_release < 2; as_release++) + { + QMap settings; + settings.insert("COPY_PHASE_STRIP", (as_release ? "YES" : "NO")); + settings.insert("GCC_GENERATE_DEBUGGING_SYMBOLS", as_release ? "NO" : "YES"); + if(!as_release) + settings.insert("GCC_OPTIMIZATION_LEVEL", "0"); + if(project->isActiveConfig("sdk") && !project->isEmpty("QMAKE_MAC_SDK")) + settings.insert("SDKROOT", project->first("QMAKE_MAC_SDK")); + { + const QStringList &l = project->values("QMAKE_MAC_XCODE_SETTINGS"); + for(int i = 0; i < l.size(); ++i) { + QString name = l.at(i); + const QString value = project->values(name + QLatin1String(".value")).join(QString(Option::field_sep)); + if(!project->isEmpty(name + QLatin1String(".name"))) + name = project->values(name + QLatin1String(".name")).first(); + settings.insert(name, value); + } + } + + QString name; + if(pbVersion >= 42) + name = (as_release ? "Release" : "Debug"); + else + name = (as_release ? "Deployment" : "Development"); + if(pbVersion >= 42) { + QString key = keyFor("QMAKE_PBX_BUILDCONFIG_" + name); + project->values("QMAKE_PBX_BUILDCONFIGS").append(key); + t << "\t\t" << key << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "XCBuildConfiguration", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << "buildSettings = {" << "\n"; + for(QMap::Iterator set_it = settings.begin(); set_it != settings.end(); ++set_it) + t << "\t\t\t\t" << writeSettings(set_it.key(), set_it.value()) << ";\n"; + t << "\t\t\t" << "};" << "\n" + << "\t\t\t" << writeSettings("name", name) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + + QString key = keyFor("QMAKE_PBX_BUILDSTYLE_" + name); + if(project->isActiveConfig("debug") != (bool)as_release) { + project->values("QMAKE_PBX_BUILDSTYLES").append(key); + active_buildstyle = name; + } else if(pbVersion >= 42) { + project->values("QMAKE_PBX_BUILDSTYLES").append(key); + } + t << "\t\t" << key << " = {" << "\n" + << "\t\t\t" << writeSettings("buildRules", QStringList(), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << "buildSettings = {" << "\n"; + for(QMap::Iterator set_it = settings.begin(); set_it != settings.end(); ++set_it) + t << "\t\t\t\t" << writeSettings(set_it.key(), set_it.value()) << ";" << "\n"; + t << "\t\t\t" << "};" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXBuildStyle") << ";" << "\n" + << "\t\t\t" << writeSettings("name", name) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + if(pbVersion >= 42) { + t << "\t\t" << keyFor("QMAKE_PBX_BUILDCONFIG_LIST") << " = {" << "\n" + << "\t\t\t" << writeSettings("isa", "XCConfigurationList", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("buildConfigurations", project->values("QMAKE_PBX_BUILDCONFIGS"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("defaultConfigurationIsVisible", "0", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("defaultConfigurationIsName", active_buildstyle) << ";" << "\n" + << "\t\t" << "};" << "\n"; + } + //ROOT + t << "\t\t" << keyFor("QMAKE_PBX_ROOT") << " = {" << "\n" + << "\t\t\t" << writeSettings("buildStyles", project->values("QMAKE_PBX_BUILDSTYLES"), SettingsAsList, 4) << ";" << "\n" + << "\t\t\t" << writeSettings("hasScannedForEncodings", "1", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("isa", "PBXProject", SettingsNoQuote) << ";" << "\n" + << "\t\t\t" << writeSettings("mainGroup", keyFor("QMAKE_PBX_ROOT_GROUP")) << ";" << "\n"; + if(pbVersion >= 42) + t << "\t\t\t" << writeSettings("buildConfigurationList", keyFor("QMAKE_PBX_BUILDCONFIG_LIST")) << ";" << "\n"; + t << "\t\t\t" << writeSettings("projectDirPath", QStringList()) << ";" << "\n" + << "\t\t\t" << writeSettings("targets", project->values("QMAKE_PBX_TARGETS"), SettingsAsList, 4) << ";" << "\n" + << "\t\t" << "};" << "\n"; + + //FOOTER + t << "\t" << "};" << "\n" + << "\t" << writeSettings("rootObject", keyFor("QMAKE_PBX_ROOT")) << ";" << "\n" + << "}" << endl; + + if(project->isActiveConfig("generate_pbxbuild_makefile")) { + QString mkwrap = fileFixify(pbx_dir + Option::dir_sep + ".." + Option::dir_sep + project->first("MAKEFILE"), + qmake_getpwd()); + QFile mkwrapf(mkwrap); + if(mkwrapf.open(QIODevice::WriteOnly | QIODevice::Text)) { + writingUnixMakefileGenerator = true; + debug_msg(1, "pbuilder: Creating file: %s", mkwrap.toLatin1().constData()); + QTextStream mkwrapt(&mkwrapf); + writeHeader(mkwrapt); + const char cleans[] = "preprocess_clean "; + mkwrapt << "#This is a makefile wrapper for PROJECT BUILDER\n" + << "all:" << "\n\t" + << "cd " << project->first("QMAKE_ORIG_TARGET") << projectSuffix() << "/ && " << pbxbuild() << "\n" + << "install: all" << "\n\t" + << "cd " << project->first("QMAKE_ORIG_TARGET") << projectSuffix() << "/ && " << pbxbuild() << " install\n" + << "distclean clean: preprocess_clean" << "\n\t" + << "cd " << project->first("QMAKE_ORIG_TARGET") << projectSuffix() << "/ && " << pbxbuild() << " clean" << "\n" + << (!did_preprocess ? cleans : "") << ":" << "\n"; + if(did_preprocess) + mkwrapt << cleans << ":" << "\n\t" + << "make -f " + << pbx_dir << Option::dir_sep << "qt_preprocess.mak $@" << endl; + writingUnixMakefileGenerator = false; + } + } + return true; +} + +QString +ProjectBuilderMakefileGenerator::findProgram(const QString &prog) +{ + QString ret = prog; + if(QDir::isRelativePath(ret)) { + QStringList paths = QString(qgetenv("PATH")).split(':'); + for(int i = 0; i < paths.size(); ++i) { + QString path = paths.at(i) + "/" + prog; + if(exists(path)) { + ret = path; + break; + } + } + } + return ret; +} + +QString +ProjectBuilderMakefileGenerator::fixForOutput(const QString &values) +{ + //get the environment variables references + QRegExp reg_var("\\$\\((.*)\\)"); + for(int rep = 0; (rep = reg_var.indexIn(values, rep)) != -1;) { + if(project->values("QMAKE_PBX_VARS").indexOf(reg_var.cap(1)) == -1) + project->values("QMAKE_PBX_VARS").append(reg_var.cap(1)); + rep += reg_var.matchedLength(); + } + QString ret = values; + ret = ret.replace(QRegExp("\\\\ "), " "); //unescape spaces + ret = ret.replace(QRegExp("('|\\\\|\")"), "\\\\1"); //fix quotes + ret = ret.replace("\t", " "); //fix tabs + ret = ret.replace(QRegExp(" "), "\\ "); //escape spaces + return ret; +} + +QStringList +ProjectBuilderMakefileGenerator::fixListForOutput(const QString &where) +{ + QStringList ret; + const QStringList &l = project->values(where); + for(int i = 0; i < l.count(); i++) + ret += fixForOutput(l[i]); + return ret; +} + +QString +ProjectBuilderMakefileGenerator::keyFor(const QString &block) +{ +#if 1 //This make this code much easier to debug.. + if(project->isActiveConfig("no_pb_munge_key")) + return block; +#endif + QString ret; + if(!keys.contains(block)) { + ret = qtMD5(block.toUtf8()).left(24).toUpper(); + keys.insert(block, ret); + } else { + ret = keys[block]; + } + return ret; +} + +bool +ProjectBuilderMakefileGenerator::openOutput(QFile &file, const QString &build) const +{ + if(QDir::isRelativePath(file.fileName())) + file.setFileName(Option::output_dir + "/" + file.fileName()); //pwd when qmake was run + QFileInfo fi(fileInfo(file.fileName())); + if(fi.suffix() != "pbxproj" || file.fileName().isEmpty()) { + QString output = file.fileName(); + if(fi.isDir()) + output += QDir::separator(); + if(!output.endsWith(projectSuffix())) { + if(file.fileName().isEmpty() || fi.isDir()) { + if(project->first("TEMPLATE") == "subdirs" || project->isEmpty("QMAKE_ORIG_TARGET")) + output += fileInfo(project->projectFile()).baseName(); + else + output += project->first("QMAKE_ORIG_TARGET"); + } + output += projectSuffix() + QDir::separator(); + } else if(output[(int)output.length() - 1] != QDir::separator()) { + output += QDir::separator(); + } + output += QString("project.pbxproj"); + output = unescapeFilePath(output); + file.setFileName(output); + } + bool ret = UnixMakefileGenerator::openOutput(file, build); + ((ProjectBuilderMakefileGenerator*)this)->pbx_dir = Option::output_dir.section(Option::dir_sep, 0, -1); + Option::output_dir = pbx_dir.section(Option::dir_sep, 0, -2); + return ret; +} + +/* This function is such a hack it is almost pointless, but it + eliminates the warning message from ProjectBuilder that the project + file is for an older version. I guess this could be used someday if + the format of the output is dependant upon the version of + ProjectBuilder as well. +*/ +int +ProjectBuilderMakefileGenerator::pbuilderVersion() const +{ + QString ret; + if(!project->isEmpty("QMAKE_PBUILDER_VERSION")) { + ret = project->first("QMAKE_PBUILDER_VERSION"); + } else { + QString version, version_plist = project->first("QMAKE_PBUILDER_VERSION_PLIST"); + if(version_plist.isEmpty()) { +#ifdef Q_OS_DARWIN + ret = QLatin1String("34"); + QCFType cfurl; + OSStatus err = LSFindApplicationForInfo(0, CFSTR("com.apple.Xcode"), 0, 0, &cfurl); + if (err == noErr) { + QCFType bundle = CFBundleCreate(0, cfurl); + if (bundle) { + CFStringRef str = CFStringRef(CFBundleGetValueForInfoDictionaryKey(bundle, + CFSTR("CFBundleShortVersionString"))); + if (str) { + QStringList versions = QCFString::toQString(str).split(QLatin1Char('.')); + int versionMajor = versions.at(0).toInt(); + int versionMinor = versions.at(1).toInt(); + if (versionMajor >= 2) { + ret = QLatin1String("42"); + } else if (versionMajor == 1 && versionMinor >= 5) { + ret = QLatin1String("39"); + } + } + } + } +#else + if(exists("/Developer/Applications/Xcode.app/Contents/version.plist")) + version_plist = "/Developer/Applications/Xcode.app/Contents/version.plist"; + else + version_plist = "/Developer/Applications/Project Builder.app/Contents/version.plist"; +#endif + } else { + version_plist = version_plist.replace(QRegExp("\""), ""); + } + if (ret.isEmpty()) { + QFile version_file(version_plist); + if (version_file.open(QIODevice::ReadOnly)) { + debug_msg(1, "pbuilder: version.plist: Reading file: %s", version_plist.toLatin1().constData()); + QTextStream plist(&version_file); + + bool in_dict = false; + QString current_key; + QRegExp keyreg("^(.*)$"), stringreg("^(.*)$"); + while(!plist.atEnd()) { + QString line = plist.readLine().trimmed(); + if(line == "") + in_dict = true; + else if(line == "") + in_dict = false; + else if(in_dict) { + if(keyreg.exactMatch(line)) + current_key = keyreg.cap(1); + else if(current_key == "CFBundleShortVersionString" && stringreg.exactMatch(line)) + version = stringreg.cap(1); + } + } + plist.flush(); + version_file.close(); + } else { + debug_msg(1, "pbuilder: version.plist: Failure to open %s", version_plist.toLatin1().constData()); + } + if(version.isEmpty() && version_plist.contains("Xcode")) { + ret = "39"; + } else { + int versionMajor = version.left(1).toInt(); + if(versionMajor >= 2) + ret = "42"; + else if(version == "1.5") + ret = "39"; + else if(version == "1.1") + ret = "34"; + } + } + } + + if(!ret.isEmpty()) { + bool ok; + int int_ret = ret.toInt(&ok); + if(ok) { + debug_msg(1, "pbuilder: version.plist: Got version: %d", int_ret); + return int_ret; + } + } + debug_msg(1, "pbuilder: version.plist: Fallback to default version"); + return 42; //my fallback +} + +int +ProjectBuilderMakefileGenerator::reftypeForFile(const QString &where) +{ + int ret = 0; //absolute is the default.. + if(QDir::isRelativePath(unescapeFilePath(where))) + ret = 4; //relative + return ret; +} + +QString +ProjectBuilderMakefileGenerator::projectSuffix() const +{ + const int pbVersion = pbuilderVersion(); + if(pbVersion >= 42) + return ".xcodeproj"; + else if(pbVersion >= 38) + return ".xcode"; + return ".pbproj"; +} + +QString +ProjectBuilderMakefileGenerator::pbxbuild() +{ + if(exists("/usr/bin/pbbuild")) + return "pbbuild"; + if(exists("/usr/bin/xcodebuild")) + return "xcodebuild"; + return (pbuilderVersion() >= 38 ? "xcodebuild" : "pbxbuild"); +} + +QString +ProjectBuilderMakefileGenerator::escapeFilePath(const QString &path) const +{ +#if 1 + //in the middle of generating a Makefile! + if(writingUnixMakefileGenerator) + return UnixMakefileGenerator::escapeFilePath(path); + + //generating stuff for the xml file! + QString ret = path; + if(!ret.isEmpty()) { + ret = unescapeFilePath(ret); + debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData()); + } + return ret; +#else + return UnixMakefileGenerator::escapeFilePath(path); +#endif +} + +QString +ProjectBuilderMakefileGenerator::writeSettings(QString var, QStringList vals, int flags, int indent_level) +{ + QString ret; + const QString quote = (flags & SettingsNoQuote) ? "" : "\""; + const QString escape_quote = quote.isEmpty() ? "" : "\\" + quote; + QString newline = "\n"; + for(int i = 0; i < indent_level; ++i) + newline += "\t"; + if(flags & SettingsAsList) { + ret += var + " = (" + newline; + for(int i = 0, count = 0; i < vals.size(); ++i) { + QString val = vals.at(i); + if(!val.isEmpty()) { + if(count++ > 0) + ret += "," + newline; + ret += quote + val.replace(quote, escape_quote) + quote; + } + } + ret += ")"; + } else { + ret += var + " = " + quote; + for(int i = 0; i < vals.size(); ++i) { + QString val = vals.at(i); +// if(val.isEmpty()) +// val = quote + quote; + if(i) + ret += " "; + ret += val; + } + ret += quote; + } + return ret; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/mac/pbuilder_pbx.h b/qmake/generators/mac/pbuilder_pbx.h new file mode 100644 index 0000000000..c11425a141 --- /dev/null +++ b/qmake/generators/mac/pbuilder_pbx.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PBUILDER_PBX_H +#define PBUILDER_PBX_H + +#include "unixmake.h" + +QT_BEGIN_NAMESPACE + +class ProjectBuilderMakefileGenerator : public UnixMakefileGenerator +{ + bool writingUnixMakefileGenerator; + QString pbx_dir; + int pbuilderVersion() const; + bool writeSubDirs(QTextStream &); + bool writeMakeParts(QTextStream &); + bool writeMakefile(QTextStream &); + + QString pbxbuild(); + QMap keys; + QString keyFor(const QString &file); + QString findProgram(const QString &prog); + QString fixForOutput(const QString &file); + QStringList fixListForOutput(const QString &where); + int reftypeForFile(const QString &where); + QString projectSuffix() const; + enum { SettingsAsList=0x01, SettingsNoQuote=0x02 }; + inline QString writeSettings(QString var, QString val, int flags=0, int indent_level=0) + { Q_UNUSED(indent_level); return writeSettings(var, QStringList(val), flags); } + QString writeSettings(QString var, QStringList vals, int flags=0, int indent_level=0); + +public: + ProjectBuilderMakefileGenerator(); + ~ProjectBuilderMakefileGenerator(); + + virtual bool supportsMetaBuild() { return false; } + virtual bool openOutput(QFile &, const QString &) const; +protected: + virtual QString escapeFilePath(const QString &path) const; + bool doPrecompiledHeaders() const { return false; } + virtual bool doDepends() const { return false; } //never necesary +}; + +inline ProjectBuilderMakefileGenerator::~ProjectBuilderMakefileGenerator() +{ } + +QT_END_NAMESPACE + +#endif // PBUILDER_PBX_H diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp new file mode 100644 index 0000000000..4f3b1137f5 --- /dev/null +++ b/qmake/generators/makefile.cpp @@ -0,0 +1,3297 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "makefile.h" +#include "option.h" +#include "cachekeys.h" +#include "meta.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(Q_OS_UNIX) +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// Well, Windows doesn't have this, so here's the macro +#ifndef S_ISDIR +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +bool MakefileGenerator::canExecute(const QStringList &cmdline, int *a) const +{ + int argv0 = -1; + for(int i = 0; i < cmdline.count(); ++i) { + if(!cmdline.at(i).contains('=')) { + argv0 = i; + break; + } + } + if(a) + *a = argv0; + if(argv0 != -1) { + const QString c = Option::fixPathToLocalOS(cmdline.at(argv0), true); + if(exists(c)) + return true; + } + return false; +} + +QString MakefileGenerator::mkdir_p_asstring(const QString &dir, bool escape) const +{ + QString ret = "@$(CHK_DIR_EXISTS) "; + if(escape) + ret += escapeFilePath(dir); + else + ret += dir; + ret += " "; + if(isWindowsShell()) + ret += "$(MKDIR)"; + else + ret += "|| $(MKDIR)"; + ret += " "; + if(escape) + ret += escapeFilePath(dir); + else + ret += dir; + ret += " "; + return ret; +} + +bool MakefileGenerator::mkdir(const QString &in_path) const +{ + QString path = Option::fixPathToLocalOS(in_path); + if(QFile::exists(path)) + return true; + + QDir d; + if(path.startsWith(QDir::separator())) { + d.cd(QString(QDir::separator())); + path.remove(0, 1); + } + bool ret = true; +#ifdef Q_OS_WIN + bool driveExists = true; + if(!QDir::isRelativePath(path)) { + if(QFile::exists(path.left(3))) { + d.cd(path.left(3)); + path.remove(0, 3); + } else { + warn_msg(WarnLogic, "Cannot access drive '%s' (%s)", + path.left(3).toLatin1().data(), path.toLatin1().data()); + driveExists = false; + } + } + if(driveExists) +#endif + { + QStringList subs = path.split(QDir::separator()); + for(QStringList::Iterator subit = subs.begin(); subit != subs.end(); ++subit) { + if(!d.cd(*subit)) { + d.mkdir((*subit)); + if(d.exists((*subit))) { + d.cd((*subit)); + } else { + ret = false; + break; + } + } + } + } + return ret; +} + +// ** base makefile generator +MakefileGenerator::MakefileGenerator() : + init_opath_already(false), init_already(false), no_io(false), project(0) +{ +} + + +void +MakefileGenerator::verifyCompilers() +{ + QMap &v = project->variables(); + QStringList &quc = v["QMAKE_EXTRA_COMPILERS"]; + for(int i = 0; i < quc.size(); ) { + bool error = false; + QString comp = quc.at(i); + if(v[comp + ".output"].isEmpty()) { + if(!v[comp + ".output_function"].isEmpty()) { + v[comp + ".output"].append("${QMAKE_FUNC_FILE_IN_" + v[comp + ".output_function"].first() + "}"); + } else { + error = true; + warn_msg(WarnLogic, "Compiler: %s: No output file specified", comp.toLatin1().constData()); + } + } else if(v[comp + ".input"].isEmpty()) { + error = true; + warn_msg(WarnLogic, "Compiler: %s: No input variable specified", comp.toLatin1().constData()); + } + if(error) + quc.removeAt(i); + else + ++i; + } +} + +void +MakefileGenerator::initOutPaths() +{ + if(init_opath_already) + return; + verifyCompilers(); + init_opath_already = true; + QMap &v = project->variables(); + //for shadow builds + if(!v.contains("QMAKE_ABSOLUTE_SOURCE_PATH")) { + if(Option::mkfile::do_cache && !Option::mkfile::cachefile.isEmpty() && + v.contains("QMAKE_ABSOLUTE_SOURCE_ROOT")) { + QString root = v["QMAKE_ABSOLUTE_SOURCE_ROOT"].first(); + root = QDir::fromNativeSeparators(root); + if(!root.isEmpty()) { + QFileInfo fi = fileInfo(Option::mkfile::cachefile); + if(!fi.makeAbsolute()) { + QString cache_r = fi.path(), pwd = Option::output_dir; + if(pwd.startsWith(cache_r) && !pwd.startsWith(root)) { + pwd = root + pwd.mid(cache_r.length()); + if(exists(pwd)) + v.insert("QMAKE_ABSOLUTE_SOURCE_PATH", QStringList(pwd)); + } + } + } + } + } + if(!v["QMAKE_ABSOLUTE_SOURCE_PATH"].isEmpty()) { + QString &asp = v["QMAKE_ABSOLUTE_SOURCE_PATH"].first(); + asp = QDir::fromNativeSeparators(asp); + if(asp.isEmpty() || asp == Option::output_dir) //if they're the same, why bother? + v["QMAKE_ABSOLUTE_SOURCE_PATH"].clear(); + } + + QString currentDir = qmake_getpwd(); //just to go back to + + //some builtin directories + if(project->isEmpty("PRECOMPILED_DIR") && !project->isEmpty("OBJECTS_DIR")) + v["PRECOMPILED_DIR"] = v["OBJECTS_DIR"]; + QString dirs[] = { QString("OBJECTS_DIR"), QString("DESTDIR"), QString("QMAKE_PKGCONFIG_DESTDIR"), + QString("SUBLIBS_DIR"), QString("DLLDESTDIR"), QString("QMAKE_LIBTOOL_DESTDIR"), + QString("PRECOMPILED_DIR"), QString() }; + for(int x = 0; !dirs[x].isEmpty(); x++) { + if(v[dirs[x]].isEmpty()) + continue; + const QString orig_path = v[dirs[x]].first(); + + QString &pathRef = v[dirs[x]].first(); + pathRef = fileFixify(pathRef, Option::output_dir, Option::output_dir); + +#ifdef Q_OS_WIN + // We don't want to add a separator for DLLDESTDIR on Windows (###why?) + if(!(dirs[x] == "DLLDESTDIR")) +#endif + { + if(!pathRef.endsWith(Option::dir_sep)) + pathRef += Option::dir_sep; + } + + if(noIO()) + continue; + + QString path = project->first(dirs[x]); //not to be changed any further + path = fileFixify(path, currentDir, Option::output_dir); + debug_msg(3, "Fixed output_dir %s (%s) into %s", dirs[x].toLatin1().constData(), + orig_path.toLatin1().constData(), path.toLatin1().constData()); + if(!mkdir(path)) + warn_msg(WarnLogic, "%s: Cannot access directory '%s'", dirs[x].toLatin1().constData(), + path.toLatin1().constData()); + } + + //out paths from the extra compilers + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + QString tmp_out = project->values((*it) + ".output").first(); + if(tmp_out.isEmpty()) + continue; + const QStringList &tmp = project->values((*it) + ".input"); + for(QStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + QStringList &inputs = project->values((*it2)); + for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) { + (*input) = fileFixify((*input), Option::output_dir, Option::output_dir); + QString path = unescapeFilePath(replaceExtraCompilerVariables(tmp_out, (*input), QString())); + path = Option::fixPathToTargetOS(path); + int slash = path.lastIndexOf(Option::dir_sep); + if(slash != -1) { + path = path.left(slash); + // Make out path only if it does not contain makefile variables + if(!path.contains("${")) + if(path != "." && + !mkdir(fileFixify(path, qmake_getpwd(), Option::output_dir))) + warn_msg(WarnLogic, "%s: Cannot access directory '%s'", + (*it).toLatin1().constData(), path.toLatin1().constData()); + } + } + } + } + + if(!v["DESTDIR"].isEmpty()) { + QDir d(v["DESTDIR"].first()); + if(Option::fixPathToLocalOS(d.absolutePath()) == Option::fixPathToLocalOS(Option::output_dir)) + v.remove("DESTDIR"); + } +} + +QMakeProject +*MakefileGenerator::projectFile() const +{ + return project; +} + +void +MakefileGenerator::setProjectFile(QMakeProject *p) +{ + if(project) + return; + project = p; + init(); + usePlatformDir(); + findLibraries(); + if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE && + project->isActiveConfig("link_prl")) //load up prl's' + processPrlFiles(); +} + +QStringList +MakefileGenerator::findFilesInVPATH(QStringList l, uchar flags, const QString &vpath_var) +{ + QStringList vpath; + QMap &v = project->variables(); + for(int val_it = 0; val_it < l.count(); ) { + bool remove_file = false; + QString &val = l[val_it]; + if(!val.isEmpty()) { + QString file = fixEnvVariables(val); + if(!(flags & VPATH_NoFixify)) + file = fileFixify(file, qmake_getpwd(), Option::output_dir); + if (file.at(0) == '\"' && file.at(file.length() - 1) == '\"') + file = file.mid(1, file.length() - 2); + + if(exists(file)) { + ++val_it; + continue; + } + bool found = false; + if(QDir::isRelativePath(val)) { + if(vpath.isEmpty()) { + if(!vpath_var.isEmpty()) + vpath = v[vpath_var]; + vpath += v["VPATH"] + v["QMAKE_ABSOLUTE_SOURCE_PATH"] + v["DEPENDPATH"]; + if(Option::output_dir != qmake_getpwd()) + vpath += Option::output_dir; + } + for(QStringList::Iterator vpath_it = vpath.begin(); + vpath_it != vpath.end(); ++vpath_it) { + QString real_dir = Option::fixPathToLocalOS((*vpath_it)); + if(exists(real_dir + QDir::separator() + val)) { + QString dir = (*vpath_it); + if(!dir.endsWith(Option::dir_sep)) + dir += Option::dir_sep; + val = dir + val; + if(!(flags & VPATH_NoFixify)) + val = fileFixify(val); + found = true; + debug_msg(1, "Found file through vpath %s -> %s", + file.toLatin1().constData(), val.toLatin1().constData()); + break; + } + } + } + if(!found) { + QString dir, regex = val, real_dir; + if(regex.lastIndexOf(Option::dir_sep) != -1) { + dir = regex.left(regex.lastIndexOf(Option::dir_sep) + 1); + real_dir = dir; + if(!(flags & VPATH_NoFixify)) + real_dir = fileFixify(real_dir, qmake_getpwd(), Option::output_dir) + '/'; + regex.remove(0, dir.length()); + } + if(real_dir.isEmpty() || exists(real_dir)) { + QStringList files = QDir(real_dir).entryList(QStringList(regex)); + if(files.isEmpty()) { + debug_msg(1, "%s:%d Failure to find %s in vpath (%s)", + __FILE__, __LINE__, + val.toLatin1().constData(), vpath.join("::").toLatin1().constData()); + if(flags & VPATH_RemoveMissingFiles) + remove_file = true; + else if(flags & VPATH_WarnMissingFiles) + warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData()); + } else { + l.removeAt(val_it); + QString a; + for(int i = (int)files.count()-1; i >= 0; i--) { + if(files[i] == "." || files[i] == "..") + continue; + a = real_dir + files[i]; + if(!(flags & VPATH_NoFixify)) + a = fileFixify(a); + l.insert(val_it, a); + } + } + } else { + debug_msg(1, "%s:%d Cannot match %s%s, as %s does not exist.", + __FILE__, __LINE__, real_dir.toLatin1().constData(), + regex.toLatin1().constData(), real_dir.toLatin1().constData()); + if(flags & VPATH_RemoveMissingFiles) + remove_file = true; + else if(flags & VPATH_WarnMissingFiles) + warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData()); + } + } + } + if(remove_file) + l.removeAt(val_it); + else + ++val_it; + } + return l; +} + +void +MakefileGenerator::initCompiler(const MakefileGenerator::Compiler &comp) +{ + QMap &v = project->variables(); + QStringList &l = v[comp.variable_in]; + // find all the relevant file inputs + if(!init_compiler_already.contains(comp.variable_in)) { + init_compiler_already.insert(comp.variable_in, true); + if(!noIO()) + l = findFilesInVPATH(l, (comp.flags & Compiler::CompilerRemoveNoExist) ? + VPATH_RemoveMissingFiles : VPATH_WarnMissingFiles, "VPATH_" + comp.variable_in); + } +} + +void +MakefileGenerator::init() +{ + initOutPaths(); + if(init_already) + return; + verifyCompilers(); + init_already = true; + + QMap &v = project->variables(); + QStringList &quc = v["QMAKE_EXTRA_COMPILERS"]; + + //make sure the COMPILERS are in the correct input/output chain order + for(int comp_out = 0, jump_count = 0; comp_out < quc.size(); ++comp_out) { + continue_compiler_chain: + if(jump_count > quc.size()) //just to avoid an infinite loop here + break; + if(project->variables().contains(quc.at(comp_out) + ".variable_out")) { + const QStringList &outputs = project->variables().value(quc.at(comp_out) + ".variable_out"); + for(int out = 0; out < outputs.size(); ++out) { + for(int comp_in = 0; comp_in < quc.size(); ++comp_in) { + if(comp_in == comp_out) + continue; + if(project->variables().contains(quc.at(comp_in) + ".input")) { + const QStringList &inputs = project->variables().value(quc.at(comp_in) + ".input"); + for(int in = 0; in < inputs.size(); ++in) { + if(inputs.at(in) == outputs.at(out) && comp_out > comp_in) { + ++jump_count; + //move comp_out to comp_in and continue the compiler chain + quc.move(comp_out, comp_in); + comp_out = comp_in; + goto continue_compiler_chain; + } + } + } + } + } + } + } + + if(!project->isEmpty("QMAKE_SUBSTITUTES")) { + const QStringList &subs = v["QMAKE_SUBSTITUTES"]; + for(int i = 0; i < subs.size(); ++i) { + QString inn = subs.at(i) + ".input", outn = subs.at(i) + ".output"; + if (v.contains(inn) || v.contains(outn)) { + if (!v.contains(inn) || !v.contains(outn)) { + warn_msg(WarnLogic, "Substitute '%s' has only one of .input and .output", + subs.at(i).toLatin1().constData()); + continue; + } + const QStringList &tinn = v[inn], &toutn = v[outn]; + if (tinn.length() != 1) { + warn_msg(WarnLogic, "Substitute '%s.input' does not have exactly one value", + subs.at(i).toLatin1().constData()); + continue; + } + if (toutn.length() != 1) { + warn_msg(WarnLogic, "Substitute '%s.output' does not have exactly one value", + subs.at(i).toLatin1().constData()); + continue; + } + inn = fileFixify(tinn.first(), qmake_getpwd()); + outn = fileFixify(toutn.first(), qmake_getpwd(), Option::output_dir); + } else { + inn = fileFixify(subs.at(i), qmake_getpwd()); + if (!QFile::exists(inn)) { + // random insanity for backwards compat: .in file specified with absolute out dir + inn = fileFixify(subs.at(i)); + } + if(!inn.endsWith(".in")) { + warn_msg(WarnLogic, "Substitute '%s' does not end with '.in'", + inn.toLatin1().constData()); + continue; + } + outn = fileFixify(inn.left(inn.length()-3), qmake_getpwd(), Option::output_dir); + } + QFile in(inn); + if(in.open(QFile::ReadOnly)) { + QString contents; + QStack state; + enum { IN_CONDITION, MET_CONDITION, PENDING_CONDITION }; + for(int count = 1; !in.atEnd(); ++count) { + QString line = QString::fromUtf8(in.readLine()); + if(line.startsWith("!!IF ")) { + if(state.isEmpty() || state.top() == IN_CONDITION) { + QString test = line.mid(5, line.length()-(5+1)); + if(project->test(test)) + state.push(IN_CONDITION); + else + state.push(PENDING_CONDITION); + } else { + state.push(MET_CONDITION); + } + } else if(line.startsWith("!!ELIF ")) { + if(state.isEmpty()) { + warn_msg(WarnLogic, "(%s:%d): Unexpected else condition", + in.fileName().toLatin1().constData(), count); + } else if(state.top() == PENDING_CONDITION) { + QString test = line.mid(7, line.length()-(7+1)); + if(project->test(test)) { + state.pop(); + state.push(IN_CONDITION); + } + } else if(state.top() == IN_CONDITION) { + state.pop(); + state.push(MET_CONDITION); + } + } else if(line.startsWith("!!ELSE")) { + if(state.isEmpty()) { + warn_msg(WarnLogic, "(%s:%d): Unexpected else condition", + in.fileName().toLatin1().constData(), count); + } else if(state.top() == PENDING_CONDITION) { + state.pop(); + state.push(IN_CONDITION); + } else if(state.top() == IN_CONDITION) { + state.pop(); + state.push(MET_CONDITION); + } + } else if(line.startsWith("!!ENDIF")) { + if(state.isEmpty()) + warn_msg(WarnLogic, "(%s:%d): Unexpected endif", + in.fileName().toLatin1().constData(), count); + else + state.pop(); + } else if(state.isEmpty() || state.top() == IN_CONDITION) { + contents += project->expand(line, in.fileName(), count); + } + } + QFile out(outn); + if(out.exists() && out.open(QFile::ReadOnly)) { + QString old = QString::fromUtf8(out.readAll()); + if(contents == old) { + v["QMAKE_INTERNAL_INCLUDED_FILES"].append(in.fileName()); + continue; + } + out.close(); + if(!out.remove()) { + warn_msg(WarnLogic, "Cannot clear substitute '%s'", + out.fileName().toLatin1().constData()); + continue; + } + } + mkdir(QFileInfo(out).absolutePath()); + if(out.open(QFile::WriteOnly)) { + v["QMAKE_INTERNAL_INCLUDED_FILES"].append(in.fileName()); + out.write(contents.toUtf8()); + } else { + warn_msg(WarnLogic, "Cannot open substitute for output '%s'", + out.fileName().toLatin1().constData()); + } + } else { + warn_msg(WarnLogic, "Cannot open substitute for input '%s'", + in.fileName().toLatin1().constData()); + } + } + } + + int x; + + //build up a list of compilers + QList compilers; + { + const char *builtins[] = { "OBJECTS", "SOURCES", "PRECOMPILED_HEADER", 0 }; + for(x = 0; builtins[x]; ++x) { + Compiler compiler; + compiler.variable_in = builtins[x]; + compiler.flags = Compiler::CompilerBuiltin; + compiler.type = QMakeSourceFileInfo::TYPE_C; + if(!strcmp(builtins[x], "OBJECTS")) + compiler.flags |= Compiler::CompilerNoCheckDeps; + compilers.append(compiler); + } + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + const QStringList &inputs = v[(*it) + ".input"]; + for(x = 0; x < inputs.size(); ++x) { + Compiler compiler; + compiler.variable_in = inputs.at(x); + compiler.flags = Compiler::CompilerNoFlags; + if(v[(*it) + ".CONFIG"].indexOf("ignore_no_exist") != -1) + compiler.flags |= Compiler::CompilerRemoveNoExist; + if(v[(*it) + ".CONFIG"].indexOf("no_dependencies") != -1) + compiler.flags |= Compiler::CompilerNoCheckDeps; + + QString dep_type; + if(!project->isEmpty((*it) + ".dependency_type")) + dep_type = project->first((*it) + ".dependency_type"); + if (dep_type.isEmpty()) + compiler.type = QMakeSourceFileInfo::TYPE_UNKNOWN; + else if(dep_type == "TYPE_UI") + compiler.type = QMakeSourceFileInfo::TYPE_UI; + else + compiler.type = QMakeSourceFileInfo::TYPE_C; + compilers.append(compiler); + } + } + } + { //do the path fixifying + QStringList paths; + for(x = 0; x < compilers.count(); ++x) { + if(!paths.contains(compilers.at(x).variable_in)) + paths << compilers.at(x).variable_in; + } + paths << "INCLUDEPATH" << "QMAKE_INTERNAL_INCLUDED_FILES" << "PRECOMPILED_HEADER"; + for(int y = 0; y < paths.count(); y++) { + QStringList &l = v[paths[y]]; + for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + if((*it).isEmpty()) + continue; + if(exists((*it))) + (*it) = fileFixify((*it)); + } + } + } + + if(noIO() || !doDepends()) + QMakeSourceFileInfo::setDependencyMode(QMakeSourceFileInfo::NonRecursive); + for(x = 0; x < compilers.count(); ++x) + initCompiler(compilers.at(x)); + + //merge actual compiler outputs into their variable_out. This is done last so that + //files are already properly fixified. + for(QStringList::Iterator it = quc.begin(); it != quc.end(); ++it) { + QString tmp_out = project->values((*it) + ".output").first(); + if(tmp_out.isEmpty()) + continue; + if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) { + QStringList &compilerInputs = project->values((*it) + ".input"); + // Don't generate compiler output if it doesn't have input. + if (compilerInputs.isEmpty() || project->values(compilerInputs.first()).isEmpty()) + continue; + if(tmp_out.indexOf("$") == -1) { + if(!verifyExtraCompiler((*it), QString())) //verify + continue; + QString out = fileFixify(tmp_out, Option::output_dir, Option::output_dir); + bool pre_dep = (project->values((*it) + ".CONFIG").indexOf("target_predeps") != -1); + if(project->variables().contains((*it) + ".variable_out")) { + const QStringList &var_out = project->variables().value((*it) + ".variable_out"); + for(int i = 0; i < var_out.size(); ++i) { + QString v = var_out.at(i); + if(v == QLatin1String("SOURCES")) + v = "GENERATED_SOURCES"; + else if(v == QLatin1String("OBJECTS")) + pre_dep = false; + QStringList &list = project->values(v); + if(!list.contains(out)) + list.append(out); + } + } else if(project->values((*it) + ".CONFIG").indexOf("no_link") == -1) { + QStringList &list = project->values("OBJECTS"); + pre_dep = false; + if(!list.contains(out)) + list.append(out); + } else { + QStringList &list = project->values("UNUSED_SOURCES"); + if(!list.contains(out)) + list.append(out); + } + if(pre_dep) { + QStringList &list = project->variables()["PRE_TARGETDEPS"]; + if(!list.contains(out)) + list.append(out); + } + } + } else { + QStringList &tmp = project->values((*it) + ".input"); + for(QStringList::Iterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + const QStringList inputs = project->values((*it2)); + for(QStringList::ConstIterator input = inputs.constBegin(); input != inputs.constEnd(); ++input) { + if((*input).isEmpty()) + continue; + QString in = Option::fixPathToTargetOS((*input), false); + if(!verifyExtraCompiler((*it), in)) //verify + continue; + QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString()); + out = fileFixify(out, Option::output_dir, Option::output_dir); + bool pre_dep = (project->values((*it) + ".CONFIG").indexOf("target_predeps") != -1); + if(project->variables().contains((*it) + ".variable_out")) { + const QStringList &var_out = project->variables().value((*it) + ".variable_out"); + for(int i = 0; i < var_out.size(); ++i) { + QString v = var_out.at(i); + if(v == QLatin1String("SOURCES")) + v = "GENERATED_SOURCES"; + else if(v == QLatin1String("OBJECTS")) + pre_dep = false; + QStringList &list = project->values(v); + if(!list.contains(out)) + list.append(out); + } + } else if(project->values((*it) + ".CONFIG").indexOf("no_link") == -1) { + pre_dep = false; + QStringList &list = project->values("OBJECTS"); + if(!list.contains(out)) + list.append(out); + } else { + QStringList &list = project->values("UNUSED_SOURCES"); + if(!list.contains(out)) + list.append(out); + } + if(pre_dep) { + QStringList &list = project->variables()["PRE_TARGETDEPS"]; + if(!list.contains(out)) + list.append(out); + } + } + } + } + } + + //handle dependencies + depHeuristicsCache.clear(); + if(!noIO()) { + // dependency paths + QStringList incDirs = v["DEPENDPATH"] + v["QMAKE_ABSOLUTE_SOURCE_PATH"]; + if(project->isActiveConfig("depend_includepath")) + incDirs += v["INCLUDEPATH"]; + if(!project->isActiveConfig("no_include_pwd")) { + QString pwd = qmake_getpwd(); + if(pwd.isEmpty()) + pwd = "."; + incDirs += pwd; + } + QList deplist; + for(QStringList::Iterator it = incDirs.begin(); it != incDirs.end(); ++it) + deplist.append(QMakeLocalFileName(unescapeFilePath((*it)))); + QMakeSourceFileInfo::setDependencyPaths(deplist); + debug_msg(1, "Dependency Directories: %s", incDirs.join(" :: ").toLatin1().constData()); + //cache info + if(project->isActiveConfig("qmake_cache")) { + QString cache_file; + if(!project->isEmpty("QMAKE_INTERNAL_CACHE_FILE")) { + cache_file = QDir::fromNativeSeparators(project->first("QMAKE_INTERNAL_CACHE_FILE")); + } else { + cache_file = ".qmake.internal.cache"; + if(project->isActiveConfig("build_pass")) + cache_file += ".BUILD." + project->first("BUILD_PASS"); + } + if(cache_file.indexOf('/') == -1) + cache_file.prepend(Option::output_dir + '/'); + QMakeSourceFileInfo::setCacheFile(cache_file); + } + + //add to dependency engine + for(x = 0; x < compilers.count(); ++x) { + const MakefileGenerator::Compiler &comp = compilers.at(x); + if(!(comp.flags & Compiler::CompilerNoCheckDeps)) + addSourceFiles(v[comp.variable_in], QMakeSourceFileInfo::SEEK_DEPS, + (QMakeSourceFileInfo::SourceFileType)comp.type); + } + } + + processSources(); //remove anything in SOURCES which is included (thus it need not be linked in) + + //all sources and generated sources must be turned into objects at some point (the one builtin compiler) + v["OBJECTS"] += createObjectList(v["SOURCES"]) + createObjectList(v["GENERATED_SOURCES"]); + + //Translation files + if(!project->isEmpty("TRANSLATIONS")) { + QStringList &trf = project->values("TRANSLATIONS"); + for(QStringList::Iterator it = trf.begin(); it != trf.end(); ++it) + (*it) = Option::fixPathToLocalOS((*it)); + } + + { //get the output_dir into the pwd + if(Option::output_dir != qmake_getpwd()) + project->values("INCLUDEPATH").append("."); + } + + //fix up the target deps + QString fixpaths[] = { QString("PRE_TARGETDEPS"), QString("POST_TARGETDEPS"), QString() }; + for(int path = 0; !fixpaths[path].isNull(); path++) { + QStringList &l = v[fixpaths[path]]; + for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) { + if(!(*val_it).isEmpty()) + (*val_it) = escapeDependencyPath(Option::fixPathToTargetOS((*val_it), false, false)); + } + } + + //extra depends + if(!project->isEmpty("DEPENDS")) { + QStringList &l = v["DEPENDS"]; + for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + QStringList files = v[(*it) + ".file"] + v[(*it) + ".files"]; //why do I support such evil things? + for(QStringList::Iterator file_it = files.begin(); file_it != files.end(); ++file_it) { + QStringList &out_deps = findDependencies(*file_it); + QStringList &in_deps = v[(*it) + ".depends"]; //even more evilness.. + for(QStringList::Iterator dep_it = in_deps.begin(); dep_it != in_deps.end(); ++dep_it) { + if(exists(*dep_it)) { + out_deps.append(*dep_it); + } else { + QString dir, regex = Option::fixPathToLocalOS((*dep_it)); + if(regex.lastIndexOf(Option::dir_sep) != -1) { + dir = regex.left(regex.lastIndexOf(Option::dir_sep) + 1); + regex.remove(0, dir.length()); + } + QStringList files = QDir(dir).entryList(QStringList(regex)); + if(files.isEmpty()) { + warn_msg(WarnLogic, "Dependency for [%s]: Not found %s", (*file_it).toLatin1().constData(), + (*dep_it).toLatin1().constData()); + } else { + for(int i = 0; i < files.count(); i++) + out_deps.append(dir + files[i]); + } + } + } + } + } + } + + // escape qmake command + QStringList &qmk = project->values("QMAKE_QMAKE"); + qmk = escapeFilePaths(qmk); +} + +bool +MakefileGenerator::processPrlFile(QString &file) +{ + bool ret = false, try_replace_file=false; + QString meta_file, orig_file = file; + if(QMakeMetaInfo::libExists(file)) { + try_replace_file = true; + meta_file = file; + file = ""; + } else { + QString tmp = file; + int ext = tmp.lastIndexOf('.'); + if(ext != -1) + tmp = tmp.left(ext); + meta_file = tmp; + } +// meta_file = fileFixify(meta_file); + QString real_meta_file = Option::fixPathToLocalOS(meta_file); + if(!meta_file.isEmpty()) { + QString f = fileFixify(real_meta_file, qmake_getpwd(), Option::output_dir); + if(QMakeMetaInfo::libExists(f)) { + QMakeMetaInfo libinfo; + debug_msg(1, "Processing PRL file: %s", real_meta_file.toLatin1().constData()); + if(!libinfo.readLib(f)) { + fprintf(stderr, "Error processing meta file: %s\n", real_meta_file.toLatin1().constData()); + } else if(project->isActiveConfig("no_read_prl_" + libinfo.type().toLower())) { + debug_msg(2, "Ignored meta file %s [%s]", real_meta_file.toLatin1().constData(), libinfo.type().toLatin1().constData()); + } else { + ret = true; + QMap &vars = libinfo.variables(); + for(QMap::Iterator it = vars.begin(); it != vars.end(); ++it) + processPrlVariable(it.key(), it.value()); + if(try_replace_file && !libinfo.isEmpty("QMAKE_PRL_TARGET")) { + QString dir; + int slsh = real_meta_file.lastIndexOf(Option::dir_sep); + if(slsh != -1) + dir = real_meta_file.left(slsh+1); + file = libinfo.first("QMAKE_PRL_TARGET"); + if(QDir::isRelativePath(file)) + file.prepend(dir); + } + } + } + if(ret) { + QString mf = QMakeMetaInfo::findLib(meta_file); + if(project->values("QMAKE_PRL_INTERNAL_FILES").indexOf(mf) == -1) + project->values("QMAKE_PRL_INTERNAL_FILES").append(mf); + if(project->values("QMAKE_INTERNAL_INCLUDED_FILES").indexOf(mf) == -1) + project->values("QMAKE_INTERNAL_INCLUDED_FILES").append(mf); + } + } + if(try_replace_file && file.isEmpty()) { +#if 0 + warn_msg(WarnLogic, "Found prl [%s] file with no target [%s]!", meta_file.toLatin1().constData(), + orig_file.toLatin1().constData()); +#endif + file = orig_file; + } + return ret; +} + +void +MakefileGenerator::filterIncludedFiles(const QString &var) +{ + QStringList &inputs = project->values(var); + for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ) { + if(QMakeSourceFileInfo::included((*input)) > 0) + input = inputs.erase(input); + else + ++input; + } +} + +void +MakefileGenerator::processPrlVariable(const QString &var, const QStringList &l) +{ + if(var == "QMAKE_PRL_LIBS") { + QString where = "QMAKE_LIBS"; + if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS")) + where = project->first("QMAKE_INTERNAL_PRL_LIBS"); + QStringList &out = project->values(where); + for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) { + if(out.indexOf((*it)) == -1) + out.append((*it)); + } + } else if(var == "QMAKE_PRL_DEFINES") { + QStringList &out = project->values("DEFINES"); + for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) { + if(out.indexOf((*it)) == -1 && + project->values("PRL_EXPORT_DEFINES").indexOf((*it)) == -1) + out.append((*it)); + } + } +} + +void +MakefileGenerator::processPrlFiles() +{ + QHash processed; + for(bool ret = false; true; ret = false) { + //read in any prl files included.. + QStringList l_out; + QString where = "QMAKE_LIBS"; + if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS")) + where = project->first("QMAKE_INTERNAL_PRL_LIBS"); + QStringList &l = project->values(where); + for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + QString file = (*it); + if(!processed.contains(file) && processPrlFile(file)) { + processed.insert(file, true); + ret = true; + } + if(!file.isEmpty()) + l_out.append(file); + } + if(ret) + l = l_out; + else + break; + } +} + +void +MakefileGenerator::writePrlFile(QTextStream &t) +{ + QString target = project->first("TARGET"); + int slsh = target.lastIndexOf(Option::dir_sep); + if(slsh != -1) + target.remove(0, slsh + 1); + QString bdir = Option::output_dir; + if(bdir.isEmpty()) + bdir = qmake_getpwd(); + t << "QMAKE_PRL_BUILD_DIR = " << bdir << endl; + + if(!project->projectFile().isEmpty() && project->projectFile() != "-") + t << "QMAKE_PRO_INPUT = " << project->projectFile().section('/', -1) << endl; + + if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH")) + t << "QMAKE_PRL_SOURCE_DIR = " << project->first("QMAKE_ABSOLUTE_SOURCE_PATH") << endl; + t << "QMAKE_PRL_TARGET = " << target << endl; + if(!project->isEmpty("PRL_EXPORT_DEFINES")) + t << "QMAKE_PRL_DEFINES = " << project->values("PRL_EXPORT_DEFINES").join(" ") << endl; + if(!project->isEmpty("PRL_EXPORT_CFLAGS")) + t << "QMAKE_PRL_CFLAGS = " << project->values("PRL_EXPORT_CFLAGS").join(" ") << endl; + if(!project->isEmpty("PRL_EXPORT_CXXFLAGS")) + t << "QMAKE_PRL_CXXFLAGS = " << project->values("PRL_EXPORT_CXXFLAGS").join(" ") << endl; + if(!project->isEmpty("CONFIG")) + t << "QMAKE_PRL_CONFIG = " << project->values("CONFIG").join(" ") << endl; + if(!project->isEmpty("TARGET_VERSION_EXT")) + t << "QMAKE_PRL_VERSION = " << project->first("TARGET_VERSION_EXT") << endl; + else if(!project->isEmpty("VERSION")) + t << "QMAKE_PRL_VERSION = " << project->first("VERSION") << endl; + if(project->isActiveConfig("staticlib") || project->isActiveConfig("explicitlib")) { + QStringList libs; + if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS")) + libs = project->values("QMAKE_INTERNAL_PRL_LIBS"); + else + libs << "QMAKE_LIBS"; //obvious one + if(project->isActiveConfig("staticlib")) + libs << "QMAKE_LIBS_PRIVATE"; + t << "QMAKE_PRL_LIBS = "; + for(QStringList::Iterator it = libs.begin(); it != libs.end(); ++it) + t << project->values((*it)).join(" ").replace('\\', "\\\\") << " "; + t << endl; + } +} + +bool +MakefileGenerator::writeProjectMakefile() +{ + usePlatformDir(); + QTextStream t(&Option::output); + + //header + writeHeader(t); + + QList targets; + { + QStringList builds = project->values("BUILDS"); + for(QStringList::Iterator it = builds.begin(); it != builds.end(); ++it) { + SubTarget *st = new SubTarget; + targets.append(st); + st->makefile = "$(MAKEFILE)." + (*it); + st->name = (*it); + st->target = project->isEmpty((*it) + ".target") ? (*it) : project->first((*it) + ".target"); + } + } + if(project->isActiveConfig("build_all")) { + t << "first: all" << endl; + QList::Iterator it; + + //install + t << "install: "; + for(it = targets.begin(); it != targets.end(); ++it) + t << (*it)->target << "-install "; + t << endl; + + //uninstall + t << "uninstall: "; + for(it = targets.begin(); it != targets.end(); ++it) + t << (*it)->target << "-uninstall "; + t << endl; + } else { + t << "first: " << targets.first()->target << endl + << "install: " << targets.first()->target << "-install" << endl + << "uninstall: " << targets.first()->target << "-uninstall" << endl; + } + + writeSubTargets(t, targets, SubTargetsNoFlags); + if(!project->isActiveConfig("no_autoqmake")) { + for(QList::Iterator it = targets.begin(); it != targets.end(); ++it) + t << (*it)->makefile << ": " << + Option::fixPathToTargetOS(fileFixify(Option::output.fileName())) << endl; + } + qDeleteAll(targets); + return true; +} + +bool +MakefileGenerator::write() +{ + if(!project) + return false; + writePrlFile(); + if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || //write makefile + Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) { + QTextStream t(&Option::output); + if(!writeMakefile(t)) { +#if 1 + warn_msg(WarnLogic, "Unable to generate output for: %s [TEMPLATE %s]", + Option::output.fileName().toLatin1().constData(), + project->first("TEMPLATE").toLatin1().constData()); + if(Option::output.exists()) + Option::output.remove(); +#endif + } + } + return true; +} + +QString +MakefileGenerator::prlFileName(bool fixify) +{ + QString ret = project->first("TARGET_PRL");; + if(ret.isEmpty()) + ret = project->first("TARGET"); + int slsh = ret.lastIndexOf(Option::dir_sep); + if(slsh != -1) + ret.remove(0, slsh); + if(!ret.endsWith(Option::prl_ext)) { + int dot = ret.indexOf('.'); + if(dot != -1) + ret.truncate(dot); + ret += Option::prl_ext; + } + if(!project->isEmpty("QMAKE_BUNDLE")) + ret.prepend(project->first("QMAKE_BUNDLE") + Option::dir_sep); + if(fixify) { + if(!project->isEmpty("DESTDIR")) + ret.prepend(project->first("DESTDIR")); + ret = Option::fixPathToLocalOS(fileFixify(ret, qmake_getpwd(), Option::output_dir)); + } + return ret; +} + +void +MakefileGenerator::writePrlFile() +{ + if((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || + Option::qmake_mode == Option::QMAKE_GENERATE_PRL) + && project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty() + && project->isActiveConfig("create_prl") + && (project->first("TEMPLATE") == "lib" + || project->first("TEMPLATE") == "vclib") + && !project->isActiveConfig("plugin")) { //write prl file + QString local_prl = prlFileName(); + QString prl = fileFixify(local_prl); + mkdir(fileInfo(local_prl).path()); + QFile ft(local_prl); + if(ft.open(QIODevice::WriteOnly)) { + project->values("ALL_DEPS").append(prl); + project->values("QMAKE_INTERNAL_PRL_FILE").append(prl); + QTextStream t(&ft); + writePrlFile(t); + } + } +} + +// Manipulate directories, so it's possible to build +// several cross-platform targets concurrently +void +MakefileGenerator::usePlatformDir() +{ + QString pltDir(project->first("QMAKE_PLATFORM_DIR")); + if(pltDir.isEmpty()) + return; + QChar sep = QDir::separator(); + QString slashPltDir = sep + pltDir; + + QString dirs[] = { QString("OBJECTS_DIR"), QString("DESTDIR"), QString("QMAKE_PKGCONFIG_DESTDIR"), + QString("SUBLIBS_DIR"), QString("DLLDESTDIR"), QString("QMAKE_LIBTOOL_DESTDIR"), + QString("PRECOMPILED_DIR"), QString("QMAKE_LIBDIR_QT"), QString() }; + for(int i = 0; !dirs[i].isEmpty(); ++i) { + QString filePath = project->first(dirs[i]); + project->values(dirs[i]) = QStringList(filePath + (filePath.isEmpty() ? pltDir : slashPltDir)); + } + + QString libs[] = { QString("QMAKE_LIBS_QT"), QString("QMAKE_LIBS_QT_THREAD"), QString("QMAKE_LIBS_QT_ENTRY"), QString() }; + for(int i = 0; !libs[i].isEmpty(); ++i) { + QString filePath = project->first(libs[i]); + int fpi = filePath.lastIndexOf(sep); + if(fpi == -1) + project->values(libs[i]).prepend(pltDir + sep); + else + project->values(libs[i]) = QStringList(filePath.left(fpi) + slashPltDir + filePath.mid(fpi)); + } +} + +void +MakefileGenerator::writeObj(QTextStream &t, const QString &src) +{ + QStringList &srcl = project->values(src); + QStringList objl = createObjectList(srcl); + + QStringList::Iterator oit = objl.begin(); + QStringList::Iterator sit = srcl.begin(); + QString stringSrc("$src"); + QString stringObj("$obj"); + for(;sit != srcl.end() && oit != objl.end(); ++oit, ++sit) { + if((*sit).isEmpty()) + continue; + + t << escapeDependencyPath((*oit)) << ": " << escapeDependencyPath((*sit)) << " " << escapeDependencyPaths(findDependencies((*sit))).join(" \\\n\t\t"); + + QString comp, cimp; + for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) { + if((*sit).endsWith((*cppit))) { + comp = "QMAKE_RUN_CXX"; + cimp = "QMAKE_RUN_CXX_IMP"; + break; + } + } + if(comp.isEmpty()) { + comp = "QMAKE_RUN_CC"; + cimp = "QMAKE_RUN_CC_IMP"; + } + bool use_implicit_rule = !project->isEmpty(cimp); + use_implicit_rule = false; + if(use_implicit_rule) { + if(!project->isEmpty("OBJECTS_DIR")) { + use_implicit_rule = false; + } else { + int dot = (*sit).lastIndexOf('.'); + if(dot == -1 || ((*sit).left(dot) + Option::obj_ext != (*oit))) + use_implicit_rule = false; + } + } + if (!use_implicit_rule && !project->isEmpty(comp)) { + QString p = var(comp), srcf(*sit); + p.replace(stringSrc, escapeFilePath(srcf)); + p.replace(stringObj, escapeFilePath((*oit))); + t << "\n\t" << p; + } + t << endl << endl; + } +} + +QString +MakefileGenerator::filePrefixRoot(const QString &root, const QString &path) +{ + QString ret(root + path); + if(path.length() > 2 && path[1] == ':') //c:\foo + ret = QString(path.mid(0, 2) + root + path.mid(2)); + while(ret.endsWith("\\")) + ret = ret.left(ret.length()-1); + return ret; +} + +void +MakefileGenerator::writeInstalls(QTextStream &t, const QString &installs, bool noBuild) +{ + QString rm_dir_contents("-$(DEL_FILE)"); + if (!isWindowsShell()) //ick + rm_dir_contents = "-$(DEL_FILE) -r"; + + QString all_installs, all_uninstalls; + QStringList &l = project->values(installs); + for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + QString pvar = (*it) + ".path"; + if(project->values((*it) + ".CONFIG").indexOf("no_path") == -1 && + project->values((*it) + ".CONFIG").indexOf("dummy_install") == -1 && + project->values(pvar).isEmpty()) { + warn_msg(WarnLogic, "%s is not defined: install target not created\n", pvar.toLatin1().constData()); + continue; + } + + bool do_default = true; + const QString root = "$(INSTALL_ROOT)"; + QString target, dst; + if(project->values((*it) + ".CONFIG").indexOf("no_path") == -1 && + project->values((*it) + ".CONFIG").indexOf("dummy_install") == -1) { + dst = fileFixify(unescapeFilePath(project->values(pvar).first()), FileFixifyAbsolute, false); + if(!dst.endsWith(Option::dir_sep)) + dst += Option::dir_sep; + } + dst = escapeFilePath(dst); + + QStringList tmp, uninst = project->values((*it) + ".uninstall"); + //other + tmp = project->values((*it) + ".extra"); + if(tmp.isEmpty()) + tmp = project->values((*it) + ".commands"); //to allow compatible name + if(!tmp.isEmpty()) { + do_default = false; + if(!target.isEmpty()) + target += "\n\t"; + target += tmp.join(" "); + } + //masks + tmp = findFilesInVPATH(project->values((*it) + ".files"), VPATH_NoFixify); + tmp = fileFixify(tmp, FileFixifyAbsolute); + if(!tmp.isEmpty()) { + if(!target.isEmpty()) + target += "\n"; + do_default = false; + for(QStringList::Iterator wild_it = tmp.begin(); wild_it != tmp.end(); ++wild_it) { + QString wild = Option::fixPathToTargetOS((*wild_it), false, false); + QString dirstr = qmake_getpwd(), filestr = wild; + int slsh = filestr.lastIndexOf(Option::dir_sep); + if(slsh != -1) { + dirstr = filestr.left(slsh+1); + filestr.remove(0, slsh+1); + } + if(!dirstr.endsWith(Option::dir_sep)) + dirstr += Option::dir_sep; + bool is_target = (wild == fileFixify(var("TARGET"), FileFixifyAbsolute)); + if(is_target || exists(wild)) { //real file or target + QString file = wild; + QFileInfo fi(fileInfo(wild)); + if(!target.isEmpty()) + target += "\t"; + QString dst_file = filePrefixRoot(root, dst); + if(fi.isDir() && project->isActiveConfig("copy_dir_files")) { + if(!dst_file.endsWith(Option::dir_sep)) + dst_file += Option::dir_sep; + dst_file += fi.fileName(); + } + QString cmd; + if (fi.isDir()) + cmd = "-$(INSTALL_DIR)"; + else if (is_target || fi.isExecutable()) + cmd = "-$(INSTALL_PROGRAM)"; + else + cmd = "-$(INSTALL_FILE)"; + cmd += " " + escapeFilePath(wild) + " " + dst_file + "\n"; + target += cmd; + if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") && + !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP")) + target += QString("\t-") + var("QMAKE_STRIP") + " " + + filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false)) + "\n"; + if(!uninst.isEmpty()) + uninst.append("\n\t"); + uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false))); + continue; + } + QString local_dirstr = Option::fixPathToLocalOS(dirstr, true); + QStringList files = QDir(local_dirstr).entryList(QStringList(filestr)); + const QStringList &installConfigValues = project->values((*it) + ".CONFIG"); + if (installConfigValues.contains("no_check_exist") && files.isEmpty()) { + if(!target.isEmpty()) + target += "\t"; + QString dst_file = filePrefixRoot(root, dst); + QFileInfo fi(fileInfo(wild)); + QString cmd; + if (installConfigValues.contains("directory")) { + cmd = QLatin1String("-$(INSTALL_DIR)"); + if (!dst_file.endsWith(Option::dir_sep)) + dst_file += Option::dir_sep; + dst_file += fi.fileName(); + } else if (installConfigValues.contains("executable")) { + cmd = QLatin1String("-$(INSTALL_PROGRAM)"); + } else if (installConfigValues.contains("data")) { + cmd = QLatin1String("-$(INSTALL_FILE)"); + } else { + cmd = QString(fi.isExecutable() ? "-$(INSTALL_PROGRAM)" : "-$(INSTALL_FILE)"); + } + cmd += " " + wild + " " + dst_file + "\n"; + target += cmd; + if(!uninst.isEmpty()) + uninst.append("\n\t"); + uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + filestr, FileFixifyAbsolute, false))); + } + for(int x = 0; x < files.count(); x++) { + QString file = files[x]; + if(file == "." || file == "..") //blah + continue; + if(!uninst.isEmpty()) + uninst.append("\n\t"); + uninst.append(rm_dir_contents + " " + filePrefixRoot(root, fileFixify(dst + file, FileFixifyAbsolute, false))); + QFileInfo fi(fileInfo(dirstr + file)); + if(!target.isEmpty()) + target += "\t"; + QString dst_file = filePrefixRoot(root, fileFixify(dst, FileFixifyAbsolute, false)); + if(fi.isDir() && project->isActiveConfig("copy_dir_files")) { + if(!dst_file.endsWith(Option::dir_sep)) + dst_file += Option::dir_sep; + dst_file += fi.fileName(); + } + QString cmd = QString(fi.isDir() ? "-$(INSTALL_DIR)" : "-$(INSTALL_FILE)") + " " + + dirstr + file + " " + dst_file + "\n"; + target += cmd; + if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") && + !fi.isDir() && fi.isExecutable() && !project->isEmpty("QMAKE_STRIP")) + target += QString("\t-") + var("QMAKE_STRIP") + " " + + filePrefixRoot(root, fileFixify(dst + file, FileFixifyAbsolute, false)) + + "\n"; + } + } + } + //default? + if(do_default) { + target = defaultInstall((*it)); + uninst = project->values((*it) + ".uninstall"); + } + + if(!target.isEmpty() || project->values((*it) + ".CONFIG").indexOf("dummy_install") != -1) { + if(noBuild || project->values((*it) + ".CONFIG").indexOf("no_build") != -1) + t << "install_" << (*it) << ":"; + else if(project->isActiveConfig("build_all")) + t << "install_" << (*it) << ": all"; + else + t << "install_" << (*it) << ": first"; + const QStringList &deps = project->values((*it) + ".depends"); + if(!deps.isEmpty()) { + for(QStringList::ConstIterator dep_it = deps.begin(); dep_it != deps.end(); ++dep_it) { + QString targ = var((*dep_it) + ".target"); + if(targ.isEmpty()) + targ = (*dep_it); + t << " " << escapeDependencyPath(targ); + } + } + if(project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + t << "\n\t"; + const QStringList &dirs = project->values(pvar); + for(QStringList::ConstIterator pit = dirs.begin(); pit != dirs.end(); ++pit) { + QString tmp_dst = fileFixify((*pit), FileFixifyAbsolute, false); + if (!isWindowsShell() && !tmp_dst.endsWith(Option::dir_sep)) + tmp_dst += Option::dir_sep; + t << mkdir_p_asstring(filePrefixRoot(root, tmp_dst)) << "\n\t"; + } + t << target << endl << endl; + if(!uninst.isEmpty()) { + t << "uninstall_" << (*it) << ": "; + if(project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + t << "\n\t" + << uninst.join(" ") << "\n\t" + << "-$(DEL_DIR) " << filePrefixRoot(root, dst) << " " << endl << endl; + } + t << endl; + + if(project->values((*it) + ".CONFIG").indexOf("no_default_install") == -1) { + all_installs += QString("install_") + (*it) + " "; + if(!uninst.isEmpty()) + all_uninstalls += "uninstall_" + (*it) + " "; + } + } else { + debug_msg(1, "no definition for install %s: install target not created",(*it).toLatin1().constData()); + } + } + t << "install: " << var("INSTALLDEPS") << " " << all_installs; + if(project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + t << "\n\n"; + t << "uninstall: " << all_uninstalls << " " << var("UNINSTALLDEPS"); + if(project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + t << "\n\n"; +} + +QString +MakefileGenerator::var(const QString &var) +{ + return val(project->values(var)); +} + +QString +MakefileGenerator::val(const QStringList &varList) +{ + return valGlue(varList, "", " ", ""); +} + +QString +MakefileGenerator::varGlue(const QString &var, const QString &before, const QString &glue, const QString &after) +{ + return valGlue(project->values(var), before, glue, after); +} + +QString +MakefileGenerator::valGlue(const QStringList &varList, const QString &before, const QString &glue, const QString &after) +{ + QString ret; + for(QStringList::ConstIterator it = varList.begin(); it != varList.end(); ++it) { + if(!(*it).isEmpty()) { + if(!ret.isEmpty()) + ret += glue; + ret += (*it); + } + } + return ret.isEmpty() ? QString("") : before + ret + after; +} + + +QString +MakefileGenerator::varList(const QString &var) +{ + return valList(project->values(var)); +} + +QString +MakefileGenerator::valList(const QStringList &varList) +{ + return valGlue(varList, "", " \\\n\t\t", ""); +} + +QStringList +MakefileGenerator::createObjectList(const QStringList &sources) +{ + QStringList ret; + QString objdir; + if(!project->values("OBJECTS_DIR").isEmpty()) + objdir = project->first("OBJECTS_DIR"); + for(QStringList::ConstIterator it = sources.begin(); it != sources.end(); ++it) { + QFileInfo fi(fileInfo(Option::fixPathToLocalOS((*it)))); + QString dir; + if(objdir.isEmpty() && project->isActiveConfig("object_with_source")) { + QString fName = Option::fixPathToTargetOS((*it), false); + int dl = fName.lastIndexOf(Option::dir_sep); + if(dl != -1) + dir = fName.left(dl + 1); + } else { + dir = objdir; + } + ret.append(dir + fi.completeBaseName() + Option::obj_ext); + } + return ret; +} + +ReplaceExtraCompilerCacheKey::ReplaceExtraCompilerCacheKey(const QString &v, const QStringList &i, const QStringList &o) +{ + static QString doubleColon = QLatin1String("::"); + + hash = 0; + pwd = qmake_getpwd(); + var = v; + { + QStringList il = i; + il.sort(); + in = il.join(doubleColon); + } + { + QStringList ol = o; + ol.sort(); + out = ol.join(doubleColon); + } +} + +bool ReplaceExtraCompilerCacheKey::operator==(const ReplaceExtraCompilerCacheKey &f) const +{ + return (hashCode() == f.hashCode() && + f.in == in && + f.out == out && + f.var == var && + f.pwd == pwd); +} + + +QString +MakefileGenerator::replaceExtraCompilerVariables(const QString &orig_var, const QStringList &in, const QStringList &out) +{ + //lazy cache + ReplaceExtraCompilerCacheKey cacheKey(orig_var, in, out); + QString cacheVal = extraCompilerVariablesCache.value(cacheKey); + if(!cacheVal.isNull()) + return cacheVal; + + //do the work + QString ret = orig_var; + QRegExp reg_var("\\$\\{.*\\}"); + reg_var.setMinimal(true); + for(int rep = 0; (rep = reg_var.indexIn(ret, rep)) != -1; ) { + QStringList val; + const QString var = ret.mid(rep + 2, reg_var.matchedLength() - 3); + bool filePath = false; + if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_"))) { + const QString varname = var.mid(10); + val += project->values(varname); + } + if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_VAR_FIRST_"))) { + const QString varname = var.mid(16); + val += project->first(varname); + } + + if(val.isEmpty() && !in.isEmpty()) { + if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_IN_"))) { + filePath = true; + const QString funcname = var.mid(19); + val += project->expand(funcname, QList() << in); + } else if(var == QLatin1String("QMAKE_FILE_BASE") || var == QLatin1String("QMAKE_FILE_IN_BASE")) { + //filePath = true; + for(int i = 0; i < in.size(); ++i) { + QFileInfo fi(fileInfo(Option::fixPathToLocalOS(in.at(i)))); + QString base = fi.completeBaseName(); + if(base.isNull()) + base = fi.fileName(); + val += base; + } + } else if(var == QLatin1String("QMAKE_FILE_EXT")) { + filePath = true; + for(int i = 0; i < in.size(); ++i) { + QFileInfo fi(fileInfo(Option::fixPathToLocalOS(in.at(i)))); + QString ext; + // Ensure complementarity with QMAKE_FILE_BASE + int baseLen = fi.completeBaseName().length(); + if(baseLen == 0) + ext = fi.fileName(); + else + ext = fi.fileName().remove(0, baseLen); + val += ext; + } + } else if(var == QLatin1String("QMAKE_FILE_PATH") || var == QLatin1String("QMAKE_FILE_IN_PATH")) { + filePath = true; + for(int i = 0; i < in.size(); ++i) + val += fileInfo(Option::fixPathToLocalOS(in.at(i))).path(); + } else if(var == QLatin1String("QMAKE_FILE_NAME") || var == QLatin1String("QMAKE_FILE_IN")) { + filePath = true; + for(int i = 0; i < in.size(); ++i) + val += fileInfo(Option::fixPathToLocalOS(in.at(i))).filePath(); + + } + } + if(val.isEmpty() && !out.isEmpty()) { + if(var.startsWith(QLatin1String("QMAKE_FUNC_FILE_OUT_"))) { + filePath = true; + const QString funcname = var.mid(20); + val += project->expand(funcname, QList() << out); + } else if(var == QLatin1String("QMAKE_FILE_OUT")) { + filePath = true; + for(int i = 0; i < out.size(); ++i) + val += fileInfo(Option::fixPathToLocalOS(out.at(i))).filePath(); + } else if(var == QLatin1String("QMAKE_FILE_OUT_BASE")) { + //filePath = true; + for(int i = 0; i < out.size(); ++i) { + QFileInfo fi(fileInfo(Option::fixPathToLocalOS(out.at(i)))); + QString base = fi.completeBaseName(); + if(base.isNull()) + base = fi.fileName(); + val += base; + } + } + } + if(val.isEmpty() && var.startsWith(QLatin1String("QMAKE_FUNC_"))) { + const QString funcname = var.mid(11); + val += project->expand(funcname, QList() << in << out); + } + + if(!val.isEmpty()) { + QString fullVal; + if(filePath) { + for(int i = 0; i < val.size(); ++i) { + const QString file = Option::fixPathToTargetOS(unescapeFilePath(val.at(i)), false); + if(!fullVal.isEmpty()) + fullVal += " "; + fullVal += escapeFilePath(file); + } + } else { + fullVal = val.join(" "); + } + ret.replace(rep, reg_var.matchedLength(), fullVal); + rep += fullVal.length(); + } else { + rep += reg_var.matchedLength(); + } + } + + //cache the value + extraCompilerVariablesCache.insert(cacheKey, ret); + return ret; +} + +bool +MakefileGenerator::verifyExtraCompiler(const QString &comp, const QString &file_unfixed) +{ + if(noIO()) + return false; + const QString file = Option::fixPathToLocalOS(file_unfixed); + + if(project->values(comp + ".CONFIG").indexOf("moc_verify") != -1) { + if(!file.isNull()) { + QMakeSourceFileInfo::addSourceFile(file, QMakeSourceFileInfo::SEEK_MOCS); + if(!mocable(file)) { + return false; + } else { + project->values("MOCABLES").append(file); + } + } + } else if(project->values(comp + ".CONFIG").indexOf("function_verify") != -1) { + QString tmp_out = project->values(comp + ".output").first(); + if(tmp_out.isEmpty()) + return false; + QStringList verify_function = project->values(comp + ".verify_function"); + if(verify_function.isEmpty()) + return false; + + for(int i = 0; i < verify_function.size(); ++i) { + bool invert = false; + QString verify = verify_function.at(i); + if(verify.at(0) == QLatin1Char('!')) { + invert = true; + verify = verify.mid(1); + } + + if(project->values(comp + ".CONFIG").indexOf("combine") != -1) { + bool pass = project->test(verify, QList() << QStringList(tmp_out) << QStringList(file)); + if(invert) + pass = !pass; + if(!pass) + return false; + } else { + QStringList &tmp = project->values(comp + ".input"); + for(QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it) { + QStringList &inputs = project->values((*it)); + for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) { + if((*input).isEmpty()) + continue; + QString in = fileFixify(Option::fixPathToTargetOS((*input), false)); + if(in == file) { + bool pass = project->test(verify, + QList() << QStringList(replaceExtraCompilerVariables(tmp_out, (*input), QString())) << + QStringList(file)); + if(invert) + pass = !pass; + if(!pass) + return false; + break; + } + } + } + } + } + } else if(project->values(comp + ".CONFIG").indexOf("verify") != -1) { + QString tmp_out = project->values(comp + ".output").first(); + if(tmp_out.isEmpty()) + return false; + QString tmp_cmd; + if(!project->isEmpty(comp + ".commands")) { + int argv0 = -1; + QStringList cmdline = project->values(comp + ".commands"); + for(int i = 0; i < cmdline.count(); ++i) { + if(!cmdline.at(i).contains('=')) { + argv0 = i; + break; + } + } + if(argv0 != -1) { + cmdline[argv0] = Option::fixPathToTargetOS(cmdline.at(argv0), false); + tmp_cmd = cmdline.join(" "); + } + } + + if(project->values(comp + ".CONFIG").indexOf("combine") != -1) { + QString cmd = replaceExtraCompilerVariables(tmp_cmd, QString(), tmp_out); + if(system(cmd.toLatin1().constData())) + return false; + } else { + QStringList &tmp = project->values(comp + ".input"); + for(QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it) { + QStringList &inputs = project->values((*it)); + for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) { + if((*input).isEmpty()) + continue; + QString in = fileFixify(Option::fixPathToTargetOS((*input), false)); + if(in == file) { + QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString()); + QString cmd = replaceExtraCompilerVariables(tmp_cmd, in, out); + if(system(cmd.toLatin1().constData())) + return false; + break; + } + } + } + } + } + return true; +} + +void +MakefileGenerator::writeExtraTargets(QTextStream &t) +{ + QStringList &qut = project->values("QMAKE_EXTRA_TARGETS"); + for(QStringList::Iterator it = qut.begin(); it != qut.end(); ++it) { + QString targ = var((*it) + ".target"), + cmd = var((*it) + ".commands"), deps; + if(targ.isEmpty()) + targ = (*it); + QStringList &deplist = project->values((*it) + ".depends"); + for(QStringList::Iterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) { + QString dep = var((*dep_it) + ".target"); + if(dep.isEmpty()) + dep = (*dep_it); + deps += " " + escapeDependencyPath(dep); + } + if(project->values((*it) + ".CONFIG").indexOf("fix_target") != -1) + targ = fileFixify(targ, Option::output_dir, Option::output_dir); + if(project->isEmpty("QMAKE_NOFORCE") && + project->values((*it) + ".CONFIG").indexOf("phony") != -1) + deps += QString(" ") + "FORCE"; + t << escapeDependencyPath(targ) << ":" << deps; + if(!cmd.isEmpty()) + t << "\n\t" << cmd; + t << endl << endl; + + project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(targ); + project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(targ)) << deps.split(" ", QString::SkipEmptyParts); + project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(targ)) << cmd; + } +} + +void +MakefileGenerator::writeExtraCompilerTargets(QTextStream &t) +{ + QString clean_targets; + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + QString tmp_out = fileFixify(project->values((*it) + ".output").first(), + Option::output_dir, Option::output_dir); + QString tmp_cmd; + if(!project->isEmpty((*it) + ".commands")) { + QStringList cmdline = project->values((*it) + ".commands"); + int argv0 = findExecutable(cmdline); + if(argv0 != -1) { + cmdline[argv0] = escapeFilePath(Option::fixPathToTargetOS(cmdline.at(argv0), false)); + tmp_cmd = cmdline.join(" "); + } + } + QStringList tmp_dep = project->values((*it) + ".depends"); + QString tmp_dep_cmd; + QString dep_cd_cmd; + if(!project->isEmpty((*it) + ".depend_command")) { + int argv0 = -1; + QStringList cmdline = project->values((*it) + ".depend_command"); + for(int i = 0; i < cmdline.count(); ++i) { + if(!cmdline.at(i).contains('=')) { + argv0 = i; + break; + } + } + if(argv0 != -1) { + const QString c = Option::fixPathToLocalOS(cmdline.at(argv0), true); + if(exists(c)) { + cmdline[argv0] = escapeFilePath(Option::fixPathToLocalOS(cmdline.at(argv0), false)); + } else { + cmdline[argv0] = escapeFilePath(cmdline.at(argv0)); + } + QFileInfo cmdFileInfo(cmdline[argv0]); + if (!cmdFileInfo.isAbsolute() || cmdFileInfo.exists()) + tmp_dep_cmd = cmdline.join(" "); + } + dep_cd_cmd = QLatin1String("cd ") + + escapeFilePath(Option::fixPathToLocalOS(Option::output_dir, false)) + + QLatin1String(" && "); + } + QStringList &vars = project->values((*it) + ".variables"); + if(tmp_out.isEmpty() || tmp_cmd.isEmpty()) + continue; + QStringList tmp_inputs; + { + const QStringList &comp_inputs = project->values((*it) + ".input"); + for(QStringList::ConstIterator it2 = comp_inputs.begin(); it2 != comp_inputs.end(); ++it2) { + const QStringList &tmp = project->values((*it2)); + for(QStringList::ConstIterator input = tmp.begin(); input != tmp.end(); ++input) { + QString in = Option::fixPathToTargetOS((*input), false); + if(verifyExtraCompiler((*it), in)) + tmp_inputs.append((*input)); + } + } + } + + t << "compiler_" << (*it) << "_make_all:"; + if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) { + // compilers with a combined input only have one output + QString input = project->values((*it) + ".output").first(); + t << " " << escapeDependencyPath(replaceExtraCompilerVariables(tmp_out, input, QString())); + } else { + for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) { + QString in = Option::fixPathToTargetOS((*input), false); + t << " " << escapeDependencyPath(replaceExtraCompilerVariables(tmp_out, (*input), QString())); + } + } + t << endl; + + if(project->values((*it) + ".CONFIG").indexOf("no_clean") == -1) { + QString tmp_clean = project->values((*it) + ".clean").join(" "); + QString tmp_clean_cmds = project->values((*it) + ".clean_commands").join(" "); + if(!tmp_inputs.isEmpty()) + clean_targets += QString("compiler_" + (*it) + "_clean "); + t << "compiler_" << (*it) << "_clean:"; + bool wrote_clean_cmds = false, wrote_clean = false; + if(tmp_clean_cmds.isEmpty()) { + wrote_clean_cmds = true; + } else if(tmp_clean_cmds.indexOf("${QMAKE_") == -1) { + t << "\n\t" << tmp_clean_cmds; + wrote_clean_cmds = true; + } + if(tmp_clean.isEmpty()) + tmp_clean = tmp_out; + if(tmp_clean.indexOf("${QMAKE_") == -1) { + t << "\n\t" << "-$(DEL_FILE) " << tmp_clean; + wrote_clean = true; + } + if(!wrote_clean_cmds || !wrote_clean) { + QStringList cleans; + const QString del_statement("-$(DEL_FILE)"); + if(!wrote_clean) { + if(project->isActiveConfig("no_delete_multiple_files")) { + for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) + cleans.append(" " + replaceExtraCompilerVariables(tmp_clean, (*input), + replaceExtraCompilerVariables(tmp_out, (*input), QString()))); + } else { + QString files, file; + const int commandlineLimit = 2047; // NT limit, expanded + for(int input = 0; input < tmp_inputs.size(); ++input) { + file = " " + replaceExtraCompilerVariables(tmp_clean, tmp_inputs.at(input), + replaceExtraCompilerVariables(tmp_out, tmp_inputs.at(input), QString())); + if(del_statement.length() + files.length() + + qMax(fixEnvVariables(file).length(), file.length()) > commandlineLimit) { + cleans.append(files); + files.clear(); + } + files += file; + } + if(!files.isEmpty()) + cleans.append(files); + } + } + if(!cleans.isEmpty()) + t << valGlue(cleans, "\n\t" + del_statement, "\n\t" + del_statement, ""); + if(!wrote_clean_cmds) { + for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) { + t << "\n\t" << replaceExtraCompilerVariables(tmp_clean_cmds, (*input), + replaceExtraCompilerVariables(tmp_out, (*input), QString())); + } + } + } + t << endl; + } + if(project->values((*it) + ".CONFIG").indexOf("combine") != -1) { + if(tmp_out.indexOf("${QMAKE_") != -1) { + warn_msg(WarnLogic, "QMAKE_EXTRA_COMPILERS(%s) with combine has variable output.", + (*it).toLatin1().constData()); + continue; + } + QStringList deps, inputs; + if(!tmp_dep.isEmpty()) + deps += fileFixify(tmp_dep, Option::output_dir, Option::output_dir); + for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) { + deps += findDependencies((*input)); + inputs += Option::fixPathToTargetOS((*input), false); + if(!tmp_dep_cmd.isEmpty() && doDepends()) { + char buff[256]; + QString dep_cmd = replaceExtraCompilerVariables(tmp_dep_cmd, (*input), + tmp_out); + dep_cmd = dep_cd_cmd + fixEnvVariables(dep_cmd); + if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) { + QString indeps; + while(!feof(proc)) { + int read_in = (int)fread(buff, 1, 255, proc); + if(!read_in) + break; + indeps += QByteArray(buff, read_in); + } + QT_PCLOSE(proc); + if(!indeps.isEmpty()) { + QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' '); + for(int i = 0; i < dep_cmd_deps.count(); ++i) { + QString &file = dep_cmd_deps[i]; + if(!exists(file)) { + QString localFile; + QList depdirs = QMakeSourceFileInfo::dependencyPaths(); + for(QList::Iterator it = depdirs.begin(); + it != depdirs.end(); ++it) { + if(exists((*it).real() + Option::dir_sep + file)) { + localFile = (*it).local() + Option::dir_sep + file; + break; + } + } + file = localFile; + } + if(!file.isEmpty()) + file = fileFixify(file); + } + deps += dep_cmd_deps; + } + } + } + } + for(int i = 0; i < inputs.size(); ) { + if(tmp_out == inputs.at(i)) + inputs.removeAt(i); + else + ++i; + } + for(int i = 0; i < deps.size(); ) { + if(tmp_out == deps.at(i)) + deps.removeAt(i); + else + ++i; + } + if (inputs.isEmpty()) + continue; + + QString cmd; + if (isForSymbianSbsv2()) { + // In sbsv2 the command inputs and outputs need to use absolute paths + cmd = replaceExtraCompilerVariables(tmp_cmd, + fileFixify(escapeFilePaths(inputs), FileFixifyAbsolute), + fileFixify(QStringList(tmp_out), FileFixifyAbsolute)); + } else { + cmd = replaceExtraCompilerVariables(tmp_cmd, escapeFilePaths(inputs), QStringList(tmp_out)); + } + + t << escapeDependencyPath(tmp_out) << ":"; + project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(tmp_out); + // compiler.CONFIG+=explicit_dependencies means that ONLY compiler.depends gets to cause Makefile dependencies + if(project->values((*it) + ".CONFIG").indexOf("explicit_dependencies") != -1) { + t << " " << valList(escapeDependencyPaths(fileFixify(tmp_dep, Option::output_dir, Option::output_dir))); + project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << tmp_dep; + } else { + t << " " << valList(escapeDependencyPaths(inputs)) << " " << valList(escapeDependencyPaths(deps)); + project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(tmp_out)) << inputs << deps; + } + t << "\n\t" << cmd << endl << endl; + project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(tmp_out)) << cmd; + continue; + } + for(QStringList::ConstIterator input = tmp_inputs.begin(); input != tmp_inputs.end(); ++input) { + QString in = Option::fixPathToTargetOS((*input), false); + QStringList deps = findDependencies((*input)); + deps += escapeDependencyPath(in); + QString out = replaceExtraCompilerVariables(tmp_out, (*input), QString()); + if(!tmp_dep.isEmpty()) { + QStringList pre_deps = fileFixify(tmp_dep, Option::output_dir, Option::output_dir); + for(int i = 0; i < pre_deps.size(); ++i) + deps += replaceExtraCompilerVariables(pre_deps.at(i), (*input), out); + } + QString cmd = replaceExtraCompilerVariables(tmp_cmd, (*input), out); + // NOTE: The var -> QMAKE_COMP_var replace feature is unsupported, do not use! + if (isForSymbianSbsv2()) { + // In sbsv2 the command inputs and outputs need to use absolute paths + cmd = replaceExtraCompilerVariables(tmp_cmd, + fileFixify((*input), FileFixifyAbsolute), + fileFixify(out, FileFixifyAbsolute)); + } else { + cmd = replaceExtraCompilerVariables(tmp_cmd, (*input), out); + } + for(QStringList::ConstIterator it3 = vars.constBegin(); it3 != vars.constEnd(); ++it3) + cmd.replace("$(" + (*it3) + ")", "$(QMAKE_COMP_" + (*it3)+")"); + if(!tmp_dep_cmd.isEmpty() && doDepends()) { + char buff[256]; + QString dep_cmd = replaceExtraCompilerVariables(tmp_dep_cmd, (*input), out); + dep_cmd = dep_cd_cmd + fixEnvVariables(dep_cmd); + if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) { + QString indeps; + while(!feof(proc)) { + int read_in = (int)fread(buff, 1, 255, proc); + if(!read_in) + break; + indeps += QByteArray(buff, read_in); + } + QT_PCLOSE(proc); + if(!indeps.isEmpty()) { + QStringList dep_cmd_deps = indeps.replace('\n', ' ').simplified().split(' '); + for(int i = 0; i < dep_cmd_deps.count(); ++i) { + QString &file = dep_cmd_deps[i]; + if(!exists(file)) { + QString localFile; + QList depdirs = QMakeSourceFileInfo::dependencyPaths(); + for(QList::Iterator it = depdirs.begin(); + it != depdirs.end(); ++it) { + if(exists((*it).real() + Option::dir_sep + file)) { + localFile = (*it).local() + Option::dir_sep + file; + break; + } + } + file = localFile; + } + if(!file.isEmpty()) + file = fileFixify(file); + } + deps += dep_cmd_deps; + } + } + //use the depend system to find includes of these included files + QStringList inc_deps; + for(int i = 0; i < deps.size(); ++i) { + const QString dep = deps.at(i); + if(QFile::exists(dep)) { + SourceFileType type = TYPE_UNKNOWN; + if(type == TYPE_UNKNOWN) { + for(QStringList::Iterator cit = Option::c_ext.begin(); + cit != Option::c_ext.end(); ++cit) { + if(dep.endsWith((*cit))) { + type = TYPE_C; + break; + } + } + } + if(type == TYPE_UNKNOWN) { + for(QStringList::Iterator cppit = Option::cpp_ext.begin(); + cppit != Option::cpp_ext.end(); ++cppit) { + if(dep.endsWith((*cppit))) { + type = TYPE_C; + break; + } + } + } + if(type == TYPE_UNKNOWN) { + for(QStringList::Iterator hit = Option::h_ext.begin(); + type == TYPE_UNKNOWN && hit != Option::h_ext.end(); ++hit) { + if(dep.endsWith((*hit))) { + type = TYPE_C; + break; + } + } + } + if(type != TYPE_UNKNOWN) { + if(!QMakeSourceFileInfo::containsSourceFile(dep, type)) + QMakeSourceFileInfo::addSourceFile(dep, type); + inc_deps += QMakeSourceFileInfo::dependencies(dep); + } + } + } + deps += inc_deps; + } + for(int i = 0; i < deps.size(); ) { + QString &dep = deps[i]; + dep = Option::fixPathToTargetOS(unescapeFilePath(dep), false); + if(out == dep) + deps.removeAt(i); + else + ++i; + } + t << escapeDependencyPath(out) << ": " << valList(escapeDependencyPaths(deps)) << "\n\t" + << cmd << endl << endl; + project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + (*it)) << escapeDependencyPath(out); + project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + (*it) + escapeDependencyPath(out)) << deps; + project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + (*it) + escapeDependencyPath(out)) << cmd; + } + } + t << "compiler_clean: " << clean_targets << endl << endl; +} + +void +MakefileGenerator::writeExtraCompilerVariables(QTextStream &t) +{ + bool first = true; + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + const QStringList &vars = project->values((*it) + ".variables"); + for(QStringList::ConstIterator varit = vars.begin(); varit != vars.end(); ++varit) { + if(first) { + t << "\n####### Custom Compiler Variables" << endl; + first = false; + } + t << "QMAKE_COMP_" << (*varit) << " = " + << valList(project->values((*varit))) << endl; + } + } + if(!first) + t << endl; +} + +void +MakefileGenerator::writeExtraVariables(QTextStream &t) +{ + bool first = true; + QMap &vars = project->variables(); + QStringList &exports = project->values("QMAKE_EXTRA_VARIABLES"); + for(QMap::Iterator it = vars.begin(); it != vars.end(); ++it) { + for(QStringList::Iterator exp_it = exports.begin(); exp_it != exports.end(); ++exp_it) { + QRegExp rx((*exp_it), Qt::CaseInsensitive, QRegExp::Wildcard); + if(rx.exactMatch(it.key())) { + if(first) { + t << "\n####### Custom Variables" << endl; + first = false; + } + t << "EXPORT_" << it.key() << " = " << it.value().join(" ") << endl; + } + } + } + if(!first) + t << endl; +} + +bool +MakefileGenerator::writeStubMakefile(QTextStream &t) +{ + t << "QMAKE = " << var("QMAKE_QMAKE") << endl; + QStringList &qut = project->values("QMAKE_EXTRA_TARGETS"); + for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it) + t << *it << " "; + //const QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName())); + t << "first all clean install distclean uninstall: " << "qmake" << endl + << "qmake_all:" << endl; + writeMakeQmake(t); + if(project->isEmpty("QMAKE_NOFORCE")) + t << "FORCE:" << endl << endl; + return true; +} + +bool +MakefileGenerator::writeMakefile(QTextStream &t) +{ + t << "####### Compile" << endl << endl; + writeObj(t, "SOURCES"); + writeObj(t, "GENERATED_SOURCES"); + + t << "####### Install" << endl << endl; + writeInstalls(t, "INSTALLS"); + + if(project->isEmpty("QMAKE_NOFORCE")) + t << "FORCE:" << endl << endl; + return true; +} + +QString MakefileGenerator::buildArgs(const QString &outdir) +{ + QString ret; + //special variables + if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH")) + ret += " QMAKE_ABSOLUTE_SOURCE_PATH=" + escapeFilePath(project->first("QMAKE_ABSOLUTE_SOURCE_PATH")); + + //warnings + else if(Option::warn_level == WarnNone) + ret += " -Wnone"; + else if(Option::warn_level == WarnAll) + ret += " -Wall"; + else if(Option::warn_level & WarnParser) + ret += " -Wparser"; + //other options + if(!Option::user_template.isEmpty()) + ret += " -t " + Option::user_template; + if(!Option::user_template_prefix.isEmpty()) + ret += " -tp " + Option::user_template_prefix; + if(!Option::mkfile::do_cache) + ret += " -nocache"; + if(!Option::mkfile::do_deps) + ret += " -nodepend"; + if(!Option::mkfile::do_dep_heuristics) + ret += " -nodependheuristics"; + if(!Option::mkfile::qmakespec_commandline.isEmpty()) + ret += " -spec " + specdir(outdir); + if (Option::target_mode_overridden) { + if (Option::target_mode == Option::TARG_MACX_MODE) + ret += " -macx"; + else if (Option::target_mode == Option::TARG_UNIX_MODE) + ret += " -unix"; + else if (Option::target_mode == Option::TARG_WIN_MODE) + ret += " -win32"; + } + + //configs + for(QStringList::Iterator it = Option::user_configs.begin(); + it != Option::user_configs.end(); ++it) + ret += " -config " + (*it); + //arguments + for(QStringList::Iterator it = Option::before_user_vars.begin(); + it != Option::before_user_vars.end(); ++it) { + if((*it).left(qstrlen("QMAKE_ABSOLUTE_SOURCE_PATH")) != "QMAKE_ABSOLUTE_SOURCE_PATH") + ret += " " + escapeFilePath((*it)); + } + if(Option::after_user_vars.count()) { + ret += " -after "; + for(QStringList::Iterator it = Option::after_user_vars.begin(); + it != Option::after_user_vars.end(); ++it) { + if((*it).left(qstrlen("QMAKE_ABSOLUTE_SOURCE_PATH")) != "QMAKE_ABSOLUTE_SOURCE_PATH") + ret += " " + escapeFilePath((*it)); + } + } + return ret; +} + +//could get stored argv, but then it would have more options than are +//probably necesary this will try to guess the bare minimum.. +QString MakefileGenerator::build_args(const QString &outdir) +{ + QString ret = "$(QMAKE)"; + + // general options and arguments + ret += buildArgs(outdir); + + //output + QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName())); + if(!ofile.isEmpty() && ofile != project->first("QMAKE_MAKEFILE")) + ret += " -o " + escapeFilePath(ofile); + + //inputs + ret += " " + escapeFilePath(fileFixify(project->projectFile(), outdir)); + + return ret; +} + +void +MakefileGenerator::writeHeader(QTextStream &t) +{ + t << "#############################################################################" << endl; + t << "# Makefile for building: " << escapeFilePath(var("TARGET")) << endl; + t << "# Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: "; + t << QDateTime::currentDateTime().toString() << endl; + t << "# Project: " << fileFixify(project->projectFile()) << endl; + t << "# Template: " << var("TEMPLATE") << endl; + if(!project->isActiveConfig("build_pass")) + t << "# Command: " << build_args().replace("$(QMAKE)", var("QMAKE_QMAKE")) << endl; + t << "#############################################################################" << endl; + t << endl; +} + +QList +MakefileGenerator::findSubDirsSubTargets() const +{ + QList targets; + { + const QStringList subdirs = project->values("SUBDIRS"); + for(int subdir = 0; subdir < subdirs.size(); ++subdir) { + QString fixedSubdir = subdirs[subdir]; + fixedSubdir = fixedSubdir.replace(QRegExp("[^a-zA-Z0-9_]"),"-"); + + SubTarget *st = new SubTarget; + st->name = subdirs[subdir]; + targets.append(st); + + bool fromFile = false; + QString file = subdirs[subdir]; + if(!project->isEmpty(fixedSubdir + ".file")) { + if(!project->isEmpty(fixedSubdir + ".subdir")) + warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s", + subdirs[subdir].toLatin1().constData()); + file = project->first(fixedSubdir + ".file"); + fromFile = true; + } else if(!project->isEmpty(fixedSubdir + ".subdir")) { + file = project->first(fixedSubdir + ".subdir"); + fromFile = false; + } else { + fromFile = file.endsWith(Option::pro_ext); + } + file = Option::fixPathToTargetOS(file); + + if(fromFile) { + int slsh = file.lastIndexOf(Option::dir_sep); + if(slsh != -1) { + st->in_directory = file.left(slsh+1); + st->profile = file.mid(slsh+1); + } else { + st->profile = file; + } + } else { + if(!file.isEmpty() && !project->isActiveConfig("subdir_first_pro")) + st->profile = file.section(Option::dir_sep, -1) + Option::pro_ext; + st->in_directory = file; + } + while(st->in_directory.endsWith(Option::dir_sep)) + st->in_directory.chop(1); + if(fileInfo(st->in_directory).isRelative()) + st->out_directory = st->in_directory; + else + st->out_directory = fileFixify(st->in_directory, qmake_getpwd(), Option::output_dir); + if(!project->isEmpty(fixedSubdir + ".makefile")) { + st->makefile = project->first(fixedSubdir + ".makefile"); + } else { + st->makefile = "$(MAKEFILE)"; + if(!st->profile.isEmpty()) { + QString basename = st->in_directory; + int new_slsh = basename.lastIndexOf(Option::dir_sep); + if(new_slsh != -1) + basename = basename.mid(new_slsh+1); + if(st->profile != basename + Option::pro_ext) + st->makefile += "." + st->profile.left(st->profile.length() - Option::pro_ext.length()); + } + } + if(!project->isEmpty(fixedSubdir + ".depends")) { + const QStringList depends = project->values(fixedSubdir + ".depends"); + for(int depend = 0; depend < depends.size(); ++depend) { + bool found = false; + for(int subDep = 0; subDep < subdirs.size(); ++subDep) { + if(subdirs[subDep] == depends.at(depend)) { + QString fixedSubDep = subdirs[subDep]; + fixedSubDep = fixedSubDep.replace(QRegExp("[^a-zA-Z0-9_]"),"-"); + if(!project->isEmpty(fixedSubDep + ".target")) { + st->depends += project->first(fixedSubDep + ".target"); + } else { + QString d = Option::fixPathToLocalOS(subdirs[subDep]); + if(!project->isEmpty(fixedSubDep + ".file")) + d = project->first(fixedSubDep + ".file"); + else if(!project->isEmpty(fixedSubDep + ".subdir")) + d = project->first(fixedSubDep + ".subdir"); + st->depends += "sub-" + d.replace(QRegExp("[^a-zA-Z0-9_]"),"-"); + } + found = true; + break; + } + } + if(!found) { + QString depend_str = depends.at(depend); + st->depends += depend_str.replace(QRegExp("[^a-zA-Z0-9_]"),"-"); + } + } + } + if(!project->isEmpty(fixedSubdir + ".target")) { + st->target = project->first(fixedSubdir + ".target"); + } else { + st->target = "sub-" + file; + st->target = st->target.replace(QRegExp("[^a-zA-Z0-9_]"),"-"); + } + } + } + return targets; +} + +void +MakefileGenerator::writeSubDirs(QTextStream &t) +{ + QList targets = findSubDirsSubTargets(); + t << "first: make_default" << endl; + int flags = SubTargetInstalls; + if(project->isActiveConfig("ordered")) + flags |= SubTargetOrdered; + writeSubTargets(t, targets, flags); + qDeleteAll(targets); +} + +void +MakefileGenerator::writeSubTargets(QTextStream &t, QList targets, int flags) +{ + // blasted includes + QStringList &qeui = project->values("QMAKE_EXTRA_INCLUDES"); + for(QStringList::Iterator qeui_it = qeui.begin(); qeui_it != qeui.end(); ++qeui_it) + t << "include " << (*qeui_it) << endl; + + if (!(flags & SubTargetSkipDefaultVariables)) { + QString ofile = Option::fixPathToTargetOS(Option::output.fileName()); + if(ofile.lastIndexOf(Option::dir_sep) != -1) + ofile.remove(0, ofile.lastIndexOf(Option::dir_sep) +1); + t << "MAKEFILE = " << ofile << endl; + /* Calling Option::fixPathToTargetOS() is necessary for MinGW/MSYS, which requires + * back-slashes to be turned into slashes. */ + t << "QMAKE = " << var("QMAKE_QMAKE") << endl; + t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl; + t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl; + t << "MKDIR = " << var("QMAKE_MKDIR") << endl; + t << "COPY = " << var("QMAKE_COPY") << endl; + t << "COPY_FILE = " << var("QMAKE_COPY_FILE") << endl; + t << "COPY_DIR = " << var("QMAKE_COPY_DIR") << endl; + t << "INSTALL_FILE = " << var("QMAKE_INSTALL_FILE") << endl; + t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl; + t << "INSTALL_DIR = " << var("QMAKE_INSTALL_DIR") << endl; + t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl; + t << "SYMLINK = " << var("QMAKE_SYMBOLIC_LINK") << endl; + t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl; + t << "MOVE = " << var("QMAKE_MOVE") << endl; + t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl; + t << "MKDIR = " << var("QMAKE_MKDIR") << endl; + t << "SUBTARGETS = "; // subtargets are sub-directory + for(int target = 0; target < targets.size(); ++target) + t << " \\\n\t\t" << targets.at(target)->target; + t << endl << endl; + } + writeExtraVariables(t); + + QStringList targetSuffixes; + const QString abs_source_path = project->first("QMAKE_ABSOLUTE_SOURCE_PATH"); + if (!(flags & SubTargetSkipDefaultTargets)) { + targetSuffixes << "make_default" << "make_first" << "all" << "clean" << "distclean" + << QString((flags & SubTargetInstalls) ? "install_subtargets" : "install") + << QString((flags & SubTargetInstalls) ? "uninstall_subtargets" : "uninstall"); + } + + // generate target rules + for(int target = 0; target < targets.size(); ++target) { + SubTarget *subtarget = targets.at(target); + QString in_directory = subtarget->in_directory; + if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep)) + in_directory += Option::dir_sep; + QString out_directory = subtarget->out_directory; + if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep)) + out_directory += Option::dir_sep; + if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path)) + out_directory = Option::output_dir + out_directory.mid(abs_source_path.length()); + + QString mkfile = subtarget->makefile; + if(!in_directory.isEmpty()) + mkfile.prepend(out_directory); + + QString in_directory_cdin, in_directory_cdout, out_directory_cdin, out_directory_cdout; +#define MAKE_CD_IN_AND_OUT(directory) \ + if(!directory.isEmpty()) { \ + if(project->isActiveConfig("cd_change_global")) { \ + directory ## _cdin = "\n\tcd " + directory + "\n\t"; \ + QDir pwd(Option::output_dir); \ + QStringList in = directory.split(Option::dir_sep), out; \ + for(int i = 0; i < in.size(); i++) { \ + if(in.at(i) == "..") \ + out.prepend(fileInfo(pwd.path()).fileName()); \ + else if(in.at(i) != ".") \ + out.prepend(".."); \ + pwd.cd(in.at(i)); \ + } \ + directory ## _cdout = "\n\t@cd " + out.join(Option::dir_sep); \ + } else { \ + directory ## _cdin = "\n\tcd " + directory + " && "; \ + } \ + } else { \ + directory ## _cdin = "\n\t"; \ + } + MAKE_CD_IN_AND_OUT(in_directory); + MAKE_CD_IN_AND_OUT(out_directory); + + //qmake it + if(!subtarget->profile.isEmpty()) { + QString out = subtarget->makefile; + QString in = fileFixify(in_directory + subtarget->profile, FileFixifyAbsolute); + if(out.startsWith(in_directory)) + out = out.mid(in_directory.length()); + t << mkfile << ": " << "\n\t"; + if(!in_directory.isEmpty()) { + t << mkdir_p_asstring(out_directory) + << out_directory_cdin + << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out + << in_directory_cdout << endl; + } else { + t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl; + } + t << subtarget->target << "-qmake_all: "; + if(project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + t << "\n\t"; + if(!in_directory.isEmpty()) { + t << mkdir_p_asstring(out_directory) + << out_directory_cdin + << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out + << in_directory_cdout << endl; + } else { + t << "$(QMAKE) " << in << buildArgs(in_directory) << " -o " << out << endl; + } + } + + QString makefilein = " -f " + subtarget->makefile; + + { //actually compile + t << subtarget->target << ": " << mkfile; + if(!subtarget->depends.isEmpty()) + t << " " << valList(subtarget->depends); + if(project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + t << out_directory_cdin + << "$(MAKE)" << makefilein + << out_directory_cdout << endl; + } + + for(int suffix = 0; suffix < targetSuffixes.size(); ++suffix) { + QString s = targetSuffixes.at(suffix); + if(s == "install_subtargets") + s = "install"; + else if(s == "uninstall_subtargets") + s = "uninstall"; + else if(s == "make_first") + s = "first"; + else if(s == "make_default") + s = QString(); + + if(flags & SubTargetOrdered) { + t << subtarget->target << "-" << targetSuffixes.at(suffix) << "-ordered: " << mkfile; + if(target) + t << " " << targets.at(target-1)->target << "-" << targetSuffixes.at(suffix) << "-ordered "; + if(project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + t << out_directory_cdin + << "$(MAKE)" << makefilein << " " << s + << out_directory_cdout << endl; + } + t << subtarget->target << "-" << targetSuffixes.at(suffix) << ": " << mkfile; + if(!subtarget->depends.isEmpty()) + t << " " << valGlue(subtarget->depends, QString(), "-" + targetSuffixes.at(suffix) + " ", + "-"+targetSuffixes.at(suffix)); + if(project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + t << out_directory_cdin + << "$(MAKE)" << makefilein << " " << s + << out_directory_cdout << endl; + } + } + t << endl; + + if (!(flags & SubTargetSkipDefaultTargets)) { + if(project->values("QMAKE_INTERNAL_QMAKE_DEPS").indexOf("qmake_all") == -1) + project->values("QMAKE_INTERNAL_QMAKE_DEPS").append("qmake_all"); + + writeMakeQmake(t); + + t << "qmake_all:"; + if(!targets.isEmpty()) { + for(QList::Iterator it = targets.begin(); it != targets.end(); ++it) { + if(!(*it)->profile.isEmpty()) + t << " " << (*it)->target << "-" << "qmake_all"; + } + } + if(project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + if(project->isActiveConfig("no_empty_targets")) + t << "\n\t" << "@cd ."; + t << endl << endl; + } + + for(int s = 0; s < targetSuffixes.size(); ++s) { + QString suffix = targetSuffixes.at(s); + if(!(flags & SubTargetInstalls) && suffix.endsWith("install")) + continue; + + t << suffix << ":"; + for(int target = 0; target < targets.size(); ++target) { + SubTarget *subTarget = targets.at(target); + if((suffix == "make_first" || suffix == "make_default") + && project->values(subTarget->name + ".CONFIG").indexOf("no_default_target") != -1) { + continue; + } + QString targetRule = subTarget->target + "-" + suffix; + if(flags & SubTargetOrdered) + targetRule += "-ordered"; + t << " " << targetRule; + } + if(suffix == "all" || suffix == "make_first") + t << varGlue("ALL_DEPS"," "," ",""); + if(suffix == "clean") + t << varGlue("CLEAN_DEPS"," "," ",""); + if(project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + t << endl; + if(suffix == "clean") { + t << varGlue("QMAKE_CLEAN","\t-$(DEL_FILE) ","\n\t-$(DEL_FILE) ", "\n"); + } else if(suffix == "distclean") { + QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName())); + if(!ofile.isEmpty()) + t << "\t-$(DEL_FILE) " << ofile << endl; + t << varGlue("QMAKE_DISTCLEAN","\t-$(DEL_FILE) "," ","\n"); + } else if(project->isActiveConfig("no_empty_targets")) { + t << "\t" << "@cd ." << endl; + } + } + + // user defined targets + QStringList &qut = project->values("QMAKE_EXTRA_TARGETS"); + for(QStringList::Iterator qut_it = qut.begin(); qut_it != qut.end(); ++qut_it) { + QString targ = var((*qut_it) + ".target"), + cmd = var((*qut_it) + ".commands"), deps; + if(targ.isEmpty()) + targ = (*qut_it); + t << endl; + + QStringList &deplist = project->values((*qut_it) + ".depends"); + for(QStringList::Iterator dep_it = deplist.begin(); dep_it != deplist.end(); ++dep_it) { + QString dep = var((*dep_it) + ".target"); + if(dep.isEmpty()) + dep = Option::fixPathToTargetOS(*dep_it, false); + deps += " " + dep; + } + if(project->values((*qut_it) + ".CONFIG").indexOf("recursive") != -1) { + QSet recurse; + if(project->isSet((*qut_it) + ".recurse")) { + recurse = project->values((*qut_it) + ".recurse").toSet(); + } else { + for(int target = 0; target < targets.size(); ++target) + recurse.insert(targets.at(target)->name); + } + for(int target = 0; target < targets.size(); ++target) { + SubTarget *subtarget = targets.at(target); + QString in_directory = subtarget->in_directory; + if(!in_directory.isEmpty() && !in_directory.endsWith(Option::dir_sep)) + in_directory += Option::dir_sep; + QString out_directory = subtarget->out_directory; + if(!out_directory.isEmpty() && !out_directory.endsWith(Option::dir_sep)) + out_directory += Option::dir_sep; + if(!abs_source_path.isEmpty() && out_directory.startsWith(abs_source_path)) + out_directory = Option::output_dir + out_directory.mid(abs_source_path.length()); + + if(!recurse.contains(subtarget->name)) + continue; + QString mkfile = subtarget->makefile; + if(!in_directory.isEmpty()) { + if(!out_directory.endsWith(Option::dir_sep)) + mkfile.prepend(out_directory + Option::dir_sep); + else + mkfile.prepend(out_directory); + } + QString out_directory_cdin, out_directory_cdout; + MAKE_CD_IN_AND_OUT(out_directory); + + //don't need the makefile arg if it isn't changed + QString makefilein; + if(subtarget->makefile != "$(MAKEFILE)") + makefilein = " -f " + subtarget->makefile; + + //write the rule/depends + if(flags & SubTargetOrdered) { + const QString dep = subtarget->target + "-" + (*qut_it) + "_ordered"; + t << dep << ": " << mkfile; + if(target) + t << " " << targets.at(target-1)->target << "-" << (*qut_it) << "_ordered "; + deps += " " + dep; + } else { + const QString dep = subtarget->target + "-" + (*qut_it); + t << dep << ": " << mkfile; + if(!subtarget->depends.isEmpty()) + t << " " << valGlue(subtarget->depends, QString(), "-" + (*qut_it) + " ", "-" + (*qut_it)); + deps += " " + dep; + } + + QString sub_targ = targ; + if(project->isSet((*qut_it) + ".recurse_target")) + sub_targ = project->first((*qut_it) + ".recurse_target"); + + //write the commands + if(!out_directory.isEmpty()) { + t << out_directory_cdin + << "$(MAKE)" << makefilein << " " << sub_targ + << out_directory_cdout << endl; + } else { + t << "\n\t" + << "$(MAKE)" << makefilein << " " << sub_targ << endl; + } + } + } + if(project->isEmpty("QMAKE_NOFORCE") && + project->values((*qut_it) + ".CONFIG").indexOf("phony") != -1) + deps += " FORCE"; + t << targ << ":" << deps << "\n"; + if(!cmd.isEmpty()) + t << "\t" << cmd << endl; + } + + if(flags & SubTargetInstalls) { + project->values("INSTALLDEPS") += "install_subtargets"; + project->values("UNINSTALLDEPS") += "uninstall_subtargets"; + writeInstalls(t, "INSTALLS", true); + } + + if(project->isEmpty("QMAKE_NOFORCE")) + t << "FORCE:" << endl << endl; +} + +void +MakefileGenerator::writeMakeQmake(QTextStream &t) +{ + QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName())); + if(project->isEmpty("QMAKE_FAILED_REQUIREMENTS") && !project->isEmpty("QMAKE_INTERNAL_PRL_FILE")) { + QStringList files = fileFixify(Option::mkfile::project_files); + t << escapeDependencyPath(project->first("QMAKE_INTERNAL_PRL_FILE")) << ": " << "\n\t" + << "@$(QMAKE) -prl " << buildArgs() << " " << files.join(" ") << endl; + } + + QString pfile = project->projectFile(); + if(pfile != "(stdin)") { + QString qmake = build_args(); + if(!ofile.isEmpty() && !project->isActiveConfig("no_autoqmake")) { + t << escapeFilePath(ofile) << ": " << escapeDependencyPath(fileFixify(pfile)) << " "; + if(Option::mkfile::do_cache) + t << escapeDependencyPath(fileFixify(Option::mkfile::cachefile)) << " "; + if(!specdir().isEmpty()) { + if(exists(Option::fixPathToLocalOS(specdir()+QDir::separator()+"qmake.conf"))) + t << escapeDependencyPath(specdir() + Option::dir_sep + "qmake.conf") << " "; + } + const QStringList &included = project->values("QMAKE_INTERNAL_INCLUDED_FILES"); + t << escapeDependencyPaths(included).join(" \\\n\t\t") << "\n\t" + << qmake << endl; + for(int include = 0; include < included.size(); ++include) { + const QString i(included.at(include)); + if(!i.isEmpty()) + t << i << ":" << endl; + } + } + if(project->first("QMAKE_ORIG_TARGET") != "qmake") { + t << "qmake: " << + project->values("QMAKE_INTERNAL_QMAKE_DEPS").join(" \\\n\t\t"); + if(project->isEmpty("QMAKE_NOFORCE")) + t << " FORCE"; + t << "\n\t" << "@" << qmake << endl << endl; + } + } +} + +QFileInfo +MakefileGenerator::fileInfo(QString file) const +{ + static QHash *cache = 0; + static QFileInfo noInfo = QFileInfo(); + if(!cache) { + cache = new QHash; + qmakeAddCacheClear(qmakeDeleteCacheClear >, (void**)&cache); + } + FileInfoCacheKey cacheKey(file); + QFileInfo value = cache->value(cacheKey, noInfo); + if (value != noInfo) + return value; + + QFileInfo fi(file); + if (fi.exists()) + cache->insert(cacheKey, fi); + return fi; +} + +QString +MakefileGenerator::unescapeFilePath(const QString &path) const +{ + QString ret = path; + if(!ret.isEmpty()) { + if(ret.contains(QLatin1String("\\ "))) + ret.replace(QLatin1String("\\ "), QLatin1String(" ")); + if(ret.contains(QLatin1Char('\"'))) + ret.remove(QLatin1Char('\"')); + } + return ret; +} + +QStringList +MakefileGenerator::escapeFilePaths(const QStringList &paths) const +{ + QStringList ret; + for(int i = 0; i < paths.size(); ++i) + ret.append(escapeFilePath(paths.at(i))); + return ret; +} + +QStringList +MakefileGenerator::escapeDependencyPaths(const QStringList &paths) const +{ + QStringList ret; + for(int i = 0; i < paths.size(); ++i) + ret.append(escapeDependencyPath(paths.at(i))); + return ret; +} + +QStringList +MakefileGenerator::unescapeFilePaths(const QStringList &paths) const +{ + QStringList ret; + for(int i = 0; i < paths.size(); ++i) + ret.append(unescapeFilePath(paths.at(i))); + return ret; +} + +QStringList +MakefileGenerator::fileFixify(const QStringList& files, const QString &out_dir, const QString &in_dir, + FileFixifyType fix, bool canon) const +{ + if(files.isEmpty()) + return files; + QStringList ret; + for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) { + if(!(*it).isEmpty()) + ret << fileFixify((*it), out_dir, in_dir, fix, canon); + } + return ret; +} + +QString +MakefileGenerator::fileFixify(const QString& file, const QString &out_d, const QString &in_d, + FileFixifyType fix, bool canon) const +{ + if(file.isEmpty()) + return file; + QString ret = unescapeFilePath(file); + + //do the fixin' + QString orig_file = ret; + if(ret.startsWith(QLatin1Char('~'))) { + if(ret.startsWith(QLatin1String("~/"))) + ret = QDir::homePath() + ret.mid(1); + else + warn_msg(WarnLogic, "Unable to expand ~ in %s", ret.toLatin1().constData()); + } + if(fix == FileFixifyAbsolute || (fix == FileFixifyDefault && project->isActiveConfig("no_fixpath"))) { + if(fix == FileFixifyAbsolute && QDir::isRelativePath(ret)) { //already absolute + QString pwd = qmake_getpwd(); + if (!pwd.endsWith(QLatin1Char('/'))) + pwd += QLatin1Char('/'); + ret.prepend(pwd); + } + ret = Option::fixPathToTargetOS(ret, false, canon); + } else { //fix it.. + QString out_dir = QDir(Option::output_dir).absoluteFilePath(out_d); + QString in_dir = QDir(qmake_getpwd()).absoluteFilePath(in_d); + { + QFileInfo in_fi(fileInfo(in_dir)); + if(in_fi.exists()) + in_dir = in_fi.canonicalFilePath(); + QFileInfo out_fi(fileInfo(out_dir)); + if(out_fi.exists()) + out_dir = out_fi.canonicalFilePath(); + } + + QString qfile(Option::fixPathToLocalOS(ret, true, canon)); + QFileInfo qfileinfo(fileInfo(qfile)); + if(out_dir != in_dir || !qfileinfo.isRelative()) { + if(qfileinfo.isRelative()) { + ret = in_dir + "/" + qfile; + qfileinfo.setFile(ret); + } + ret = Option::fixPathToTargetOS(ret, false, canon); + if(canon && qfileinfo.exists() && + file == Option::fixPathToTargetOS(ret, true, canon)) + ret = Option::fixPathToTargetOS(qfileinfo.canonicalFilePath()); + QString match_dir = Option::fixPathToTargetOS(out_dir, false, canon); + if(ret == match_dir) { + ret = ""; + } else if(ret.startsWith(match_dir + Option::dir_sep)) { + ret = ret.mid(match_dir.length() + Option::dir_sep.length()); + } else { + //figure out the depth + int depth = 4; + if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || + Option::qmake_mode == Option::QMAKE_GENERATE_PRL) { + if(project && !project->isEmpty("QMAKE_PROJECT_DEPTH")) + depth = project->first("QMAKE_PROJECT_DEPTH").toInt(); + else if(Option::mkfile::cachefile_depth != -1) + depth = Option::mkfile::cachefile_depth; + } + //calculate how much can be removed + QString dot_prefix; + for(int i = 1; i <= depth; i++) { + int sl = match_dir.lastIndexOf(Option::dir_sep); + if(sl == -1) + break; + match_dir = match_dir.left(sl); + if(match_dir.isEmpty()) + break; + if(ret.startsWith(match_dir + Option::dir_sep)) { + //concat + int remlen = ret.length() - (match_dir.length() + 1); + if(remlen < 0) + remlen = 0; + ret = ret.right(remlen); + //prepend + for(int o = 0; o < i; o++) + dot_prefix += ".." + Option::dir_sep; + break; + } + } + ret.prepend(dot_prefix); + } + } else { + ret = Option::fixPathToTargetOS(ret, false, canon); + } + } + if(ret.isEmpty()) + ret = "."; + debug_msg(3, "Fixed[%d,%d] %s :: to :: %s [%s::%s] [%s::%s]", fix, canon, orig_file.toLatin1().constData(), + ret.toLatin1().constData(), in_d.toLatin1().constData(), out_d.toLatin1().constData(), + qmake_getpwd().toLatin1().constData(), Option::output_dir.toLatin1().constData()); + return ret; +} + +void +MakefileGenerator::checkMultipleDefinition(const QString &f, const QString &w) +{ + if(!(Option::warn_level & WarnLogic)) + return; + QString file = f; + int slsh = f.lastIndexOf(Option::dir_sep); + if(slsh != -1) + file.remove(0, slsh + 1); + QStringList &l = project->values(w); + for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) { + QString file2((*val_it)); + slsh = file2.lastIndexOf(Option::dir_sep); + if(slsh != -1) + file2.remove(0, slsh + 1); + if(file2 == file) { + warn_msg(WarnLogic, "Found potential symbol conflict of %s (%s) in %s", + file.toLatin1().constData(), (*val_it).toLatin1().constData(), w.toLatin1().constData()); + break; + } + } +} + +QMakeLocalFileName +MakefileGenerator::fixPathForFile(const QMakeLocalFileName &file, bool forOpen) +{ + if(forOpen) + return QMakeLocalFileName(fileFixify(file.real(), qmake_getpwd(), Option::output_dir)); + return QMakeLocalFileName(fileFixify(file.real())); +} + +QFileInfo +MakefileGenerator::findFileInfo(const QMakeLocalFileName &file) +{ + return fileInfo(file.local()); +} + +QMakeLocalFileName +MakefileGenerator::findFileForDep(const QMakeLocalFileName &dep, const QMakeLocalFileName &file) +{ + QMakeLocalFileName ret; + if(!project->isEmpty("SKIP_DEPENDS")) { + bool found = false; + QStringList &nodeplist = project->values("SKIP_DEPENDS"); + for(QStringList::Iterator it = nodeplist.begin(); + it != nodeplist.end(); ++it) { + QRegExp regx((*it)); + if(regx.indexIn(dep.local()) != -1) { + found = true; + break; + } + } + if(found) + return ret; + } + + ret = QMakeSourceFileInfo::findFileForDep(dep, file); + if(!ret.isNull()) + return ret; + + //these are some "hacky" heuristics it will try to do on an include + //however these can be turned off at runtime, I'm not sure how + //reliable these will be, most likely when problems arise turn it off + //and see if they go away.. + if(Option::mkfile::do_dep_heuristics) { + if(depHeuristicsCache.contains(dep.real())) + return depHeuristicsCache[dep.real()]; + + if(Option::output_dir != qmake_getpwd() + && QDir::isRelativePath(dep.real())) { //is it from the shadow tree + QList depdirs = QMakeSourceFileInfo::dependencyPaths(); + depdirs.prepend(fileInfo(file.real()).absoluteDir().path()); + QString pwd = qmake_getpwd(); + if(pwd.at(pwd.length()-1) != '/') + pwd += '/'; + for(int i = 0; i < depdirs.count(); i++) { + QString dir = depdirs.at(i).real(); + if(!QDir::isRelativePath(dir) && dir.startsWith(pwd)) + dir = dir.mid(pwd.length()); + if(QDir::isRelativePath(dir)) { + if(!dir.endsWith(Option::dir_sep)) + dir += Option::dir_sep; + QString shadow = fileFixify(dir + dep.local(), pwd, Option::output_dir); + if(exists(shadow)) { + ret = QMakeLocalFileName(shadow); + goto found_dep_from_heuristic; + } + } + } + } + { //is it from an EXTRA_TARGET + const QString dep_basename = dep.local().section(Option::dir_sep, -1); + QStringList &qut = project->values("QMAKE_EXTRA_TARGETS"); + for(QStringList::Iterator it = qut.begin(); it != qut.end(); ++it) { + QString targ = var((*it) + ".target"); + if(targ.isEmpty()) + targ = (*it); + QString out = Option::fixPathToTargetOS(targ); + if(out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) { + ret = QMakeLocalFileName(out); + goto found_dep_from_heuristic; + } + } + } + { //is it from an EXTRA_COMPILER + const QString dep_basename = dep.local().section(Option::dir_sep, -1); + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + QString tmp_out = project->values((*it) + ".output").first(); + if(tmp_out.isEmpty()) + continue; + QStringList &tmp = project->values((*it) + ".input"); + for(QStringList::Iterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + QStringList &inputs = project->values((*it2)); + for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) { + QString out = Option::fixPathToTargetOS(unescapeFilePath(replaceExtraCompilerVariables(tmp_out, (*input), QString()))); + if(out == dep.real() || out.section(Option::dir_sep, -1) == dep_basename) { + ret = QMakeLocalFileName(fileFixify(out, qmake_getpwd(), Option::output_dir)); + goto found_dep_from_heuristic; + } + } + } + } + } + found_dep_from_heuristic: + depHeuristicsCache.insert(dep.real(), ret); + } + return ret; +} + +QStringList +&MakefileGenerator::findDependencies(const QString &file) +{ + const QString fixedFile = fileFixify(file); + if(!dependsCache.contains(fixedFile)) { +#if 1 + QStringList deps = QMakeSourceFileInfo::dependencies(file); + if(file != fixedFile) + deps += QMakeSourceFileInfo::dependencies(fixedFile); +#else + QStringList deps = QMakeSourceFileInfo::dependencies(fixedFile); +#endif + dependsCache.insert(fixedFile, deps); + } + return dependsCache[fixedFile]; +} + +QString +MakefileGenerator::specdir(const QString &outdir) +{ +#if 0 + if(!spec.isEmpty()) + return spec; +#endif + spec = fileFixify(Option::mkfile::qmakespec, outdir); + return spec; +} + +bool +MakefileGenerator::openOutput(QFile &file, const QString &build) const +{ + { + QString outdir; + if(!file.fileName().isEmpty()) { + if(QDir::isRelativePath(file.fileName())) + file.setFileName(Option::output_dir + "/" + file.fileName()); //pwd when qmake was run + QFileInfo fi(fileInfo(file.fileName())); + if(fi.isDir()) + outdir = file.fileName() + '/'; + } + if(!outdir.isEmpty() || file.fileName().isEmpty()) { + QString fname = "Makefile"; + if(!project->isEmpty("MAKEFILE")) + fname = project->first("MAKEFILE"); + file.setFileName(outdir + fname); + } + } + if(QDir::isRelativePath(file.fileName())) { + QString fname = Option::output_dir; //pwd when qmake was run + if(!fname.endsWith("/")) + fname += "/"; + fname += file.fileName(); + file.setFileName(fname); + } + if(!build.isEmpty()) + file.setFileName(file.fileName() + "." + build); + if(project->isEmpty("QMAKE_MAKEFILE")) + project->values("QMAKE_MAKEFILE").append(file.fileName()); + int slsh = file.fileName().lastIndexOf('/'); + if(slsh != -1) + mkdir(file.fileName().left(slsh)); + if(file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + QFileInfo fi(fileInfo(Option::output.fileName())); + QString od; + if(fi.isSymLink()) + od = fileInfo(fi.readLink()).absolutePath(); + else + od = fi.path(); + od = QDir::fromNativeSeparators(od); + if(QDir::isRelativePath(od)) { + QString dir = Option::output_dir; + if (!dir.endsWith('/') && !od.isEmpty()) + dir += '/'; + od.prepend(dir); + } + Option::output_dir = od; + return true; + } + return false; +} + +QString +MakefileGenerator::pkgConfigFileName(bool fixify) +{ + QString ret = var("TARGET"); + int slsh = ret.lastIndexOf(Option::dir_sep); + if(slsh != -1) + ret = ret.right(ret.length() - slsh - 1); + if(ret.startsWith("lib")) + ret = ret.mid(3); + int dot = ret.indexOf('.'); + if(dot != -1) + ret = ret.left(dot); + ret += Option::pkgcfg_ext; + QString subdir = project->first("QMAKE_PKGCONFIG_DESTDIR"); + if(!subdir.isEmpty()) { + // initOutPaths() appends dir_sep, but just to be safe.. + if (!subdir.endsWith(Option::dir_sep)) + ret.prepend(Option::dir_sep); + ret.prepend(subdir); + } + if(fixify) { + if(QDir::isRelativePath(ret) && !project->isEmpty("DESTDIR")) + ret.prepend(project->first("DESTDIR")); + ret = Option::fixPathToLocalOS(fileFixify(ret, qmake_getpwd(), Option::output_dir)); + } + return ret; +} + +QString +MakefileGenerator::pkgConfigPrefix() const +{ + if(!project->isEmpty("QMAKE_PKGCONFIG_PREFIX")) + return project->first("QMAKE_PKGCONFIG_PREFIX"); + return QLibraryInfo::location(QLibraryInfo::PrefixPath); +} + +QString +MakefileGenerator::pkgConfigFixPath(QString path) const +{ + QString prefix = pkgConfigPrefix(); + if(path.startsWith(prefix)) + path = path.replace(prefix, "${prefix}"); + return path; +} + +void +MakefileGenerator::writePkgConfigFile() +{ + QString fname = pkgConfigFileName(), lname = fname; + mkdir(fileInfo(fname).path()); + int slsh = lname.lastIndexOf(Option::dir_sep); + if(slsh != -1) + lname = lname.right(lname.length() - slsh - 1); + QFile ft(fname); + if(!ft.open(QIODevice::WriteOnly)) + return; + project->values("ALL_DEPS").append(fileFixify(fname)); + QTextStream t(&ft); + + QString prefix = pkgConfigPrefix(); + QString libDir = project->first("QMAKE_PKGCONFIG_LIBDIR"); + if(libDir.isEmpty()) + libDir = prefix + Option::dir_sep + "lib" + Option::dir_sep; + QString includeDir = project->first("QMAKE_PKGCONFIG_INCDIR"); + if(includeDir.isEmpty()) + includeDir = prefix + "/include"; + + t << "prefix=" << prefix << endl; + t << "exec_prefix=${prefix}\n" + << "libdir=" << pkgConfigFixPath(libDir) << "\n" + << "includedir=" << pkgConfigFixPath(includeDir) << endl; + // non-standard entry. Provides useful info normally only + // contained in the internal .qmake.cache file + t << varGlue("CONFIG", "qt_config=", " ", "") << endl; + + //extra PKGCONFIG variables + const QStringList &pkgconfig_vars = project->values("QMAKE_PKGCONFIG_VARIABLES"); + for(int i = 0; i < pkgconfig_vars.size(); ++i) { + QString var = project->first(pkgconfig_vars.at(i) + ".name"), + val = project->values(pkgconfig_vars.at(i) + ".value").join(" "); + if(var.isEmpty()) + continue; + if(val.isEmpty()) { + const QStringList &var_vars = project->values(pkgconfig_vars.at(i) + ".variable"); + for(int v = 0; v < var_vars.size(); ++v) { + const QStringList &vars = project->values(var_vars.at(v)); + for(int var = 0; var < vars.size(); ++var) { + if(!val.isEmpty()) + val += " "; + val += pkgConfigFixPath(vars.at(var)); + } + } + } + t << var << "=" << val << endl; + } + + t << endl; + + QString name = project->first("QMAKE_PKGCONFIG_NAME"); + if(name.isEmpty()) { + name = project->first("QMAKE_ORIG_TARGET").toLower(); + name.replace(0, 1, name[0].toUpper()); + } + t << "Name: " << name << endl; + QString desc = project->values("QMAKE_PKGCONFIG_DESCRIPTION").join(" "); + if(desc.isEmpty()) { + if(name.isEmpty()) { + desc = project->first("QMAKE_ORIG_TARGET").toLower(); + desc.replace(0, 1, desc[0].toUpper()); + } else { + desc = name; + } + if(project->first("TEMPLATE") == "lib") { + if(project->isActiveConfig("plugin")) + desc += " Plugin"; + else + desc += " Library"; + } else if(project->first("TEMPLATE") == "app") { + desc += " Application"; + } + } + t << "Description: " << desc << endl; + t << "Version: " << project->first("VERSION") << endl; + + // libs + t << "Libs: "; + QString pkgConfiglibDir; + QString pkgConfiglibName; + if (Option::target_mode == Option::TARG_MACX_MODE && project->isActiveConfig("lib_bundle")) { + pkgConfiglibDir = "-F${libdir}"; + QString bundle; + if (!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME")) + bundle = unescapeFilePath(project->first("QMAKE_FRAMEWORK_BUNDLE_NAME")); + else + bundle = unescapeFilePath(project->first("TARGET")); + int suffix = bundle.lastIndexOf(".framework"); + if (suffix != -1) + bundle = bundle.left(suffix); + pkgConfiglibName = "-framework " + bundle + " "; + } else { + pkgConfiglibDir = "-L${libdir}"; + pkgConfiglibName = "-l" + lname.left(lname.length()-Option::libtool_ext.length()); + } + t << pkgConfiglibDir << " " << pkgConfiglibName << " " << endl; + + QStringList libs; + if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS")) { + libs = project->values("QMAKE_INTERNAL_PRL_LIBS"); + } else { + libs << "QMAKE_LIBS"; //obvious one + } + libs << "QMAKE_LIBS_PRIVATE"; + libs << "QMAKE_LFLAGS_THREAD"; //not sure about this one, but what about things like -pthread? + t << "Libs.private: "; + for(QStringList::ConstIterator it = libs.begin(); it != libs.end(); ++it) { + t << project->values((*it)).join(" ") << " "; + } + t << endl; + + // flags + // ### too many + t << "Cflags: " + // << var("QMAKE_CXXFLAGS") << " " + << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ") + << project->values("PRL_EXPORT_CXXFLAGS").join(" ") + << project->values("QMAKE_PKGCONFIG_CFLAGS").join(" ") + // << varGlue("DEFINES","-D"," -D"," ") + << " -I${includedir}" << endl; + + // requires + const QString requires = project->values("QMAKE_PKGCONFIG_REQUIRES").join(" "); + if (!requires.isEmpty()) { + t << "Requires: " << requires << endl; + } + + t << endl; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/makefile.h b/qmake/generators/makefile.h new file mode 100644 index 0000000000..e0ef52d251 --- /dev/null +++ b/qmake/generators/makefile.h @@ -0,0 +1,309 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAKEFILE_H +#define MAKEFILE_H + +#include "option.h" +#include "project.h" +#include "makefiledeps.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#ifdef Q_OS_WIN32 +#define QT_POPEN _popen +#define QT_PCLOSE _pclose +#else +#define QT_POPEN popen +#define QT_PCLOSE pclose +#endif + +struct ReplaceExtraCompilerCacheKey +{ + mutable uint hash; + QString var, in, out, pwd; + ReplaceExtraCompilerCacheKey(const QString &v, const QStringList &i, const QStringList &o); + bool operator==(const ReplaceExtraCompilerCacheKey &f) const; + inline uint hashCode() const { + if(!hash) + hash = qHash(var) | qHash(in) | qHash(out) /*| qHash(pwd)*/; + return hash; + } +}; +inline uint qHash(const ReplaceExtraCompilerCacheKey &f) { return f.hashCode(); } + +struct ReplaceExtraCompilerCacheKey; + +class MakefileGenerator : protected QMakeSourceFileInfo +{ + QString spec; + bool init_opath_already, init_already, no_io; + QHash init_compiler_already; + QString build_args(const QString &outdir=QString()); + void checkMultipleDefinition(const QString &, const QString &); + + //internal caches + mutable QHash depHeuristicsCache; + mutable QHash dependsCache; + mutable QHash extraCompilerVariablesCache; + +protected: + QStringList createObjectList(const QStringList &sources); + + //makefile style generator functions + void writeObj(QTextStream &, const QString &src); + void writeInstalls(QTextStream &t, const QString &installs, bool noBuild=false); + void writeHeader(QTextStream &t); + void writeSubDirs(QTextStream &t); + void writeMakeQmake(QTextStream &t); + void writeExtraVariables(QTextStream &t); + void writeExtraTargets(QTextStream &t); + void writeExtraCompilerTargets(QTextStream &t); + void writeExtraCompilerVariables(QTextStream &t); + virtual bool writeStubMakefile(QTextStream &t); + virtual bool writeMakefile(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; + QStringList depends; + }; + enum SubTargetFlags { + SubTargetInstalls=0x01, + SubTargetOrdered=0x02, + SubTargetSkipDefaultVariables=0x04, + SubTargetSkipDefaultTargets=0x08, + + SubTargetsNoFlags=0x00 + }; + QList findSubDirsSubTargets() const; + void writeSubTargets(QTextStream &t, QList subtargets, int flags); + + //extra compiler interface + bool verifyExtraCompiler(const QString &c, const QString &f); + virtual QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &); + inline QString replaceExtraCompilerVariables(const QString &val, const QString &in, const QString &out) + { return replaceExtraCompilerVariables(val, QStringList(in), QStringList(out)); } + + //interface to the source file info + QMakeLocalFileName fixPathForFile(const QMakeLocalFileName &, bool); + QMakeLocalFileName findFileForDep(const QMakeLocalFileName &, const QMakeLocalFileName &); + QFileInfo findFileInfo(const QMakeLocalFileName &); + QMakeProject *project; + + //escape + virtual QString unescapeFilePath(const QString &path) const; + virtual QStringList unescapeFilePaths(const QStringList &path) const; + virtual QString escapeFilePath(const QString &path) const { return path; } + virtual QString escapeDependencyPath(const QString &path) const { return escapeFilePath(path); } + QStringList escapeFilePaths(const QStringList &paths) const; + QStringList escapeDependencyPaths(const QStringList &paths) const; + + //initialization + void verifyCompilers(); + virtual void init(); + void initOutPaths(); + struct Compiler + { + QString variable_in; + enum CompilerFlag { + CompilerNoFlags = 0x00, + CompilerBuiltin = 0x01, + CompilerNoCheckDeps = 0x02, + CompilerRemoveNoExist = 0x04 + }; + uint flags, type; + }; + void initCompiler(const Compiler &comp); + enum VPATHFlag { + VPATH_NoFlag = 0x00, + VPATH_WarnMissingFiles = 0x01, + VPATH_RemoveMissingFiles = 0x02, + VPATH_NoFixify = 0x04 + }; + QStringList findFilesInVPATH(QStringList 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(' '), 0); } + + bool mkdir(const QString &dir) const; + QString mkdir_p_asstring(const QString &dir, bool escape=true) const; + + //subclasses can use these to query information about how the generator was "run" + QString buildArgs(const QString &outdir=QString()); + QString specdir(const QString &outdir=QString()); + + virtual QStringList &findDependencies(const QString &file); + virtual bool doDepends() const { return Option::mkfile::do_deps; } + + void filterIncludedFiles(const QString &); + virtual void processSources() { + filterIncludedFiles("SOURCES"); + filterIncludedFiles("GENERATED_SOURCES"); + } + + //for cross-platform dependent directories + virtual void usePlatformDir(); + + //for installs + virtual QString defaultInstall(const QString &); + + //for prl + QString prlFileName(bool fixify=true); + void writePrlFile(); + bool processPrlFile(QString &); + virtual void processPrlVariable(const QString &, const QStringList &); + virtual void processPrlFiles(); + virtual void writePrlFile(QTextStream &); + + //make sure libraries are found + virtual bool findLibraries(); + + //for retrieving values and lists of values + virtual QString var(const QString &var); + QString varGlue(const QString &var, const QString &before, const QString &glue, const QString &after); + QString varList(const QString &var); + QString val(const QStringList &varList); + QString valGlue(const QStringList &varList, const QString &before, const QString &glue, const QString &after); + QString valList(const QStringList &varList); + + QString filePrefixRoot(const QString &, const QString &); + + //file fixification to unify all file names into a single pattern + enum FileFixifyType { FileFixifyAbsolute, FileFixifyRelative, FileFixifyDefault }; + QString fileFixify(const QString& file, const QString &out_dir=QString(), + const QString &in_dir=QString(), FileFixifyType fix=FileFixifyDefault, bool canon=true) const; + inline QString fileFixify(const QString& file, FileFixifyType fix, bool canon=true) const + { return fileFixify(file, QString(), QString(), fix, canon); } + QStringList fileFixify(const QStringList& files, const QString &out_dir=QString(), + const QString &in_dir=QString(), FileFixifyType fix=FileFixifyDefault, bool canon=true) const; + inline QStringList fileFixify(const QStringList& files, FileFixifyType fix, bool canon=true) const + { return fileFixify(files, QString(), QString(), fix, canon); } + +public: + MakefileGenerator(); + virtual ~MakefileGenerator(); + 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; + virtual bool isWindowsShell() const { return Option::host_mode == Option::HOST_WIN_MODE; } + virtual bool isForSymbianSbsv2() const { return false; } // FIXME: killme - i'm ugly! + + /* The next one is to avoid having SymbianCommonGenerator as a virtually + inherited class of this class. Instead it is without a base class + (avoiding the virtual inheritance problem), and is allowed to use + functions defined in here. + + To illustrate: + +-------------------+ + | MakefileGenerator | + +-------------------+ + ^ ^ + | | + | X <-- Avoid this inheritance + | | + +------------------------+ +------------------------+ + | UnixMakefileGenerator | | SymbianCommonGenerator | + | or | | | + | NmakeMakefileGenerator | | | + +------------------------+ +------------------------+ + ^ ^ + | | + | | + | | + +-----------------------------+ + | SymbianMakefileTemplate<> | + +-----------------------------+ + + We want to avoid the famous diamond problem, because if we have that, we need + virtual inheritance, which not all compilers like. Therefore, we break the + link as illustrated. Instead, we have a pointer to MakefileGenerator inside + SymbianCommonGenerator, and allows full access by making it a friend here. + */ + friend class SymbianCommonGenerator; +}; + +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 bool MakefileGenerator::findLibraries() +{ return true; } + +inline MakefileGenerator::~MakefileGenerator() +{ } + +QT_END_NAMESPACE + +#endif // MAKEFILE_H diff --git a/qmake/generators/makefiledeps.cpp b/qmake/generators/makefiledeps.cpp new file mode 100644 index 0000000000..56dc9917c0 --- /dev/null +++ b/qmake/generators/makefiledeps.cpp @@ -0,0 +1,961 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "makefiledeps.h" +#include "option.h" +#include +#include +#include +#include +#include +#if defined(Q_OS_UNIX) +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#include +#endif + +QT_BEGIN_NAMESPACE + +#if 1 +#define qmake_endOfLine(c) (c == '\r' || c == '\n') +#else +inline bool qmake_endOfLine(const char &c) { return (c == '\r' || c == '\n'); } +#endif + +//#define QMAKE_USE_CACHE + +QMakeLocalFileName::QMakeLocalFileName(const QString &name) : is_null(name.isNull()) +{ + if(!name.isEmpty()) { + if(name.at(0) == QLatin1Char('"') && name.at(name.length()-2) == QLatin1Char('"')) + real_name = name.mid(1, name.length()-2); + else + real_name = name; + } +} +const QString +&QMakeLocalFileName::local() const +{ + if(!is_null && local_name.isNull()) + local_name = Option::fixPathToLocalOS(real_name, true); + return local_name; +} + +struct SourceDependChildren; +struct SourceFile { + SourceFile() : deps(0), type(QMakeSourceFileInfo::TYPE_UNKNOWN), + mocable(0), traversed(0), exists(1), + moc_checked(0), dep_checked(0), included_count(0) { } + ~SourceFile(); + QMakeLocalFileName file; + SourceDependChildren *deps; + QMakeSourceFileInfo::SourceFileType type; + uint mocable : 1, traversed : 1, exists : 1; + uint moc_checked : 1, dep_checked : 1; + uchar included_count; +}; +struct SourceDependChildren { + SourceFile **children; + int num_nodes, used_nodes; + SourceDependChildren() : children(0), num_nodes(0), used_nodes(0) { } + ~SourceDependChildren() { if(children) free(children); children = 0; } + void addChild(SourceFile *s) { + if(num_nodes <= used_nodes) { + num_nodes += 200; + children = (SourceFile**)realloc(children, sizeof(SourceFile*)*(num_nodes)); + } + children[used_nodes++] = s; + } +}; +SourceFile::~SourceFile() { delete deps; } +class SourceFiles { + int hash(const char *); +public: + SourceFiles(); + ~SourceFiles(); + + SourceFile *lookupFile(const char *); + inline SourceFile *lookupFile(const QString &f) { return lookupFile(f.toLatin1().constData()); } + inline SourceFile *lookupFile(const QMakeLocalFileName &f) { return lookupFile(f.local().toLatin1().constData()); } + void addFile(SourceFile *, const char *k=0, bool own=true); + + struct SourceFileNode { + SourceFileNode() : key(0), next(0), file(0), own_file(1) { } + ~SourceFileNode() { + delete [] key; + if(own_file) + delete file; + } + char *key; + SourceFileNode *next; + SourceFile *file; + uint own_file : 1; + } **nodes; + int num_nodes; +}; +SourceFiles::SourceFiles() +{ + nodes = (SourceFileNode**)malloc(sizeof(SourceFileNode*)*(num_nodes=3037)); + for(int n = 0; n < num_nodes; n++) + nodes[n] = 0; +} + +SourceFiles::~SourceFiles() +{ + for(int n = 0; n < num_nodes; n++) { + for(SourceFileNode *next = nodes[n]; next;) { + SourceFileNode *next_next = next->next; + delete next; + next = next_next; + } + } + free(nodes); +} + +int SourceFiles::hash(const char *file) +{ + uint h = 0, g; + while (*file) { + h = (h << 4) + *file; + if ((g = (h & 0xf0000000)) != 0) + h ^= g >> 23; + h &= ~g; + file++; + } + return h; +} + +SourceFile *SourceFiles::lookupFile(const char *file) +{ + int h = hash(file) % num_nodes; + for(SourceFileNode *p = nodes[h]; p; p = p->next) { + if(!strcmp(p->key, file)) + return p->file; + } + return 0; +} + +void SourceFiles::addFile(SourceFile *p, const char *k, bool own_file) +{ + QByteArray ba = p->file.local().toLatin1(); + if(!k) + k = ba; + int h = hash(k) % num_nodes; + SourceFileNode *pn = new SourceFileNode; + pn->own_file = own_file; + pn->key = qstrdup(k); + pn->file = p; + pn->next = nodes[h]; + nodes[h] = pn; +} + +void QMakeSourceFileInfo::dependTreeWalker(SourceFile *node, SourceDependChildren *place) +{ + if(node->traversed || !node->exists) + return; + place->addChild(node); + node->traversed = true; //set flag + if(node->deps) { + for(int i = 0; i < node->deps->used_nodes; i++) + dependTreeWalker(node->deps->children[i], place); + } +} + +void QMakeSourceFileInfo::setDependencyPaths(const QList &l) +{ + // Ensure that depdirs does not contain the same paths several times, to minimize the stats + QList ll; + for (int i = 0; i < l.count(); ++i) { + if (!ll.contains(l.at(i))) + ll.append(l.at(i)); + } + depdirs = ll; +} + +QStringList QMakeSourceFileInfo::dependencies(const QString &file) +{ + QStringList ret; + if(!files) + return ret; + + if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file))) { + if(node->deps) { + /* I stick them into a SourceDependChildren here because it is faster to just + iterate over the list to stick them in the list, and reset the flag, then it is + to loop over the tree (about 50% faster I saw) --Sam */ + SourceDependChildren place; + for(int i = 0; i < node->deps->used_nodes; i++) + dependTreeWalker(node->deps->children[i], &place); + if(place.children) { + for(int i = 0; i < place.used_nodes; i++) { + place.children[i]->traversed = false; //reset flag + ret.append(place.children[i]->file.real()); + } + } + } + } + return ret; +} + +int +QMakeSourceFileInfo::included(const QString &file) +{ + if (!files) + return 0; + + if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file))) + return node->included_count; + return 0; +} + +bool QMakeSourceFileInfo::mocable(const QString &file) +{ + if(SourceFile *node = files->lookupFile(QMakeLocalFileName(file))) + return node->mocable; + return false; +} + +QMakeSourceFileInfo::QMakeSourceFileInfo(const QString &cf) +{ + //dep_mode + dep_mode = Recursive; + + //quick project lookups + includes = files = 0; + files_changed = false; + + //buffer + spare_buffer = 0; + spare_buffer_size = 0; + + //cache + cachefile = cf; + if(!cachefile.isEmpty()) + loadCache(cachefile); +} + +QMakeSourceFileInfo::~QMakeSourceFileInfo() +{ + //cache + if(!cachefile.isEmpty() /*&& files_changed*/) + saveCache(cachefile); + + //buffer + if(spare_buffer) { + free(spare_buffer); + spare_buffer = 0; + spare_buffer_size = 0; + } + + //quick project lookup + delete files; + delete includes; +} + +void QMakeSourceFileInfo::setCacheFile(const QString &cf) +{ + cachefile = cf; + loadCache(cachefile); +} + +void QMakeSourceFileInfo::addSourceFiles(const QStringList &l, uchar seek, + QMakeSourceFileInfo::SourceFileType type) +{ + for(int i=0; ilookupFile(fn); + if(!file) { + file = new SourceFile; + file->file = fn; + files->addFile(file); + } else { + if(file->type != type && file->type != TYPE_UNKNOWN && type != TYPE_UNKNOWN) + warn_msg(WarnLogic, "%s is marked as %d, then %d!", f.toLatin1().constData(), + file->type, type); + } + if(type != TYPE_UNKNOWN) + file->type = type; + + if(seek & SEEK_MOCS && !file->moc_checked) + findMocs(file); + if(seek & SEEK_DEPS && !file->dep_checked) + findDeps(file); +} + +bool QMakeSourceFileInfo::containsSourceFile(const QString &f, SourceFileType type) +{ + if(SourceFile *file = files->lookupFile(QMakeLocalFileName(f))) + return (file->type == type || file->type == TYPE_UNKNOWN || type == TYPE_UNKNOWN); + return false; +} + +char *QMakeSourceFileInfo::getBuffer(int s) { + if(!spare_buffer || spare_buffer_size < s) + spare_buffer = (char *)realloc(spare_buffer, spare_buffer_size=s); + return spare_buffer; +} + +#ifndef S_ISDIR +#define S_ISDIR(x) (x & _S_IFDIR) +#endif + +QMakeLocalFileName QMakeSourceFileInfo::fixPathForFile(const QMakeLocalFileName &f, bool) +{ + return f; +} + +QMakeLocalFileName QMakeSourceFileInfo::findFileForDep(const QMakeLocalFileName &/*dep*/, + const QMakeLocalFileName &/*file*/) +{ + return QMakeLocalFileName(); +} + +QFileInfo QMakeSourceFileInfo::findFileInfo(const QMakeLocalFileName &dep) +{ + return QFileInfo(dep.real()); +} + +bool QMakeSourceFileInfo::findDeps(SourceFile *file) +{ + if(file->dep_checked || file->type == TYPE_UNKNOWN) + return true; + files_changed = true; + file->dep_checked = true; + + const QMakeLocalFileName sourceFile = fixPathForFile(file->file, true); + + struct stat fst; + char *buffer = 0; + int buffer_len = 0; + { + int fd; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (_sopen_s(&fd, sourceFile.local().toLatin1().constData(), + _O_RDONLY, _SH_DENYNO, _S_IREAD) != 0) + fd = -1; +#else + fd = open(sourceFile.local().toLatin1().constData(), O_RDONLY); +#endif + if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode)) + return false; + buffer = getBuffer(fst.st_size); + for(int have_read = 0; + (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len)); + buffer_len += have_read) ; + QT_CLOSE(fd); + } + if(!buffer) + return false; + if(!file->deps) + file->deps = new SourceDependChildren; + + int line_count = 1; + + for(int x = 0; x < buffer_len; ++x) { + bool try_local = true; + char *inc = 0; + if(file->type == QMakeSourceFileInfo::TYPE_UI) { + // skip whitespaces + while(x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t')) + ++x; + if(*(buffer + x) == '<') { + ++x; + if(buffer_len >= x + 12 && !strncmp(buffer + x, "includehint", 11) && + (*(buffer + x + 11) == ' ' || *(buffer + x + 11) == '>')) { + for(x += 11; *(buffer + x) != '>'; ++x) ; + int inc_len = 0; + for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len) ; + *(buffer + x + inc_len) = '\0'; + inc = buffer + x; + } else if(buffer_len >= x + 13 && !strncmp(buffer + x, "customwidget", 12) && + (*(buffer + x + 12) == ' ' || *(buffer + x + 12) == '>')) { + for(x += 13; *(buffer + x) != '>'; ++x) ; //skip up to > + while(x < buffer_len) { + for(x++; *(buffer + x) != '<'; ++x) ; //skip up to < + x++; + if(buffer_len >= x + 7 && !strncmp(buffer+x, "header", 6) && + (*(buffer + x + 6) == ' ' || *(buffer + x + 6) == '>')) { + for(x += 7; *(buffer + x) != '>'; ++x) ; //skip up to > + int inc_len = 0; + for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len) ; + *(buffer + x + inc_len) = '\0'; + inc = buffer + x; + break; + } else if(buffer_len >= x + 14 && !strncmp(buffer+x, "/customwidget", 13) && + (*(buffer + x + 13) == ' ' || *(buffer + x + 13) == '>')) { + x += 14; + break; + } + } + } else if(buffer_len >= x + 8 && !strncmp(buffer + x, "include", 7) && + (*(buffer + x + 7) == ' ' || *(buffer + x + 7) == '>')) { + for(x += 8; *(buffer + x) != '>'; ++x) { + if(buffer_len >= x + 9 && *(buffer + x) == 'i' && + !strncmp(buffer + x, "impldecl", 8)) { + for(x += 8; *(buffer + x) != '='; ++x) ; + if(*(buffer + x) != '=') + continue; + for(++x; *(buffer+x) == '\t' || *(buffer+x) == ' '; ++x) ; + char quote = 0; + if(*(buffer+x) == '\'' || *(buffer+x) == '"') { + quote = *(buffer + x); + ++x; + } + int val_len; + for(val_len = 0; true; ++val_len) { + if(quote) { + if(*(buffer+x+val_len) == quote) + break; + } else if(*(buffer + x + val_len) == '>' || + *(buffer + x + val_len) == ' ') { + break; + } + } +//? char saved = *(buffer + x + val_len); + *(buffer + x + val_len) = '\0'; + if(!strcmp(buffer+x, "in implementation")) { + //### do this + } + } + } + int inc_len = 0; + for(x += 1 ; *(buffer + x + inc_len) != '<'; ++inc_len) ; + *(buffer + x + inc_len) = '\0'; + inc = buffer + x; + } + } + //read past new line now.. + for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x) ; + ++line_count; + } else if(file->type == QMakeSourceFileInfo::TYPE_QRC) { + } else if(file->type == QMakeSourceFileInfo::TYPE_C) { + for(int beginning=1; x < buffer_len; ++x) { + // whitespace comments and line-endings + for(; x < buffer_len; ++x) { + if(*(buffer+x) == ' ' || *(buffer+x) == '\t') { + // keep going + } else if(*(buffer+x) == '/') { + ++x; + if(buffer_len >= x) { + if(*(buffer+x) == '/') { //c++ style comment + for(; x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x) ; + beginning = 1; + } else if(*(buffer+x) == '*') { //c style comment + for(++x; x < buffer_len; ++x) { + if(*(buffer+x) == '*') { + if(x < buffer_len-1 && *(buffer + (x+1)) == '/') { + ++x; + break; + } + } else if(qmake_endOfLine(*(buffer+x))) { + ++line_count; + } + } + } + } + } else if(qmake_endOfLine(*(buffer+x))) { + ++line_count; + beginning = 1; + } else { + break; + } + } + + if(x >= buffer_len) + break; + + // preprocessor directive + if(beginning && *(buffer+x) == '#') + break; + + // quoted strings + if(*(buffer+x) == '\'' || *(buffer+x) == '"') { + const char term = *(buffer+(x++)); + for(; x < buffer_len; ++x) { + if(*(buffer+x) == term) { + ++x; + break; + } else if(*(buffer+x) == '\\') { + ++x; + } else if(qmake_endOfLine(*(buffer+x))) { + ++line_count; + } + } + } + beginning = 0; + } + if(x >= buffer_len) + break; + + //got a preprocessor symbol + ++x; + while(x < buffer_len) { + if(*(buffer+x) != ' ' && *(buffer+x) != '\t') + break; + ++x; + } + + int keyword_len = 0; + const char *keyword = buffer+x; + while(x+keyword_len < buffer_len) { + if(((*(buffer+x+keyword_len) < 'a' || *(buffer+x+keyword_len) > 'z')) && + *(buffer+x+keyword_len) != '_') { + for(x+=keyword_len; //skip spaces after keyword + x < buffer_len && (*(buffer+x) == ' ' || *(buffer+x) == '\t'); + x++) ; + break; + } else if(qmake_endOfLine(*(buffer+x+keyword_len))) { + x += keyword_len-1; + keyword_len = 0; + break; + } + keyword_len++; + } + + if(keyword_len == 7 && !strncmp(keyword, "include", keyword_len)) { + char term = *(buffer + x); + if(term == '<') { + try_local = false; + term = '>'; + } else if(term != '"') { //wtf? + continue; + } + x++; + + int inc_len; + for(inc_len = 0; *(buffer + x + inc_len) != term && !qmake_endOfLine(*(buffer + x + inc_len)); ++inc_len) ; + *(buffer + x + inc_len) = '\0'; + inc = buffer + x; + x += inc_len; + } else if(keyword_len == 13 && !strncmp(keyword, "qmake_warning", keyword_len)) { + char term = 0; + if(*(buffer + x) == '"') + term = '"'; + if(*(buffer + x) == '\'') + term = '\''; + if(term) + x++; + + int msg_len; + for(msg_len = 0; (term && *(buffer + x + msg_len) != term) && + !qmake_endOfLine(*(buffer + x + msg_len)); ++msg_len) ; + *(buffer + x + msg_len) = '\0'; + debug_msg(0, "%s:%d %s -- %s", file->file.local().toLatin1().constData(), line_count, keyword, buffer+x); + x += msg_len; + } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') { + const char term = *(buffer+(x++)); + while(x < buffer_len) { + if(*(buffer+x) == term) + break; + if(*(buffer+x) == '\\') { + x+=2; + } else { + if(qmake_endOfLine(*(buffer+x))) + ++line_count; + ++x; + } + } + } else { + --x; + } + } + + if(inc) { + if(!includes) + includes = new SourceFiles; + SourceFile *dep = includes->lookupFile(inc); + if(!dep) { + bool exists = false; + QMakeLocalFileName lfn(inc); + if(QDir::isRelativePath(lfn.real())) { + if(try_local) { + QDir sourceDir = findFileInfo(sourceFile).dir(); + QMakeLocalFileName f(sourceDir.absoluteFilePath(lfn.local())); + if(findFileInfo(f).exists()) { + lfn = fixPathForFile(f); + exists = true; + } + } + if(!exists) { //path lookup + for(QList::Iterator it = depdirs.begin(); it != depdirs.end(); ++it) { + QMakeLocalFileName f((*it).real() + Option::dir_sep + lfn.real()); + QFileInfo fi(findFileInfo(f)); + if(fi.exists() && !fi.isDir()) { + lfn = fixPathForFile(f); + exists = true; + break; + } + } + } + if(!exists) { //heuristic lookup + lfn = findFileForDep(QMakeLocalFileName(inc), file->file); + if((exists = !lfn.isNull())) + lfn = fixPathForFile(lfn); + } + } else { + exists = QFile::exists(lfn.real()); + } + if(!lfn.isNull()) { + dep = files->lookupFile(lfn); + if(!dep) { + dep = new SourceFile; + dep->file = lfn; + dep->type = QMakeSourceFileInfo::TYPE_C; + files->addFile(dep); + includes->addFile(dep, inc, false); + } + dep->exists = exists; + } + } + if(dep && dep->file != file->file) { + dep->included_count++; + if(dep->exists) { + debug_msg(5, "%s:%d Found dependency to %s", file->file.real().toLatin1().constData(), + line_count, dep->file.local().toLatin1().constData()); + file->deps->addChild(dep); + } + } + } + } + if(dependencyMode() == Recursive) { //done last because buffer is shared + for(int i = 0; i < file->deps->used_nodes; i++) { + if(!file->deps->children[i]->deps) + findDeps(file->deps->children[i]); + } + } + return true; +} + +bool QMakeSourceFileInfo::findMocs(SourceFile *file) +{ + if(file->moc_checked) + return true; + files_changed = true; + file->moc_checked = true; + + int buffer_len; + char *buffer = 0; + { + struct stat fst; + int fd; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (_sopen_s(&fd, fixPathForFile(file->file, true).local().toLocal8Bit().constData(), + _O_RDONLY, _SH_DENYRW, _S_IREAD) != 0) + fd = -1; +#else + fd = open(fixPathForFile(file->file, true).local().toLocal8Bit().constData(), O_RDONLY); +#endif + if(fd == -1 || fstat(fd, &fst) || S_ISDIR(fst.st_mode)) + return false; //shouldn't happen + buffer = getBuffer(fst.st_size); + for(int have_read = buffer_len = 0; + (have_read = QT_READ(fd, buffer + buffer_len, fst.st_size - buffer_len)); + buffer_len += have_read) ; + QT_CLOSE(fd); + } + + debug_msg(2, "findMocs: %s", file->file.local().toLatin1().constData()); + int line_count = 1; + bool ignore_qobject = false, ignore_qgadget = false; + /* qmake ignore Q_GADGET */ + /* qmake ignore Q_OBJECT */ + for(int x = 0; x < buffer_len; x++) { + if(*(buffer + x) == '/') { + ++x; + if(buffer_len >= x) { + if(*(buffer + x) == '/') { //c++ style comment + for(;x < buffer_len && !qmake_endOfLine(*(buffer + x)); ++x) ; + } else if(*(buffer + x) == '*') { //c style comment + for(++x; x < buffer_len; ++x) { + if(*(buffer + x) == 't' || *(buffer + x) == 'q') { //ignore + if(buffer_len >= (x + 20) && + !strncmp(buffer + x + 1, "make ignore Q_OBJECT", 20)) { + debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_OBJECT\"", + file->file.real().toLatin1().constData(), line_count); + x += 20; + ignore_qobject = true; + } else if(buffer_len >= (x + 20) && + !strncmp(buffer + x + 1, "make ignore Q_GADGET", 20)) { + debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_GADGET\"", + file->file.real().toLatin1().constData(), line_count); + x += 20; + ignore_qgadget = true; + } + } else if(*(buffer + x) == '*') { + if(buffer_len >= (x+1) && *(buffer + (x+1)) == '/') { + ++x; + break; + } + } else if(Option::debug_level && qmake_endOfLine(*(buffer + x))) { + ++line_count; + } + } + } + } + } else if(*(buffer+x) == '\'' || *(buffer+x) == '"') { + const char term = *(buffer+(x++)); + while(x < buffer_len) { + if(*(buffer+x) == term) + break; + if(*(buffer+x) == '\\') { + x+=2; + } else { + if(qmake_endOfLine(*(buffer+x))) + ++line_count; + ++x; + } + } + } + if(Option::debug_level && qmake_endOfLine(*(buffer+x))) + ++line_count; + if(((buffer_len > x+2 && *(buffer+x+1) == 'Q' && *(buffer+x+2) == '_') + || + (buffer_len > x+4 && *(buffer+x+1) == 'Q' && *(buffer+x+2) == 'O' + && *(buffer+x+3) == 'M' && *(buffer+x+4) == '_')) + && + *(buffer + x) != '_' && + (*(buffer + x) < 'a' || *(buffer + x) > 'z') && + (*(buffer + x) < 'A' || *(buffer + x) > 'Z') && + (*(buffer + x) < '0' || *(buffer + x) > '9')) { + ++x; + int match = 0; + static const char *interesting[] = { "OBJECT", "GADGET", + "M_OBJECT" }; + for(int interest = 0, m1, m2; interest < 3; ++interest) { + if(interest == 0 && ignore_qobject) + continue; + else if(interest == 1 && ignore_qgadget) + continue; + for(m1 = 0, m2 = 0; *(interesting[interest]+m1); ++m1) { + if(*(interesting[interest]+m1) != *(buffer+x+2+m1)) { + m2 = -1; + break; + } + ++m2; + } + if(m1 == m2) { + match = m2 + 2; + break; + } + } + if(match && *(buffer+x+match) != '_' && + (*(buffer+x+match) < 'a' || *(buffer+x+match) > 'z') && + (*(buffer+x+match) < 'A' || *(buffer+x+match) > 'Z') && + (*(buffer+x+match) < '0' || *(buffer+x+match) > '9')) { + if(Option::debug_level) { + *(buffer+x+match) = '\0'; + debug_msg(2, "Mocgen: %s:%d Found MOC symbol %s", file->file.real().toLatin1().constData(), + line_count, buffer+x); + } + file->mocable = true; + return true; + } + } + } + return true; +} + + +void QMakeSourceFileInfo::saveCache(const QString &cf) +{ +#ifdef QMAKE_USE_CACHE + if(cf.isEmpty()) + return; + + QFile file(QMakeLocalFileName(cf).local()); + if(file.open(QIODevice::WriteOnly)) { + QTextStream stream(&file); + stream << qmake_version() << endl << endl; //version + { //cache verification + QMap verify = getCacheVerification(); + stream << verify.count() << endl; + for(QMap::iterator it = verify.begin(); + it != verify.end(); ++it) { + stream << it.key() << endl << it.value().join(";") << endl; + } + stream << endl; + } + if(files->nodes) { + for(int file = 0; file < files->num_nodes; ++file) { + for(SourceFiles::SourceFileNode *node = files->nodes[file]; node; node = node->next) { + stream << node->file->file.local() << endl; //source + stream << node->file->type << endl; //type + + //depends + stream << ";"; + if(node->file->deps) { + for(int depend = 0; depend < node->file->deps->used_nodes; ++depend) { + if(depend) + stream << ";"; + stream << node->file->deps->children[depend]->file.local(); + } + } + stream << endl; + + stream << node->file->mocable << endl; //mocable + stream << endl; //just for human readability + } + } + } + stream.flush(); + file.close(); + } +#else + Q_UNUSED(cf); +#endif +} + +void QMakeSourceFileInfo::loadCache(const QString &cf) +{ + if(cf.isEmpty()) + return; + +#ifdef QMAKE_USE_CACHE + QMakeLocalFileName cache_file(cf); + int fd = open(QMakeLocalFileName(cf).local().toLatin1(), O_RDONLY); + if(fd == -1) + return; + QFileInfo cache_fi = findFileInfo(cache_file); + if(!cache_fi.exists() || cache_fi.isDir()) + return; + + QFile file; + if(!file.open(QIODevice::ReadOnly, fd)) + return; + QTextStream stream(&file); + + if(stream.readLine() == qmake_version()) { //version check + stream.skipWhiteSpace(); + + bool verified = true; + { //cache verification + QMap verify; + int len = stream.readLine().toInt(); + for(int i = 0; i < len; ++i) { + QString var = stream.readLine(); + QString val = stream.readLine(); + verify.insert(var, val.split(';', QString::SkipEmptyParts)); + } + verified = verifyCache(verify); + } + if(verified) { + stream.skipWhiteSpace(); + if(!files) + files = new SourceFiles; + while(!stream.atEnd()) { + QString source = stream.readLine(); + QString type = stream.readLine(); + QString depends = stream.readLine(); + QString mocable = stream.readLine(); + stream.skipWhiteSpace(); + + QMakeLocalFileName fn(source); + QFileInfo fi = findFileInfo(fn); + + SourceFile *file = files->lookupFile(fn); + if(!file) { + file = new SourceFile; + file->file = fn; + files->addFile(file); + file->type = (SourceFileType)type.toInt(); + file->exists = fi.exists(); + } + if(fi.exists() && fi.lastModified() < cache_fi.lastModified()) { + if(!file->dep_checked) { //get depends + if(!file->deps) + file->deps = new SourceDependChildren; + file->dep_checked = true; + QStringList depend_list = depends.split(";", QString::SkipEmptyParts); + for(int depend = 0; depend < depend_list.size(); ++depend) { + QMakeLocalFileName dep_fn(depend_list.at(depend)); + QFileInfo dep_fi(findFileInfo(dep_fn)); + SourceFile *dep = files->lookupFile(dep_fn); + if(!dep) { + dep = new SourceFile; + dep->file = dep_fn; + dep->exists = dep_fi.exists(); + dep->type = QMakeSourceFileInfo::TYPE_UNKNOWN; + files->addFile(dep); + } + dep->included_count++; + file->deps->addChild(dep); + } + } + if(!file->moc_checked) { //get mocs + file->moc_checked = true; + file->mocable = mocable.toInt(); + } + } + } + } + } +#endif +} + +QMap QMakeSourceFileInfo::getCacheVerification() +{ + return QMap(); +} + +bool QMakeSourceFileInfo::verifyCache(const QMap &v) +{ + return v == getCacheVerification(); +} + +QT_END_NAMESPACE diff --git a/qmake/generators/makefiledeps.h b/qmake/generators/makefiledeps.h new file mode 100644 index 0000000000..c3f8770797 --- /dev/null +++ b/qmake/generators/makefiledeps.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAKEFILEDEPS_H +#define MAKEFILEDEPS_H + +#include +#include + +QT_BEGIN_NAMESPACE + +struct SourceFile; +struct SourceDependChildren; +class SourceFiles; + +class QMakeLocalFileName { + uint is_null : 1; + mutable QString real_name, local_name; +public: + QMakeLocalFileName() : is_null(1) { } + QMakeLocalFileName(const QString &); + bool isNull() const { return is_null; } + inline const QString &real() const { return real_name; } + const QString &local() const; + + bool operator==(const QMakeLocalFileName &other) { + return (this->real_name == other.real_name); + } + bool operator!=(const QMakeLocalFileName &other) { + return !(*this == other); + } +}; + +class QMakeSourceFileInfo +{ +private: + //quick project lookups + SourceFiles *files, *includes; + bool files_changed; + QList depdirs; + + //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 *); + + //cache + QString cachefile; + +protected: + virtual QMakeLocalFileName fixPathForFile(const QMakeLocalFileName &, bool forOpen=false); + virtual QMakeLocalFileName findFileForDep(const QMakeLocalFileName &, const QMakeLocalFileName &); + virtual QFileInfo findFileInfo(const QMakeLocalFileName &); + +public: + QMakeSourceFileInfo(const QString &cachefile=""); + virtual ~QMakeSourceFileInfo(); + + QList dependencyPaths() const { return depdirs; } + void setDependencyPaths(const QList &); + + enum DependencyMode { Recursive, NonRecursive }; + inline void setDependencyMode(DependencyMode mode) { dep_mode = mode; } + inline DependencyMode dependencyMode() const { return dep_mode; } + + enum SourceFileType { TYPE_UNKNOWN, TYPE_C, TYPE_UI, TYPE_QRC }; + enum SourceFileSeek { SEEK_DEPS=0x01, SEEK_MOCS=0x02 }; + void addSourceFiles(const QStringList &, uchar seek, SourceFileType type=TYPE_C); + void addSourceFile(const QString &, uchar seek, SourceFileType type=TYPE_C); + bool containsSourceFile(const QString &, SourceFileType type=TYPE_C); + + int included(const QString &file); + QStringList dependencies(const QString &file); + + bool mocable(const QString &file); + + virtual QMap getCacheVerification(); + virtual bool verifyCache(const QMap &); + void setCacheFile(const QString &cachefile); //auto caching + void loadCache(const QString &cf); + void saveCache(const QString &cf); + +private: + DependencyMode dep_mode; +}; + +QT_END_NAMESPACE + +#endif // MAKEFILEDEPS_H diff --git a/qmake/generators/metamakefile.cpp b/qmake/generators/metamakefile.cpp new file mode 100644 index 0000000000..a3fba6ab55 --- /dev/null +++ b/qmake/generators/metamakefile.cpp @@ -0,0 +1,572 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "metamakefile.h" +#include "qregexp.h" +#include "qdir.h" +#include "qdebug.h" +#include "makefile.h" +#include "project.h" +#include "cachekeys.h" + +#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 makefiles; + void clearBuilds(); + MakefileGenerator *processBuild(const QString &); + +public: + + BuildsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { } + virtual ~BuildsMetaMakefileGenerator() { clearBuilds(); } + + virtual bool init(); + virtual int type() const { return BUILDSMETATYPE; } + virtual bool write(const QString &); +}; + +void +BuildsMetaMakefileGenerator::clearBuilds() +{ + for(int i = 0; i < makefiles.count(); 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 QStringList &builds = project->variables()["BUILDS"]; + bool use_single_build = builds.isEmpty(); + if(builds.count() > 1 && Option::output.fileName() == "-") { + use_single_build = true; + warn_msg(WarnLogic, "Cannot direct to stdout when using multiple BUILDS."); + } else if(0 && !use_single_build && project->first("TEMPLATE") == "subdirs") { + use_single_build = true; + warn_msg(WarnLogic, "Cannot specify multiple builds with TEMPLATE subdirs."); + } + if(!use_single_build) { + for(int i = 0; i < builds.count(); i++) { + QString 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.count() != 1) + b->build += build; + 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(const QString &oldpwd) +{ + Build *glue = 0; + if(!makefiles.isEmpty() && !makefiles.first()->build.isNull()) { + 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.count(); 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")); + Option::output_dir = oldpwd; + 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) { + 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(); + } + + // debugging + if(Option::debug_level) { + debug_msg(1, "Dumping all variables:"); + QMap &vars = project->variables(); + for(QMap::Iterator it = vars.begin(); it != vars.end(); ++it) { + if(!it.key().startsWith(".") && !it.value().isEmpty()) + debug_msg(1, "%s === %s", it.key().toLatin1().constData(), + it.value().join(" :: ").toLatin1().constData()); + } + } + } + return ret; +} + +MakefileGenerator +*BuildsMetaMakefileGenerator::processBuild(const QString &build) +{ + if(project) { + debug_msg(1, "Meta Generator: Parsing '%s' for build [%s].", + project->projectFile().toLatin1().constData(),build.toLatin1().constData()); + + //initialize the base + QMap basevars; + if(!project->isEmpty(build + ".CONFIG")) + basevars["CONFIG"] += project->values(build + ".CONFIG"); + basevars["CONFIG"] += build; + basevars["CONFIG"] += "build_pass"; + basevars["BUILD_PASS"] = QStringList(build); + QStringList buildname = project->values(build + ".name"); + basevars["BUILD_NAME"] = (buildname.isEmpty() ? QStringList(build) : buildname); + + //create project + QMakeProject *build_proj = new QMakeProject(project->properties(), basevars); + + //all the user configs must be set again afterwards (for .pro tests and for .prf tests) + const QStringList old_after_user_config = Option::after_user_configs; + const QStringList old_user_config = Option::user_configs; + Option::after_user_configs += basevars["CONFIG"]; + Option::user_configs += basevars["CONFIG"]; + build_proj->read(project->projectFile()); + Option::after_user_configs = old_after_user_config; + Option::user_configs = old_user_config; + + //done + return createMakefileGenerator(build_proj); + } + return 0; +} + +class SubdirsMetaMakefileGenerator : public MetaMakefileGenerator +{ +protected: + bool init_flag; + struct Subdir { + Subdir() : makefile(0), indent(0) { } + ~Subdir() { delete makefile; } + QString input_dir; + QString output_dir, output_file; + MetaMakefileGenerator *makefile; + int indent; + }; + QList subs; + MakefileGenerator *processBuild(const QString &); + +public: + SubdirsMetaMakefileGenerator(QMakeProject *p, const QString &n, bool op) : MetaMakefileGenerator(p, n, op), init_flag(false) { } + virtual ~SubdirsMetaMakefileGenerator(); + + virtual bool init(); + virtual int type() const { return SUBDIRSMETATYPE; } + virtual bool write(const QString &); +}; + +bool +SubdirsMetaMakefileGenerator::init() +{ + if(init_flag) + return false; + init_flag = true; + bool hasError = false; + + // It might make sense to bequeath the CONFIG option to the recursed + // projects. OTOH, one would most likely have it in all projects anyway - + // either through a qmakespec, a .qmake.cache or explicitly - as otherwise + // running qmake in a subdirectory would have a different auto-recurse + // setting than in parent directories. + bool recurse = Option::recursive == Option::QMAKE_RECURSIVE_YES + || (Option::recursive == Option::QMAKE_RECURSIVE_DEFAULT + && project->isRecursive()); + 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 QStringList &subdirs = project->values("SUBDIRS"); + 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)); + if(!project->isEmpty(subdirs.at(i) + ".file")) + subdir = project->first(subdirs.at(i) + ".file"); + else if(!project->isEmpty(subdirs.at(i) + ".subdir")) + subdir = project->first(subdirs.at(i) + ".subdir"); + 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.length())); + } + + //handle sub project + QMakeProject *sub_proj = new QMakeProject(project->properties()); + 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(); + 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->variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) { + 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); + if(0 && sub->makefile->type() == SUBDIRSMETATYPE) { + subs.append(sub); + } else { + const QString output_name = Option::output.fileName(); + Option::output.setFileName(sub->output_file); + hasError |= !sub->makefile->write(sub->output_dir); + delete sub; + qmakeClearCaches(); + sub = 0; + 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(const QString &oldpwd) +{ + 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.count(); i++) { + const Subdir *sub = subs.at(i); + qmake_setpwd(subs.at(i)->input_dir); + Option::output_dir = QFileInfo(subs.at(i)->output_dir).absoluteFilePath(); + if(Option::output_dir.at(Option::output_dir.length()-1) != QLatin1Char('/')) + Option::output_dir += QLatin1Char('/'); + Option::output.setFileName(subs.at(i)->output_file); + if(i != subs.count()-1) { + for (int ind = 0; ind < sub->indent; ++ind) + printf(" "); + printf("Writing %s\n", QDir::cleanPath(Option::output_dir+"/"+ + Option::output.fileName()).toLatin1().constData()); + } + QString writepwd = Option::fixPathToLocalOS(qmake_getpwd()); + if(!writepwd.startsWith(Option::fixPathToLocalOS(oldpwd))) + writepwd = oldpwd; + if(!(ret = subs.at(i)->makefile->write(writepwd))) + 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.count(); 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 "borland_bmake.h" +#include "msvc_vcproj.h" +#include "msvc_vcxproj.h" +#include "symmake_abld.h" +#include "symmake_sbsv2.h" +#include "symbian_makefile.h" +#include "gbuild.h" +QT_END_INCLUDE_NAMESPACE + +MakefileGenerator * +MetaMakefileGenerator::createMakefileGenerator(QMakeProject *proj, bool noIO) +{ + MakefileGenerator *mkfile = NULL; + if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) { + mkfile = new ProjectGenerator; + mkfile->setProjectFile(proj); + return mkfile; + } + + QString 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") { + mkfile = new ProjectBuilderMakefileGenerator; + } 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 if(gen == "BMAKE") { + mkfile = new BorlandMakefileGenerator; + } else if(gen == "SYMBIAN_ABLD") { + mkfile = new SymbianAbldMakefileGenerator; + } else if(gen == "SYMBIAN_SBSV2") { + mkfile = new SymbianSbsv2MakefileGenerator; + } else if(gen == "SYMBIAN_UNIX") { + mkfile = new SymbianMakefileTemplate; + } else if(gen == "SYMBIAN_MINGW") { + mkfile = new SymbianMakefileTemplate; + } else if(gen == "GBUILD") { + mkfile = new GBuildMakefileGenerator; + } 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) +{ + MetaMakefileGenerator *ret = 0; + 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 + +bool +MetaMakefileGenerator::modesForGenerator(const QString &gen, + Option::HOST_MODE *host_mode, Option::TARG_MODE *target_mode) +{ + if (gen == "UNIX") { +#ifdef Q_OS_MAC + *host_mode = Option::HOST_MACX_MODE; + *target_mode = Option::TARG_MACX_MODE; +#else + *host_mode = Option::HOST_UNIX_MODE; + *target_mode = Option::TARG_UNIX_MODE; +#endif + } else if (gen == "MSVC.NET" || gen == "BMAKE" || gen == "MSBUILD") { + *host_mode = Option::HOST_WIN_MODE; + *target_mode = Option::TARG_WIN_MODE; + } else if (gen == "MINGW") { +#if defined(Q_OS_MAC) + *host_mode = Option::HOST_MACX_MODE; +#elif defined(Q_OS_UNIX) + *host_mode = Option::HOST_UNIX_MODE; +#else + *host_mode = Option::HOST_WIN_MODE; +#endif + *target_mode = Option::TARG_WIN_MODE; + } else if (gen == "PROJECTBUILDER" || gen == "XCODE") { + *host_mode = Option::HOST_MACX_MODE; + *target_mode = Option::TARG_MACX_MODE; + } else if (gen == "SYMBIAN_ABLD" || gen == "SYMBIAN_SBSV2" || gen == "SYMBIAN_UNIX" || gen == "SYMBIAN_MINGW") { +#if defined(Q_OS_MAC) + *host_mode = Option::HOST_MACX_MODE; +#elif defined(Q_OS_UNIX) + *host_mode = Option::HOST_UNIX_MODE; +#else + *host_mode = Option::HOST_WIN_MODE; +#endif + *target_mode = Option::TARG_SYMBIAN_MODE; + } else if (gen == "GBUILD") { + *host_mode = Option::HOST_UNIX_MODE; + *target_mode = Option::TARG_INTEGRITY_MODE; + } else { + fprintf(stderr, "Unknown generator specified: %s\n", gen.toLatin1().constData()); + return false; + } + return true; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/metamakefile.h b/qmake/generators/metamakefile.h new file mode 100644 index 0000000000..c8592d3afc --- /dev/null +++ b/qmake/generators/metamakefile.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef METAMAKEFILE_H +#define METAMAKEFILE_H + +#include + +#include +#include + +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 = 0); + static MakefileGenerator *createMakefileGenerator(QMakeProject *proj, bool noIO = false); + + static bool modesForGenerator(const QString &generator, + Option::HOST_MODE *host_mode, Option::TARG_MODE *target_mode); + + inline QMakeProject *projectFile() const { return project; } + + virtual bool init() = 0; + virtual int type() const { return -1; } + virtual bool write(const QString &oldpwd) = 0; +}; + +QT_END_NAMESPACE + +#endif // METAMAKEFILE_H diff --git a/qmake/generators/projectgenerator.cpp b/qmake/generators/projectgenerator.cpp new file mode 100644 index 0000000000..da634a1179 --- /dev/null +++ b/qmake/generators/projectgenerator.cpp @@ -0,0 +1,511 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "projectgenerator.h" +#include "option.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +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; +} + +ProjectGenerator::ProjectGenerator() : MakefileGenerator(), init_flag(false) +{ +} + +void +ProjectGenerator::init() +{ + if(init_flag) + return; + int file_count = 0; + init_flag = true; + verifyCompilers(); + + project->read(QMakeProject::ReadFeatures); + project->variables()["CONFIG"].clear(); + + QMap &v = project->variables(); + QString templ = Option::user_template.isEmpty() ? QString("app") : Option::user_template; + if(!Option::user_template_prefix.isEmpty()) + templ.prepend(Option::user_template_prefix); + v["TEMPLATE_ASSIGN"] += templ; + + //figure out target + if(Option::output.fileName() == "-") + v["TARGET_ASSIGN"] = QStringList("unknown"); + else + v["TARGET_ASSIGN"] = QStringList(QFileInfo(Option::output).baseName()); + + //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.count(); ++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 == Option::QMAKE_RECURSIVE_YES) { + QStringList files = QDir(dir).entryList(QDir::Files); + for(int i = 0; i < (int)files.count(); i++) { + if(files[i] != "." && files[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.length() - (s+1)); + } + if(Option::recursive == Option::QMAKE_RECURSIVE_YES) { + QStringList entries = QDir(dir).entryList(QDir::Dirs); + for(int i = 0; i < (int)entries.count(); i++) { + if(entries[i] != "." && entries[i] != "..") { + dirs.append(dir + entries[i] + QDir::separator() + regex); + } + } + } + QStringList files = QDir(dir).entryList(QDir::nameFiltersFromString(regex)); + for(int i = 0; i < (int)files.count(); i++) { + QString file = dir + 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.count(); ++i) { + QString pd = knownDirs.at(i); + if(exists(pd)) { + QString newdir = pd; + QFileInfo fi(fileInfo(newdir)); + if(fi.isDir()) { + newdir = fileFixify(newdir); + QStringList &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.count(); i++) { + QString nd = newdir; + if(nd == ".") + nd = ""; + else if(!nd.isEmpty() && !nd.endsWith(QString(QChar(QDir::separator())))) + nd += QDir::separator(); + nd += profiles[i]; + fileFixify(nd); + if(profiles[i] != "." && profiles[i] != ".." && + !subdirs.contains(nd, Qt::CaseInsensitive) && !out_file.endsWith(nd)) + subdirs.append(nd); + } + } + if(Option::recursive == Option::QMAKE_RECURSIVE_YES) { + QStringList dirs = QDir(newdir).entryList(QDir::Dirs); + for(int i = 0; i < (int)dirs.count(); i++) { + QString nd = fileFixify(newdir + QDir::separator() + dirs[i]); + if(dirs[i] != "." && dirs[i] != ".." && !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.length() - (s+1)); + } + QStringList files = QDir(dir).entryList(QDir::nameFiltersFromString(regx), QDir::Dirs); + QStringList &subdirs = v["SUBDIRS"]; + for(int i = 0; i < (int)files.count(); i++) { + QString newdir(dir + files[i]); + QFileInfo fi(fileInfo(newdir)); + if(fi.fileName() != "." && fi.fileName() != "..") { + 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.count(); 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 == Option::QMAKE_RECURSIVE_YES + && !knownDirs.contains(newdir, Qt::CaseInsensitive)) + knownDirs.append(newdir); + } + } + } + } + v["TEMPLATE_ASSIGN"] = QStringList("subdirs"); + return; + } + + //setup deplist + QList deplist; + { + const QStringList &d = v["DEPENDPATH"]; + for(int i = 0; i < d.size(); ++i) + deplist.append(QMakeLocalFileName(d[i])); + } + setDependencyPaths(deplist); + + QStringList &h = v["HEADERS"]; + bool no_qt_files = true; + QString srcs[] = { "SOURCES", "YACCSOURCES", "LEXSOURCES", "FORMS", QString() }; + for(int i = 0; !srcs[i].isNull(); i++) { + const QStringList &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[i]); + 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.indexOf(QRegExp("^q[a-z_0-9].h$")) != -1) + 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.length() - h_ext.length()) + + Option::cpp_ext.at(cppit)); + if(exists(src)) { + QStringList &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 QStringList &quc = project->variables()["QMAKE_EXTRA_COMPILERS"]; + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + QString tmp_out = project->variables()[(*it) + ".output"].first(); + if(tmp_out.isEmpty()) + continue; + + QStringList var_out = project->variables()[(*it) + ".variable_out"]; + bool defaults = var_out.isEmpty(); + for(int i = 0; i < var_out.size(); ++i) { + QString v = var_out.at(i); + if(v.startsWith("GENERATED_")) { + defaults = true; + break; + } + } + if(defaults) { + var_out << "SOURCES"; + var_out << "HEADERS"; + var_out << "FORMS"; + } + const QStringList &tmp = project->variables()[(*it) + ".input"]; + for(QStringList::ConstIterator it2 = tmp.begin(); it2 != tmp.end(); ++it2) { + QStringList &inputs = project->variables()[(*it2)]; + for(QStringList::Iterator input = inputs.begin(); input != inputs.end(); ++input) { + QString path = replaceExtraCompilerVariables(tmp_out, (*input), QString()); + path = fixPathToQmake(path).section('/', -1); + for(int i = 0; i < var_out.size(); ++i) { + QString v = var_out.at(i); + QStringList &list = project->variables()[v]; + 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 << "######################################################################" << endl; + t << "# Automatically generated by qmake (" << qmake_version() << ") " << QDateTime::currentDateTime().toString() << endl; + t << "######################################################################" << endl << endl; + if(!Option::user_configs.isEmpty()) + t << "CONFIG += " << Option::user_configs.join(" ") << endl; + int i; + for(i = 0; i < Option::before_user_vars.size(); ++i) + t << Option::before_user_vars[i] << endl; + t << getWritableVar("TEMPLATE_ASSIGN", false); + if(project->first("TEMPLATE_ASSIGN") == "subdirs") { + t << endl << "# Directories" << "\n" + << getWritableVar("SUBDIRS"); + } else { + t << getWritableVar("TARGET_ASSIGN") + << getWritableVar("CONFIG", false) + << getWritableVar("CONFIG_REMOVE", false) + << getWritableVar("DEPENDPATH") + << getWritableVar("INCLUDEPATH") << endl; + + t << "# Input" << "\n"; + t << getWritableVar("HEADERS") + << getWritableVar("FORMS") + << getWritableVar("LEXSOURCES") + << getWritableVar("YACCSOURCES") + << getWritableVar("SOURCES") + << getWritableVar("RESOURCES") + << getWritableVar("TRANSLATIONS"); + } + for(i = 0; i < Option::after_user_vars.size(); ++i) + t << Option::after_user_vars[i] << endl; + return true; +} + +bool +ProjectGenerator::addConfig(const QString &cfg, bool add) +{ + QString where = "CONFIG"; + if(!add) + where = "CONFIG_REMOVE"; + if(!project->variables()[where].contains(cfg)) { + project->variables()[where] += cfg; + return true; + } + return false; +} + +bool +ProjectGenerator::addFile(QString file) +{ + file = fileFixify(file, qmake_getpwd()); + QString dir; + int s = file.lastIndexOf(Option::dir_sep); + if(s != -1) + dir = file.left(s+1); + if(file.mid(dir.length(), Option::h_moc_mod.length()) == Option::h_moc_mod) + return false; + + QString 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)); + + QStringList &endList = project->variables()[where]; + if(!endList.contains(newfile, Qt::CaseInsensitive)) { + endList += newfile; + return true; + } + return false; +} + +QString +ProjectGenerator::getWritableVar(const QString &v, bool) +{ + QStringList &vals = project->variables()[v]; + if(vals.isEmpty()) + return ""; + + // If values contain spaces, ensure that they are quoted + for(QStringList::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.length() + join.length() > 80) { + QString spaces; + for(int i = 0; i < ret.length(); i++) + spaces += " "; + join = vals.join(" \\\n" + spaces); + } + return ret + join + "\n"; +} + +bool +ProjectGenerator::openOutput(QFile &file, const QString &build) const +{ + QString outdir; + if(!file.fileName().isEmpty()) { + QFileInfo fi(fileInfo(file.fileName())); + if(fi.isDir()) + outdir = fi.path() + QDir::separator(); + } + if(!outdir.isEmpty() || file.fileName().isEmpty()) { + QString dir = qmake_getpwd(); + int s = dir.lastIndexOf('/'); + if(s != -1) + dir = dir.right(dir.length() - (s + 1)); + file.setFileName(outdir + dir + Option::pro_ext); + } + return MakefileGenerator::openOutput(file, build); +} + + +QString +ProjectGenerator::fixPathToQmake(const QString &file) +{ + QString ret = file; + if(Option::dir_sep != QLatin1String("/")) + ret = ret.replace(Option::dir_sep, QLatin1String("/")); + return ret; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/projectgenerator.h b/qmake/generators/projectgenerator.h new file mode 100644 index 0000000000..7b4d56c3cd --- /dev/null +++ b/qmake/generators/projectgenerator.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROJECTGENERATOR_H +#define PROJECTGENERATOR_H + +#include "makefile.h" + +QT_BEGIN_NAMESPACE + +class ProjectGenerator : public MakefileGenerator +{ + bool init_flag; + bool addFile(QString); + bool addConfig(const QString &, bool add=true); + QString getWritableVar(const QString &, bool fixPath=true); + QString fixPathToQmake(const QString &file); +protected: + virtual void init(); + virtual bool writeMakefile(QTextStream &); +public: + ProjectGenerator(); + ~ProjectGenerator(); + virtual bool supportsMetaBuild() { return false; } + virtual bool openOutput(QFile &, const QString &) const; +}; + +inline ProjectGenerator::~ProjectGenerator() +{ } + +QT_END_NAMESPACE + +#endif // PROJECTGENERATOR_H diff --git a/qmake/generators/symbian/initprojectdeploy_symbian.cpp b/qmake/generators/symbian/initprojectdeploy_symbian.cpp new file mode 100644 index 0000000000..8d04a424c6 --- /dev/null +++ b/qmake/generators/symbian/initprojectdeploy_symbian.cpp @@ -0,0 +1,379 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "initprojectdeploy_symbian.h" +#include +#include +#include +#include +#include + +// Included from tools/shared +#include + +#define SYSBIN_DIR "/sys/bin" +#define HW_Z_DIR "epoc32/data/z" + +#define SUFFIX_DLL "dll" +#define SUFFIX_EXE "exe" +#define SUFFIX_QTPLUGIN "qtplugin" + +static QString fixPathToEpocOS(const QString &src) +{ + QString ret = Option::fixPathToTargetOS(src); + + bool pathHasDriveLetter = false; + if (ret.size() > 1) + pathHasDriveLetter = (ret.at(1) == QLatin1Char(':')); + + return pathHasDriveLetter ? ret.replace('/', '\\') : QDir::toNativeSeparators(ret); +} + +static bool isPlugin(const QFileInfo& info, const QString& devicePath) +{ + // Libraries are plugins if deployment path is something else than + // SYSBIN_DIR with or without drive letter + if (0 == info.suffix().compare(QLatin1String(SUFFIX_DLL), Qt::CaseInsensitive) + && (devicePath.size() < 8 + || (0 != devicePath.compare(QLatin1String(SYSBIN_DIR), Qt::CaseInsensitive) + && 0 != devicePath.mid(1).compare(QLatin1String(":" SYSBIN_DIR), Qt::CaseInsensitive) + && 0 != devicePath.compare(qt_epocRoot() + QLatin1String(HW_Z_DIR SYSBIN_DIR))))) { + return true; + } else { + return false; + } +} + +static bool isBinary(const QFileInfo& info) +{ + if (0 == info.suffix().compare(QLatin1String(SUFFIX_DLL), Qt::CaseInsensitive) || + 0 == info.suffix().compare(QLatin1String(SUFFIX_EXE), Qt::CaseInsensitive)) { + return true; + } else { + return false; + } +} + +static void createPluginStub(const QFileInfo& info, + const QString& devicePath, + DeploymentList &deploymentList, + QStringList& generatedDirs, + QStringList& generatedFiles) +{ + QString pluginStubDir = Option::output_dir + QLatin1Char('/') + QLatin1String(PLUGIN_STUB_DIR); + QDir().mkpath(pluginStubDir); + if (!generatedDirs.contains(pluginStubDir)) + generatedDirs << pluginStubDir; + // Plugin stubs must have different name from the actual plugins, because + // the toolchain for creating ROM images cannot handle non-binary .dll files properly. + QFile stubFile(pluginStubDir + QLatin1Char('/') + info.completeBaseName() + QLatin1Char('.') + QLatin1String(SUFFIX_QTPLUGIN)); + if (stubFile.open(QIODevice::WriteOnly)) { + if (!generatedFiles.contains(stubFile.fileName())) + generatedFiles << stubFile.fileName(); + QTextStream t(&stubFile); + // Add note to stub so that people will not wonder what it is. + // Creation date is added to make new stub to deploy always to + // force plugin cache miss when loading plugins. + t << "This file is a Qt plugin stub file. The real Qt plugin is located in " SYSBIN_DIR ". Created:" << QDateTime::currentDateTime().toString(Qt::ISODate) << "\n"; + } else { + fprintf(stderr, "cannot deploy \"%s\" because of plugin stub file creation failed\n", info.fileName().toLatin1().constData()); + } + QFileInfo stubInfo(stubFile); + deploymentList.append(CopyItem(Option::fixPathToLocalOS(stubInfo.absoluteFilePath()), + fixPathToEpocOS(devicePath + "/" + stubInfo.fileName()))); +} + +QString generate_uid(const QString& target) +{ + static QMap targetToUid; + + QString tmp = targetToUid[target]; + + if (!tmp.isEmpty()) { + return tmp; + } + + quint32 hash = 5381; + int c; + + for (int i = 0; i < target.size(); ++i) { + c = target.at(i).toAscii(); + hash ^= c + ((c - i) << i % 20) + ((c + i) << (i + 5) % 20) + ((c - 2 * i) << (i + 10) % 20) + ((c + 2 * i) << (i + 15) % 20); + } + + tmp.setNum(hash, 16); + for (int i = tmp.size(); i < 8; ++i) + tmp.prepend("0"); + + targetToUid[target] = tmp; + + return tmp; +} + +// UIDs starting with 0xE are test UIDs in symbian +QString generate_test_uid(const QString& target) +{ + QString tmp = generate_uid(target); + tmp.replace(0, 1, "E"); + tmp.prepend("0x"); + + return tmp; +} + + +void initProjectDeploySymbian(QMakeProject* project, + DeploymentList &deploymentList, + const QString &testPath, + bool deployBinaries, + bool epocBuild, + const QString &platform, + const QString &build, + QStringList& generatedDirs, + QStringList& generatedFiles) +{ + QString targetPath = testPath; + if (targetPath.endsWith("/") || targetPath.endsWith("\\")) + targetPath = targetPath.mid(0, targetPath.size() - 1); + + bool targetPathHasDriveLetter = false; + if (targetPath.size() > 1) { + targetPathHasDriveLetter = targetPath.at(1) == QLatin1Char(':'); + } + + QString deploymentDrive; + if (0 == platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM))) { + deploymentDrive = qt_epocRoot() + HW_Z_DIR; + } else { + deploymentDrive = targetPathHasDriveLetter ? targetPath.left(2) : QLatin1String("c:"); + } + + foreach(QString item, project->values("DEPLOYMENT")) { + QString devicePath = project->first(item + ".path"); + QString devicePathWithoutDrive = devicePath; + + bool devicePathHasDriveLetter = false; + if (devicePath.size() > 1) { + devicePathHasDriveLetter = devicePath.at(1) == QLatin1Char(':'); + } + + // Sometimes devicePath can contain disk but APP_RESOURCE_DIR does not, + // so remove the drive letter for comparison purposes. + if (devicePathHasDriveLetter) + { + devicePathWithoutDrive.remove(0,2); + } + if (!deployBinaries + && 0 != platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM)) + && !devicePathWithoutDrive.isEmpty() + && (0 == devicePathWithoutDrive.compare(project->values("APP_RESOURCE_DIR").join(""), Qt::CaseInsensitive) + || 0 == devicePathWithoutDrive.compare(project->values("REG_RESOURCE_IMPORT_DIR").join(""), Qt::CaseInsensitive))) { + // Do not deploy resources in emulator builds, as that seems to cause conflicts + // If there is ever a real need to deploy pre-built resources for emulator, + // BLD_INF_RULES.prj_exports can be used as a workaround. + continue; + } + + if (devicePath.isEmpty() || devicePath == QLatin1String(".")) { + devicePath = targetPath; + } + // check if item.path is relative (! either / or \) + else if (!(devicePath.at(0) == QLatin1Char('/') + || devicePath.at(0) == QLatin1Char('\\') + || devicePathHasDriveLetter)) { + // Create output path + devicePath = Option::fixPathToLocalOS(QDir::cleanPath(targetPath + QLatin1Char('/') + devicePath)); + } else { + if (0 == platform.compare(QLatin1String(EMULATOR_DEPLOYMENT_PLATFORM))) { + if (devicePathHasDriveLetter) { + if (devicePath.startsWith("!")) + devicePath = qt_epocRoot() + "epoc32/winscw/c" + devicePath.remove(0, 2); + else + devicePath = qt_epocRoot() + "epoc32/winscw/" + devicePath.remove(1, 1); + } else { + devicePath = qt_epocRoot() + "epoc32/winscw/c" + devicePath; + } + } else { + if (devicePathHasDriveLetter + && 0 == platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM))) { + devicePath.remove(0,2); + } + if (0 == platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM)) + || (!devicePathHasDriveLetter && targetPathHasDriveLetter)) { + devicePath = deploymentDrive + devicePath; + } + } + } + + devicePath.replace(QLatin1String("\\"), QLatin1String("/")); + + if (!deployBinaries + && 0 == devicePath.right(8).compare(QLatin1String(SYSBIN_DIR), Qt::CaseInsensitive) + && 0 != platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM))) { + // Skip deploying to SYSBIN_DIR for anything but binary deployments + // Note: Deploying pre-built binaries also follow this rule, so emulator builds + // will not get those deployed. Since there is no way to differentiate currently + // between pre-built binaries for emulator and HW anyway, this is not a major issue. + continue; + } + + QStringList flags = project->values(item + ".flags"); + + // ### Qt 5: remove .sources, inconsistent with INSTALLS + foreach(QString source, project->values(item + ".sources") + project->values(item + ".files")) { + source = Option::fixPathToLocalOS(source); + QString nameFilter; + QFileInfo info(source); + QString searchPath; + bool dirSearch = false; + + if (info.isDir()) { + nameFilter = QLatin1String("*"); + searchPath = info.absoluteFilePath(); + dirSearch = true; + } else { + if (info.exists() || source.indexOf('*') != -1) { + nameFilter = source.split(QDir::separator()).last(); + searchPath = info.absolutePath(); + } else { + // Entry was not found. That is ok if it is a binary, since those do not necessarily yet exist. + // Dlls need to be processed even when not deploying binaries for the stubs + if (isBinary(info)) { + if (deployBinaries) { + // Executables and libraries are deployed to \sys\bin + QFileInfo targetPath; + if (epocBuild) + targetPath.setFile(qt_epocRoot() + "epoc32/release/" + platform + "/" + build + "/"); + else + targetPath.setFile(info.path() + QDir::separator()); + if(devicePathHasDriveLetter) { + deploymentList.append(CopyItem( + Option::fixPathToLocalOS(targetPath.absolutePath() + "/" + info.fileName(), + false, true), + fixPathToEpocOS(devicePath.left(2) + QLatin1String(SYSBIN_DIR "/") + + info.fileName()), + flags)); + } else { + deploymentList.append(CopyItem( + Option::fixPathToLocalOS(targetPath.absolutePath() + "/" + info.fileName(), + false, true), + fixPathToEpocOS(deploymentDrive + QLatin1String("/" SYSBIN_DIR "/") + + info.fileName()), + flags)); + } + } + if (isPlugin(info, devicePath)) { + createPluginStub(info, devicePath, deploymentList, generatedDirs, generatedFiles); + continue; + } + } else { + // Generate deployment even if file doesn't exist, as this may be the case + // when generating .pkg files. + deploymentList.append(CopyItem(Option::fixPathToLocalOS(info.absoluteFilePath()), + fixPathToEpocOS(devicePath + "/" + info.fileName()), + flags)); + continue; + } + } + } + + int pathSize = info.absolutePath().size(); + QDirIterator iterator(searchPath, QStringList() << nameFilter + , QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks + , dirSearch ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags); + + while (iterator.hasNext()) { + iterator.next(); + QFileInfo iteratorInfo(iterator.filePath()); + QString absoluteItemPath = Option::fixPathToLocalOS(iteratorInfo.absolutePath()); + int diffSize = absoluteItemPath.size() - pathSize; + + if (!iteratorInfo.isDir()) { + if (isPlugin(iterator.fileInfo(), devicePath)) { + // This deploys pre-built plugins. Other pre-built binaries will deploy normally, + // as they have SYSBIN_DIR target path. + if (deployBinaries + || (0 == platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM)))) { + if (devicePathHasDriveLetter) { + deploymentList.append(CopyItem( + Option::fixPathToLocalOS(absoluteItemPath + "/" + iterator.fileName()), + fixPathToEpocOS(devicePath.left(2) + QLatin1String(SYSBIN_DIR "/") + + iterator.fileName()), + flags)); + } else { + deploymentList.append(CopyItem( + Option::fixPathToLocalOS(absoluteItemPath + "/" + iterator.fileName()), + fixPathToEpocOS(deploymentDrive + QLatin1String("/" SYSBIN_DIR "/") + + iterator.fileName()), + flags)); + } + } + createPluginStub(info, devicePath + "/" + absoluteItemPath.right(diffSize), + deploymentList, generatedDirs, generatedFiles); + continue; + } else { + deploymentList.append(CopyItem( + Option::fixPathToLocalOS(absoluteItemPath + "/" + iterator.fileName()), + fixPathToEpocOS(devicePath + "/" + absoluteItemPath.right(diffSize) + + "/" + iterator.fileName()), + flags)); + } + } + } + } + } + + // Remove deployments that do not actually do anything + if (0 == platform.compare(QLatin1String(EMULATOR_DEPLOYMENT_PLATFORM)) + || 0 == platform.compare(QLatin1String(ROM_DEPLOYMENT_PLATFORM))) { + QMutableListIterator i(deploymentList); + while(i.hasNext()) { + CopyItem &item = i.next(); + QFileInfo fromItem(item.from); + QFileInfo toItem(item.to); +#if defined(Q_OS_WIN) + if (0 == fromItem.absoluteFilePath().compare(toItem.absoluteFilePath(), Qt::CaseInsensitive)) +#else + if (0 == fromItem.absoluteFilePath().compare(toItem.absoluteFilePath())) +#endif + i.remove(); + } + } +} diff --git a/qmake/generators/symbian/initprojectdeploy_symbian.h b/qmake/generators/symbian/initprojectdeploy_symbian.h new file mode 100644 index 0000000000..539f4c4248 --- /dev/null +++ b/qmake/generators/symbian/initprojectdeploy_symbian.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef INITPROJECTDEPLOYSYMBIAN_H +#define INITPROJECTDEPLOYSYMBIAN_H + +#include +#include +#include +#include +#include +#include +#include + +#define PLUGIN_STUB_DIR "qmakepluginstubs" +#define ROM_DEPLOYMENT_PLATFORM "rom" +#define EMULATOR_DEPLOYMENT_PLATFORM "emulator" + +struct CopyItem +{ + CopyItem(const QString& f, const QString& t) + : from(f) , to(t) { } + CopyItem(const QString& f, const QString& t, const QStringList& l) + : from(f) , to(t), flags(l) { } + QString from; + QString to; + QStringList flags; +}; +typedef QList DeploymentList; + +extern QString generate_uid(const QString& target); +extern QString generate_test_uid(const QString& target); + +extern void initProjectDeploySymbian(QMakeProject* project, + DeploymentList &deploymentList, + const QString &testPath, + bool deployBinaries, + bool epocBuild, + const QString &platform, + const QString &build, + QStringList& generatedDirs, + QStringList& generatedFiles); + +#endif // INITPROJECTDEPLOYSYMBIAN_H diff --git a/qmake/generators/symbian/symbian_makefile.h b/qmake/generators/symbian/symbian_makefile.h new file mode 100644 index 0000000000..c49845aeb1 --- /dev/null +++ b/qmake/generators/symbian/symbian_makefile.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SYMBIAN_MAKEFILE_H +#define SYMBIAN_MAKEFILE_H + +#include "symbiancommon.h" + +// This allows us to reuse the code for both win32 and unix makefile generators. +template +class SymbianMakefileTemplate : public T, public SymbianCommonGenerator +{ +public: + SymbianMakefileTemplate() : SymbianCommonGenerator(this) {} + + void init() + { + T::init(); + SymbianCommonGenerator::init(); + } + + bool writeMakefile(QTextStream &t) + { + QString numberOfIcons; + QString iconFile; + QMap userRssRules; + readRssRules(numberOfIcons, iconFile, userRssRules); + + // Generate pkg files if there are any actual files to deploy + bool generatePkg = false; + + if (targetType == TypeExe) { + generatePkg = true; + } else { + const QStringList deployments = this->project->values("DEPLOYMENT"); + for (int i = 0; i < deployments.count(); ++i) { + // ### Qt 5: remove .sources, inconsistent with INSTALLS + if (!this->project->values(deployments.at(i) + ".sources").isEmpty() || + !this->project->values(deployments.at(i) + ".files").isEmpty()) { + generatePkg = true; + break; + } + } + } + + SymbianLocalizationList symbianLocalizationList; + parseTsFiles(&symbianLocalizationList); + + if (generatePkg) { + generatePkgFile(iconFile, false, symbianLocalizationList); + } + + if (targetType == TypeExe) { + if (!this->project->values("CONFIG").contains("no_icon", Qt::CaseInsensitive)) { + writeRegRssFile(userRssRules); + writeRssFile(numberOfIcons, iconFile); + writeLocFile(symbianLocalizationList); + } + } + + writeCustomDefFile(); + + return T::writeMakefile(t); + } +}; + +#endif // SYMBIAN_MAKEFILE_H diff --git a/qmake/generators/symbian/symbiancommon.cpp b/qmake/generators/symbian/symbiancommon.cpp new file mode 100644 index 0000000000..32b465b4bf --- /dev/null +++ b/qmake/generators/symbian/symbiancommon.cpp @@ -0,0 +1,1117 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "symbiancommon.h" +#include +#include + +// Included from tools/shared +#include + +#define RESOURCE_DIRECTORY_RESOURCE "\\\\resource\\\\apps\\\\" + +#define RSS_RULES "RSS_RULES" +#define RSS_RULES_BASE "RSS_RULES." +#define RSS_TAG_NBROFICONS "number_of_icons" +#define RSS_TAG_ICONFILE "icon_file" +#define RSS_TAG_HEADER "header" +#define RSS_TAG_SERVICE_LIST "service_list" +#define RSS_TAG_FILE_OWNERSHIP_LIST "file_ownership_list" +#define RSS_TAG_DATATYPE_LIST "datatype_list" +#define RSS_TAG_FOOTER "footer" +#define RSS_TAG_DEFAULT "default_rules" // Same as just giving rules without tag + +#define PLUGIN_COMMON_DEF_FILE_ACTUAL "plugin_commonu.def" + +#define MANUFACTURER_NOTE_FILE "manufacturer_note.txt" +#define DEFAULT_MANUFACTURER_NOTE \ + "The package is not supported for devices from this manufacturer. Please try the selfsigned " \ + "version of the package instead." + +SymbianCommonGenerator::SymbianCommonGenerator(MakefileGenerator *generator) + : generator(generator) +{ +} + +void SymbianCommonGenerator::init() +{ + QMakeProject *project = generator->project; + fixedTarget = project->first("QMAKE_ORIG_TARGET"); + if (fixedTarget.isEmpty()) + fixedTarget = project->first("TARGET"); + fixedTarget = generator->unescapeFilePath(fixedTarget); + fixedTarget = removePathSeparators(fixedTarget); + removeSpecialCharacters(fixedTarget); + + // This should not be empty since the mkspecs are supposed to set it if missing. + uid3 = project->first("TARGET.UID3").trimmed(); + + if ((project->values("TEMPLATE")).contains("app")) + targetType = TypeExe; + else if ((project->values("TEMPLATE")).contains("lib")) { + // Check CONFIG to see if we are to build staticlib or dll + if (project->isActiveConfig("staticlib") || project->isActiveConfig("static")) + targetType = TypeLib; + else if (project->isActiveConfig("plugin")) + targetType = TypePlugin; + else + targetType = TypeDll; + } else { + targetType = TypeSubdirs; + } + + // UID is valid as either hex or decimal, so just convert it to number and back to hex + // to get proper string for private dir + bool conversionOk = false; + uint uidNum = uid3.toUInt(&conversionOk, 0); + + if (!conversionOk) { + fprintf(stderr, "Error: Invalid UID \"%s\".\n", uid3.toUtf8().constData()); + } else { + privateDirUid.setNum(uidNum, 16); + while (privateDirUid.length() < 8) + privateDirUid.insert(0, QLatin1Char('0')); + } +} + +bool SymbianCommonGenerator::containsStartWithItem(const QChar &c, const QStringList& src) +{ + bool result = false; + foreach(QString str, src) { + if (str.startsWith(c)) { + result = true; + break; + } + } + return result; +} + +void SymbianCommonGenerator::removeSpecialCharacters(QString& str) +{ + // When modifying this method check also symbianRemoveSpecialCharacters in symbian.conf + QString underscore = QLatin1String("_"); + str.replace(QLatin1String("/"), underscore); + str.replace(QLatin1String("\\"), underscore); + str.replace(QLatin1String(" "), underscore); + str.replace(QLatin1String(":"), underscore); +} + +QString romPath(const QString& path) +{ + if(path.length() > 2 && path[1] == ':') + return QLatin1String("z:") + path.mid(2); + return QLatin1String("z:") + path; +} + +void SymbianCommonGenerator::generatePkgFile(const QString &iconFile, + bool epocBuild, + const SymbianLocalizationList &symbianLocalizationList) +{ + QMakeProject *project = generator->project; + QString pkgFilename = Option::output_dir + QLatin1Char('/') + + QString("%1_template.pkg").arg(fixedTarget); + + QFile pkgFile(pkgFilename); + if (!pkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + PRINT_FILE_CREATE_ERROR(pkgFilename); + return; + } + + QString stubPkgFileName = Option::output_dir + QLatin1Char('/') + + QString("%1_stub.pkg").arg(fixedTarget); + + QFile stubPkgFile(stubPkgFileName); + if (!stubPkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + PRINT_FILE_CREATE_ERROR(stubPkgFileName); + return; + } + + generatedFiles << pkgFile.fileName(); + QTextStream t(&pkgFile); + generatedFiles << stubPkgFile.fileName(); + QTextStream ts(&stubPkgFile); + + QString installerSisHeader = project->values("DEPLOYMENT.installer_header").join("\n"); + if (installerSisHeader.isEmpty()) { + // Use correct protected UID for publishing if application UID is in protected range, + // otherwise use self-signable test UID. + QRegExp protUidMatcher("0[xX][0-7].*"); + if (protUidMatcher.exactMatch(uid3)) + installerSisHeader = QLatin1String("0x2002CCCF"); + else + installerSisHeader = QLatin1String("0xA000D7CE"); // Use default self-signable UID + } + + QString wrapperStreamBuffer; + QTextStream tw(&wrapperStreamBuffer); + + QString dateStr = QDateTime::currentDateTime().toString(Qt::ISODate); + + // Header info + QString wrapperPkgFilename = Option::output_dir + QLatin1Char('/') + QString("%1_installer.%2") + .arg(fixedTarget).arg("pkg"); + + QString headerComment = "; %1 generated by qmake at %2\n" + "; This file is generated by qmake and should not be modified by the user\n" + ";\n\n"; + t << headerComment.arg(pkgFilename).arg(dateStr); + tw << headerComment.arg(wrapperPkgFilename).arg(dateStr); + ts << headerComment.arg(stubPkgFileName).arg(dateStr); + + QStringList commonRawPreRules; + QStringList mainRawPreRules; + QStringList instRawPreRules; + QStringList stubRawPreRules; + + // Though there can't be more than one language or header line, use stringlists + // in case user wants comments to go with the rules. + // Note that it makes no sense to have file specific language or header rules, + // except what is provided for installer header via "DEPLOYMENT.installer_header" variable, + // because stub and main headers should always match. Vendor rules are similarly limited to + // make code cleaner as it is unlikely anyone will want different vendor in different files. + QStringList languageRules; + QStringList headerRules; + QStringList vendorRules; + + QStringList commonRawPostRules; + QStringList mainRawPostRules; + QStringList instRawPostRules; + QStringList stubRawPostRules; + + QStringList failList; // Used for detecting incorrect usage + + QString emptySuffix; + QString mainSuffix(".main"); + QString instSuffix(".installer"); + QString stubSuffix(".stub"); + + foreach(QString item, project->values("DEPLOYMENT")) { + parsePreRules(item, emptySuffix, &commonRawPreRules, &languageRules, &headerRules, &vendorRules); + parsePreRules(item, mainSuffix, &mainRawPreRules, &failList, &failList, &failList); + parsePreRules(item, instSuffix, &instRawPreRules, &failList, &failList, &failList); + parsePreRules(item, stubSuffix, &stubRawPreRules, &failList, &failList, &failList); + + parsePostRules(item, emptySuffix, &commonRawPostRules); + parsePostRules(item, mainSuffix, &mainRawPostRules); + parsePostRules(item, instSuffix, &instRawPostRules); + parsePostRules(item, stubSuffix, &stubRawPostRules); + } + + if (!failList.isEmpty()) { + fprintf(stderr, "Warning: Custom language, header, or vendor definitions are not " + "supported by file specific pkg_prerules.* variables.\n" + "Use plain pkg_prerules and/or DEPLOYMENT.installer_header for customizing " + "these items.\n"); + } + + foreach(QString item, commonRawPreRules) { + if (item.startsWith("(")) { + // Only regular pkg file should have package dependencies + mainRawPreRules << item; + } else if (item.startsWith("[")) { + // stub pkg file should not have platform dependencies + mainRawPreRules << item; + instRawPreRules << item; + } else { + mainRawPreRules << item; + instRawPreRules << item; + stubRawPreRules << item; + } + } + + // Currently common postrules only go to main + mainRawPostRules << commonRawPostRules; + + // Apply some defaults if specific data does not exist in PKG pre-rules + if (languageRules.isEmpty()) { + if (symbianLocalizationList.isEmpty()) { + languageRules << "; Language\n&EN\n\n"; + } else { + QStringList langCodes; + SymbianLocalizationListIterator iter(symbianLocalizationList); + while (iter.hasNext()) { + const SymbianLocalization &loc = iter.next(); + langCodes << loc.symbianLanguageCode; + } + languageRules << QString("; Languages\n&%1\n\n").arg(langCodes.join(",")); + } + } else if (headerRules.isEmpty()) { + // In case user defines langs, he must take care also about SIS header + fprintf(stderr, "Warning: If language is defined with DEPLOYMENT pkg_prerules, also the SIS header must be defined\n"); + } + + t << languageRules.join("\n") << endl; + tw << languageRules.join("\n") << endl; + ts << languageRules.join("\n") << endl; + + // Determine application version. If version has missing component values, + // those will default to zero. + // If VERSION is missing altogether or is invalid, use "1,0,0" + QStringList verNumList = project->first("VERSION").split('.'); + uint major = 0; + uint minor = 0; + uint patch = 0; + bool success = false; + + if (verNumList.size() > 0) { + major = verNumList[0].toUInt(&success); + if (success && verNumList.size() > 1) { + minor = verNumList[1].toUInt(&success); + if (success && verNumList.size() > 2) { + patch = verNumList[2].toUInt(&success); + } + } + } + + QString applicationVersion("1,0,0"); + if (success) + applicationVersion = QString("%1,%2,%3").arg(major).arg(minor).arg(patch); + + // Append package build version number if it is set + QString pkgBuildVersion = project->first("DEPLOYMENT.pkg_build_version"); + if (!pkgBuildVersion.isEmpty()) { + success = false; + uint build = pkgBuildVersion.toUInt(&success); + if (success && build < 100) { + if (pkgBuildVersion.size() == 1) + pkgBuildVersion.prepend(QLatin1Char('0')); + applicationVersion.append(pkgBuildVersion); + } else { + fprintf(stderr, "Warning: Invalid DEPLOYMENT.pkg_build_version (%s), must be a number between 0 - 99\n", qPrintable(pkgBuildVersion)); + } + } + + // Package header + QString sisHeader = "; SIS header: name, uid, version\n#{\"%1\"},(%2),%3\n\n"; + + QString defaultVisualTarget = project->values("DEPLOYMENT.display_name").join(" "); + if (defaultVisualTarget.isEmpty()) + defaultVisualTarget = generator->escapeFilePath(project->first("TARGET")); + defaultVisualTarget = removePathSeparators(defaultVisualTarget); + + QString visualTarget = generatePkgNameForHeader(symbianLocalizationList, defaultVisualTarget, false); + QString wrapperTarget = generatePkgNameForHeader(symbianLocalizationList, defaultVisualTarget, true); + + if (installerSisHeader.startsWith("0x", Qt::CaseInsensitive)) { + tw << sisHeader.arg(wrapperTarget).arg(installerSisHeader).arg(applicationVersion); + } else { + tw << installerSisHeader << endl; + } + + if (headerRules.isEmpty()) { + t << sisHeader.arg(visualTarget).arg(uid3).arg(applicationVersion); + ts << sisHeader.arg(visualTarget).arg(uid3).arg(applicationVersion); + } + else { + t << headerRules.join("\n") << endl; + ts << headerRules.join("\n") << endl; + } + + // Vendor name + if (!containsStartWithItem('%', vendorRules)) { + QString vendorStr = QLatin1String("\"Vendor\","); + QString locVendors = vendorStr; + for (int i = 1; i < symbianLocalizationList.size(); i++) { + locVendors.append(vendorStr); + } + locVendors.chop(1); + vendorRules << QString("; Default localized vendor name\n%{%1}\n\n").arg(locVendors); + } + if (!containsStartWithItem(':', vendorRules)) { + vendorRules << "; Default unique vendor name\n:\"Vendor\"\n\n"; + } + + t << vendorRules.join("\n") << endl; + tw << vendorRules.join("\n") << endl; + ts << vendorRules.join("\n") << endl; + + // PKG pre-rules - these are added before actual file installations i.e. SIS package body + QString comment = "\n; Manual PKG pre-rules from PRO files\n"; + + if (mainRawPreRules.size()) { + t << comment; + t << mainRawPreRules.join("\n") << endl; + } + if (instRawPreRules.size()) { + tw << comment; + tw << instRawPreRules.join("\n") << endl; + } + if (stubRawPreRules.size()) { + ts << comment; + ts << stubRawPreRules.join("\n") << endl; + } + + t << endl; + tw << endl; + ts << endl; + + // Begin Manufacturer block + if (!project->values("DEPLOYMENT.manufacturers").isEmpty()) { + QString manufacturerStr("IF "); + foreach(QString manufacturer, project->values("DEPLOYMENT.manufacturers")) { + manufacturerStr.append(QString("(MANUFACTURER)=(%1) OR \n ").arg(manufacturer)); + } + // Remove the final OR + manufacturerStr.chop(8); + t << manufacturerStr << endl; + } + + if (symbianLocalizationList.size()) { + // Add localized resources to DEPLOYMENT if default resource deployment is done + addLocalizedResourcesToDeployment("default_resource_deployment.files", symbianLocalizationList); + addLocalizedResourcesToDeployment("default_reg_deployment.files", symbianLocalizationList); + } + + // deploy files specified by DEPLOYMENT variable + QString remoteTestPath; + QString zDir; + remoteTestPath = QString("!:\\private\\%1").arg(privateDirUid); + if (epocBuild) + zDir = qt_epocRoot() + QLatin1String("epoc32/data/z"); + + DeploymentList depList; + initProjectDeploySymbian(project, depList, remoteTestPath, true, epocBuild, "$(PLATFORM)", "$(TARGET)", generatedDirs, generatedFiles); + if (depList.size()) + t << "; DEPLOYMENT" << endl; + for (int i = 0; i < depList.size(); ++i) { + QString from = depList.at(i).from; + QString to = depList.at(i).to; + QString flags; + bool showOnlyFile = false; + foreach(QString flag, depList.at(i).flags) { + if (flag == QLatin1String("FT") + || flag == QLatin1String("FILETEXT")) { + showOnlyFile = true; + } + flags.append(QLatin1Char(',')).append(flag); + } + + if (epocBuild) { + // Deploy anything not already deployed from under epoc32 instead from under + // \epoc32\data\z\ to enable using pkg file without rebuilding + // the project, which can be useful for some binary only distributions. + if (!from.contains(QLatin1String("epoc32"), Qt::CaseInsensitive)) { + from = to; + if (from.size() > 1 && from.at(1) == QLatin1Char(':')) + from = from.mid(2); + from.prepend(zDir); + } + } + + // Files with "FILETEXT"/"FT" flag are meant for showing only at installation time + // and therefore do not belong to the stub package and will not install the file into phone. + if (showOnlyFile) + to.clear(); + else + ts << QString("\"\" - \"%1\"").arg(romPath(to)) << endl; + + t << QString("\"%1\" - \"%2\"%3").arg(from.replace('\\','/')).arg(to).arg(flags) << endl; + + } + t << endl; + ts << endl; + + // PKG post-rules - these are added after actual file installations i.e. SIS package body + comment = "; Manual PKG post-rules from PRO files\n"; + + if (mainRawPostRules.size()) { + t << comment; + t << mainRawPostRules.join("\n") << endl; + } + if (instRawPostRules.size()) { + tw << comment; + tw << instRawPostRules.join("\n") << endl; + } + if (stubRawPostRules.size()) { + ts << comment; + ts << stubRawPostRules.join("\n") << endl; + } + + // Close Manufacturer block + if (!project->values("DEPLOYMENT.manufacturers").isEmpty()) { + QString manufacturerFailNoteFile; + if (project->values("DEPLOYMENT.manufacturers.fail_note").isEmpty()) { + manufacturerFailNoteFile = QString("%1_" MANUFACTURER_NOTE_FILE).arg(uid3); + QFile ft(manufacturerFailNoteFile); + if (ft.open(QIODevice::WriteOnly)) { + generatedFiles << ft.fileName(); + QTextStream t2(&ft); + + t2 << QString(DEFAULT_MANUFACTURER_NOTE) << endl; + } else { + PRINT_FILE_CREATE_ERROR(manufacturerFailNoteFile) + } + } else { + manufacturerFailNoteFile = project->values("DEPLOYMENT.manufacturers.fail_note").join(""); + } + + t << "ELSEIF NOT(0) ; MANUFACTURER" << endl + << "\"" << generator->fileInfo(manufacturerFailNoteFile).absoluteFilePath() << "\"" + << " - \"\", FILETEXT, TEXTEXIT" << endl + << "ENDIF ; MANUFACTURER" << endl; + } + + // Write wrapper pkg + if (!installerSisHeader.isEmpty()) { + QFile wrapperPkgFile(wrapperPkgFilename); + if (!wrapperPkgFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + PRINT_FILE_CREATE_ERROR(wrapperPkgFilename); + return; + } + + generatedFiles << wrapperPkgFile.fileName(); + QTextStream twf(&wrapperPkgFile); + + twf << wrapperStreamBuffer << endl; + + // Wrapped files deployment + QString currentPath = qmake_getpwd(); + QString sisName = QString("%1.sis").arg(fixedTarget); + twf << "\"" << currentPath << "/" << sisName << "\" - \"!:\\private\\2002CCCE\\import\\" << sisName << "\"" << endl; + + QString bootStrapPath = QLibraryInfo::location(QLibraryInfo::PrefixPath); + bootStrapPath.append("/smartinstaller.sis"); + QFileInfo fi(generator->fileInfo(bootStrapPath)); + twf << "@\"" << fi.absoluteFilePath() << "\",(0x2002CCCD)" << endl; + } +} + +QString SymbianCommonGenerator::removePathSeparators(QString &file) +{ + QString ret = file; + + if (QDir::separator().unicode() != '/') + ret.replace(QDir::separator(), QLatin1Char('/')); + + if (ret.indexOf(QLatin1Char('/')) >= 0) + ret.remove(0, ret.lastIndexOf(QLatin1Char('/')) + 1); + + return ret; +} + +void SymbianCommonGenerator::writeRegRssFile(QMap &userItems) +{ + QString filename(fixedTarget); + filename.append("_reg.rss"); + if (!Option::output_dir.isEmpty()) + filename = Option::output_dir + '/' + filename; + QFile ft(filename); + if (ft.open(QIODevice::WriteOnly)) { + generatedFiles << ft.fileName(); + QTextStream t(&ft); + t << "// ============================================================================" << endl; + t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: "; + t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl; + t << "// * This file is generated by qmake and should not be modified by the" << endl; + t << "// * user." << endl; + t << "// ============================================================================" << endl; + t << endl; + t << "#include <" << fixedTarget << ".rsg>" << endl; + t << "#include " << endl; + foreach(QString item, userItems[RSS_TAG_HEADER]) + t << item << endl; + t << endl; + t << "UID2 KUidAppRegistrationResourceFile" << endl; + t << "UID3 " << uid3 << endl << endl; + t << "RESOURCE APP_REGISTRATION_INFO" << endl; + t << "\t{" << endl; + t << "\tapp_file=\"" << fixedTarget << "\";" << endl; + t << "\tlocalisable_resource_file=\"" RESOURCE_DIRECTORY_RESOURCE << fixedTarget << "\";" << endl; + + writeRegRssList(t, userItems[RSS_TAG_SERVICE_LIST], + QLatin1String(RSS_TAG_SERVICE_LIST), + QLatin1String("SERVICE_INFO")); + writeRegRssList(t, userItems[RSS_TAG_FILE_OWNERSHIP_LIST], + QLatin1String(RSS_TAG_FILE_OWNERSHIP_LIST), + QLatin1String("FILE_OWNERSHIP_INFO")); + writeRegRssList(t, userItems[RSS_TAG_DATATYPE_LIST], + QLatin1String(RSS_TAG_DATATYPE_LIST), + QLatin1String("DATATYPE")); + t << endl; + + foreach(QString item, userItems[RSS_TAG_DEFAULT]) + t << "\t" << item.replace("\n","\n\t") << endl; + t << "\t}" << endl; + + foreach(QString item, userItems[RSS_TAG_FOOTER]) + t << item << endl; + } else { + PRINT_FILE_CREATE_ERROR(filename) + } +} + +void SymbianCommonGenerator::writeRegRssList(QTextStream &t, + QStringList &userList, + const QString &listTag, + const QString &listItem) +{ + int itemCount = userList.count(); + if (itemCount) { + t << "\t" << listTag << " ="<< endl; + t << "\t\t{" << endl; + foreach(QString item, userList) { + t << "\t\t" << listItem << endl; + t << "\t\t\t{" << endl; + t << "\t\t\t" << item.replace("\n","\n\t\t\t") << endl; + t << "\t\t\t}"; + if (--itemCount) + t << ","; + t << endl; + } + t << "\t\t}; "<< endl; + } +} + +void SymbianCommonGenerator::writeRssFile(QString &numberOfIcons, QString &iconFile) +{ + QString filename(fixedTarget); + if (!Option::output_dir.isEmpty()) + filename = Option::output_dir + '/' + filename; + filename.append(".rss"); + QFile ft(filename); + if (ft.open(QIODevice::WriteOnly)) { + generatedFiles << ft.fileName(); + QTextStream t(&ft); + t << "// ============================================================================" << endl; + t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: "; + t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl; + t << "// * This file is generated by qmake and should not be modified by the" << endl; + t << "// * user." << endl; + t << "// ============================================================================" << endl; + t << endl; + t << "CHARACTER_SET UTF8" << endl; + t << "#include " << endl; + t << "#include \"" << fixedTarget << ".loc\"" << endl; + t << endl; + t << "RESOURCE LOCALISABLE_APP_INFO r_localisable_app_info" << endl; + t << "\t{" << endl; + t << "\tshort_caption = STRING_r_short_caption;" << endl; + t << "\tcaption_and_icon =" << endl; + t << "\tCAPTION_AND_ICON_INFO" << endl; + t << "\t\t{" << endl; + t << "\t\tcaption = STRING_r_caption;" << endl; + + QString rssIconFile = iconFile; + rssIconFile = rssIconFile.replace("/", "\\\\"); + + if (numberOfIcons.isEmpty() || rssIconFile.isEmpty()) { + // There can be maximum one item in this tag, validated when parsed + t << "\t\tnumber_of_icons = 0;" << endl; + t << "\t\ticon_file = \"\";" << endl; + } else { + // There can be maximum one item in this tag, validated when parsed + t << "\t\tnumber_of_icons = " << numberOfIcons << ";" << endl; + t << "\t\ticon_file = \"" << rssIconFile << "\";" << endl; + } + t << "\t\t};" << endl; + t << "\t}" << endl; + t << endl; + } else { + PRINT_FILE_CREATE_ERROR(filename); + } +} + +void SymbianCommonGenerator::writeLocFile(const SymbianLocalizationList &symbianLocalizationList) +{ + QString filename = generateLocFileName(); + QFile ft(filename); + if (ft.open(QIODevice::WriteOnly)) { + generatedFiles << ft.fileName(); + QTextStream t(&ft); + + QString displayName = generator->project->values("DEPLOYMENT.display_name").join(" "); + if (displayName.isEmpty()) + displayName = generator->escapeFilePath(generator->project->first("TARGET")); + + t << "// ============================================================================" << endl; + t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: "; + t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl; + t << "// * This file is generated by qmake and should not be modified by the" << endl; + t << "// * user." << endl; + t << "// ============================================================================" << endl; + t << endl; + t << "#ifdef LANGUAGE_SC" << endl; + t << "#define STRING_r_short_caption \"" << displayName << "\"" << endl; + t << "#define STRING_r_caption \"" << displayName << "\"" << endl; + + SymbianLocalizationListIterator iter(symbianLocalizationList); + while (iter.hasNext()) { + const SymbianLocalization &loc = iter.next(); + QString shortCaption = loc.shortCaption; + QString longCaption = loc.longCaption; + if (shortCaption.isEmpty()) + shortCaption = displayName; + if (longCaption.isEmpty()) + longCaption = displayName; + + t << "#elif defined LANGUAGE_" << loc.symbianLanguageCode << endl; + t << "#define STRING_r_short_caption \"" << shortCaption << "\"" << endl; + t << "#define STRING_r_caption \"" << longCaption << "\"" << endl; + } + + t << "#else" << endl; + t << "#define STRING_r_short_caption \"" << displayName << "\"" << endl; + t << "#define STRING_r_caption \"" << displayName << "\"" << endl; + t << "#endif" << endl; + } else { + PRINT_FILE_CREATE_ERROR(filename); + } +} + +void SymbianCommonGenerator::readRssRules(QString &numberOfIcons, + QString &iconFile, QMap &userRssRules) +{ + QMakeProject *project = generator->project; + for (QMap::iterator it = project->variables().begin(); it != project->variables().end(); ++it) { + if (it.key().startsWith(RSS_RULES_BASE)) { + QString newKey = it.key().mid(sizeof(RSS_RULES_BASE) - 1); + if (newKey.isEmpty()) { + fprintf(stderr, "Warning: Empty RSS_RULES_BASE key encountered\n"); + continue; + } + QStringList newValues; + QStringList values = it.value(); + foreach(QString item, values) { + // If there is no stringlist defined for a rule, use rule value directly + // This is convenience for defining single line statements + if (project->values(item).isEmpty()) { + newValues << item; + } else { + QStringList itemList; + foreach(QString itemRow, project->values(item)) { + itemList << itemRow; + } + newValues << itemList.join("\n"); + } + } + // Verify that there is exactly one value in RSS_TAG_NBROFICONS + if (newKey == RSS_TAG_NBROFICONS) { + if (newValues.count() == 1) { + numberOfIcons = newValues[0]; + } else { + fprintf(stderr, "Warning: There must be exactly one value in '%s%s'\n", + RSS_RULES_BASE, RSS_TAG_NBROFICONS); + continue; + } + // Verify that there is exactly one value in RSS_TAG_ICONFILE + } else if (newKey == RSS_TAG_ICONFILE) { + if (newValues.count() == 1) { + iconFile = newValues[0]; + } else { + fprintf(stderr, "Warning: There must be exactly one value in '%s%s'\n", + RSS_RULES_BASE, RSS_TAG_ICONFILE); + continue; + } + } else if (newKey == RSS_TAG_HEADER + || newKey == RSS_TAG_SERVICE_LIST + || newKey == RSS_TAG_FILE_OWNERSHIP_LIST + || newKey == RSS_TAG_DATATYPE_LIST + || newKey == RSS_TAG_FOOTER + || newKey == RSS_TAG_DEFAULT) { + userRssRules[newKey] = newValues; + continue; + } else { + fprintf(stderr, "Warning: Unsupported key:'%s%s'\n", + RSS_RULES_BASE, newKey.toLatin1().constData()); + continue; + } + } + } + + QStringList newValues; + foreach(QString item, project->values(RSS_RULES)) { + // If there is no stringlist defined for a rule, use rule value directly + // This is convenience for defining single line statements + if (project->values(item).isEmpty()) { + newValues << item; + } else { + newValues << project->values(item); + } + } + userRssRules[RSS_TAG_DEFAULT] << newValues; + + // Validate that either both RSS_TAG_NBROFICONS and RSS_TAG_ICONFILE keys exist + // or neither of them exist + if (!((numberOfIcons.isEmpty() && iconFile.isEmpty()) || + (!numberOfIcons.isEmpty() && !iconFile.isEmpty()))) { + numberOfIcons.clear(); + iconFile.clear(); + fprintf(stderr, "Warning: Both or neither of '%s%s' and '%s%s' keys must exist.\n", + RSS_RULES_BASE, RSS_TAG_NBROFICONS, RSS_RULES_BASE, RSS_TAG_ICONFILE); + } + + // Validate that RSS_TAG_NBROFICONS contains only numbers + if (!numberOfIcons.isEmpty()) { + bool ok; + numberOfIcons = numberOfIcons.simplified(); + numberOfIcons.toInt(&ok); + if (!ok) { + numberOfIcons.clear(); + iconFile.clear(); + fprintf(stderr, "Warning: '%s%s' must be integer in decimal format.\n", + RSS_RULES_BASE, RSS_TAG_NBROFICONS); + } + } +} + +void SymbianCommonGenerator::writeCustomDefFile() +{ + if (targetType == TypePlugin && !generator->project->isActiveConfig("stdbinary")) { + // Create custom def file for plugin + QFile ft(Option::output_dir + QLatin1Char('/') + QLatin1String(PLUGIN_COMMON_DEF_FILE_ACTUAL)); + + if (ft.open(QIODevice::WriteOnly)) { + generatedFiles << ft.fileName(); + QTextStream t(&ft); + + t << "; ==============================================================================" << endl; + t << "; Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: "; + t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl; + t << "; This file is generated by qmake and should not be modified by the" << endl; + t << "; user." << endl; + t << "; Name : " PLUGIN_COMMON_DEF_FILE_ACTUAL << endl; + t << "; Part of : " << generator->project->values("TARGET").join(" ") << endl; + t << "; Description : Fixes common plugin symbols to known ordinals" << endl; + t << "; Version : " << endl; + t << ";" << endl; + t << "; ==============================================================================" << "\n" << endl; + + t << endl; + + t << "EXPORTS" << endl; + t << "\tqt_plugin_query_verification_data @ 1 NONAME" << endl; + t << "\tqt_plugin_instance @ 2 NONAME" << endl; + t << endl; + } else { + PRINT_FILE_CREATE_ERROR(QString(PLUGIN_COMMON_DEF_FILE_ACTUAL)) + } + } +} + +void SymbianCommonGenerator::parseTsFiles(SymbianLocalizationList *symbianLocalizationList) +{ + if (!generator->project->isActiveConfig("localize_deployment")) { + return; + } + + QStringList symbianTsFiles; + + symbianTsFiles << generator->project->values("SYMBIAN_MATCHED_TRANSLATIONS"); + + if (!symbianTsFiles.isEmpty()) { + fillQt2SymbianLocalizationList(symbianLocalizationList); + + QMutableListIterator iter(*symbianLocalizationList); + while (iter.hasNext()) { + SymbianLocalization &loc = iter.next(); + static QString matchStrTemplate = QLatin1String(".*_%1\\.ts"); + QString matchStr = matchStrTemplate.arg(loc.qtLanguageCode); + + foreach (QString file, symbianTsFiles) { + QRegExp matcher(matchStr); + if (matcher.exactMatch(file) && parseTsContent(file, &loc)) + break; + } + } + } +} + +void SymbianCommonGenerator::fillQt2SymbianLocalizationList(SymbianLocalizationList *symbianLocalizationList) +{ + static QString symbianCodePrefix = QLatin1String("SYMBIAN_LANG."); + + QStringList symbianLanguages = generator->project->values("SYMBIAN_MATCHED_LANGUAGES"); + + foreach (QString qtCode, symbianLanguages) { + SymbianLocalization newLoc; + QString symbianCodeVariable = symbianCodePrefix + qtCode; + newLoc.symbianLanguageCode = generator->project->first(symbianCodeVariable); + if (!newLoc.symbianLanguageCode.isEmpty()) { + newLoc.qtLanguageCode = qtCode; + symbianLocalizationList->append(newLoc); + } + } +} + +void SymbianCommonGenerator::parsePreRules(const QString &deploymentVariable, + const QString &variableSuffix, + QStringList *rawRuleList, + QStringList *languageRuleList, + QStringList *headerRuleList, + QStringList *vendorRuleList) +{ + QMakeProject *project = generator->project; + foreach(QString pkgrulesItem, project->values(deploymentVariable + ".pkg_prerules" + variableSuffix)) { + QStringList pkgrulesValue = project->values(pkgrulesItem); + // If there is no stringlist defined for a rule, use rule name directly + // This is convenience for defining single line statements + if (pkgrulesValue.isEmpty()) { + if (pkgrulesItem.startsWith("&")) + *languageRuleList << pkgrulesItem; + else if (pkgrulesItem.startsWith("#")) + *headerRuleList << pkgrulesItem; + else if (pkgrulesItem.startsWith("%") || pkgrulesItem.startsWith(":")) + *vendorRuleList << pkgrulesItem; + else + *rawRuleList << pkgrulesItem; + } else { + if (containsStartWithItem('&', pkgrulesValue)) { + foreach(QString pkgrule, pkgrulesValue) { + *languageRuleList << pkgrule; + } + } else if (containsStartWithItem('#', pkgrulesValue)) { + foreach(QString pkgrule, pkgrulesValue) { + *headerRuleList << pkgrule; + } + } else if (containsStartWithItem('%', pkgrulesValue) + || containsStartWithItem(':', pkgrulesValue)) { + foreach(QString pkgrule, pkgrulesValue) { + *vendorRuleList << pkgrule; + } + } else { + foreach(QString pkgrule, pkgrulesValue) { + *rawRuleList << pkgrule; + } + } + } + } +} + +void SymbianCommonGenerator::parsePostRules(const QString &deploymentVariable, + const QString &variableSuffix, + QStringList *rawRuleList) +{ + QMakeProject *project = generator->project; + foreach(QString pkgrulesItem, project->values(deploymentVariable + ".pkg_postrules" + variableSuffix)) { + QStringList pkgrulesValue = project->values(pkgrulesItem); + // If there is no stringlist defined for a rule, use rule name directly + // This is convenience for defining single line statements + if (pkgrulesValue.isEmpty()) { + *rawRuleList << pkgrulesItem; + } else { + foreach(QString pkgrule, pkgrulesValue) { + *rawRuleList << pkgrule; + } + } + } +} + +bool SymbianCommonGenerator::parseTsContent(const QString &tsFilename, SymbianLocalization *loc) +{ + bool retval = true; + QMakeProject *project = generator->project; + QFile tsFile(tsFilename); + + if (tsFile.exists()) { + if (tsFile.open(QIODevice::ReadOnly)) { + static QString applicationCaptionsContext = QLatin1String("QtApplicationCaptions"); + static QString pkgNameContext = QLatin1String("QtPackageNames"); + static QString tsElement = QLatin1String("TS"); + static QString contextElement = QLatin1String("context"); + static QString nameElement = QLatin1String("name"); + static QString messageElement = QLatin1String("message"); + static QString sourceElement = QLatin1String("source"); + static QString translationElement = QLatin1String("translation"); + static QString shortCaptionId = QLatin1String("Application short caption"); + static QString longCaptionId = QLatin1String("Application long caption"); + static QString pkgDisplayNameId = QLatin1String("Package name"); + static QString installerPkgDisplayNameId = QLatin1String("Smart installer package name"); + static QString languageAttribute = QLatin1String("language"); + static QChar underscoreChar = QLatin1Char('_'); + + enum CurrentContext { + ContextUnknown, + ContextUninteresting, + ContextInteresting + }; + + QXmlStreamReader xml(&tsFile); + + while (!xml.atEnd() && xml.name() != tsElement) + xml.readNextStartElement(); + + while (xml.readNextStartElement()) { + if (xml.name() == contextElement) { + CurrentContext currentContext = ContextUnknown; + while (xml.readNextStartElement()) { + if (currentContext == ContextUnknown) { + // Expect name element before message elements + if (xml.name() == nameElement) { + QString nameText = xml.readElementText(); + if (nameText == applicationCaptionsContext || nameText == pkgNameContext) { + currentContext = ContextInteresting; + } else { + currentContext = ContextUninteresting; + } + } else { + xml.skipCurrentElement(); + } + } else if (currentContext == ContextInteresting) { + if (xml.name() == messageElement) { + QString source; + QString translation; + while (xml.readNextStartElement()) { + if (xml.name() == sourceElement) { + source = xml.readElementText(); + } else if (xml.name() == translationElement) { + translation = xml.readElementText(); + } else { + xml.skipCurrentElement(); + } + } + + if (source == shortCaptionId) { + if (loc->shortCaption.isEmpty()) { + loc->shortCaption = translation; + } else { + fprintf(stderr, "Warning: Duplicate application short caption defined in (%s).\n", + qPrintable(tsFilename)); + } + } else if (source == longCaptionId) { + if (loc->longCaption.isEmpty()) { + loc->longCaption = translation; + } else { + fprintf(stderr, "Warning: Duplicate application long caption defined in (%s).\n", + qPrintable(tsFilename)); + } + } else if (source == pkgDisplayNameId) { + if (loc->pkgDisplayName.isEmpty()) { + loc->pkgDisplayName = translation; + } else { + fprintf(stderr, "Warning: Duplicate package display name defined in (%s).\n", + qPrintable(tsFilename)); + } + } else if (source == installerPkgDisplayNameId) { + if (loc->installerPkgDisplayName.isEmpty()) { + loc->installerPkgDisplayName = translation; + } else { + fprintf(stderr, "Warning: Duplicate smart installer package display name defined in (%s).\n", + qPrintable(tsFilename)); + } + } + } else { + xml.skipCurrentElement(); + } + } else { + xml.skipCurrentElement(); + } + } + } else { + xml.skipCurrentElement(); + } + } + if (xml.hasError()) { + retval = false; + fprintf(stderr, "ERROR: Encountered error \"%s\" when parsing ts file (%s).\n", + qPrintable(xml.errorString()), qPrintable(tsFilename)); + } + } else { + retval = false; + fprintf(stderr, "Warning: Could not open ts file (%s).\n", qPrintable(tsFilename)); + } + } else { + retval = false; + fprintf(stderr, "Warning: ts file does not exist: (%s), unable to parse it.\n", + qPrintable(tsFilename)); + } + + return retval; +} + +QString SymbianCommonGenerator::generatePkgNameForHeader(const SymbianLocalizationList &symbianLocalizationList, + const QString &defaultName, + bool isForSmartInstaller) +{ + QStringList allNames; + QString noTranslation = defaultName; + + if (isForSmartInstaller) + noTranslation += QLatin1String(" installer"); + + SymbianLocalizationListIterator iter(symbianLocalizationList); + while (iter.hasNext()) { + const SymbianLocalization &loc = iter.next(); + QString currentName; + if (isForSmartInstaller) { + currentName = loc.installerPkgDisplayName; + } else { + currentName = loc.pkgDisplayName; + } + + if (currentName.isEmpty()) + currentName = noTranslation; + + allNames << currentName; + } + + if (!allNames.size()) + allNames << noTranslation; + + return allNames.join("\",\""); + +} + +void SymbianCommonGenerator::addLocalizedResourcesToDeployment(const QString &deploymentFilesVar, + const SymbianLocalizationList &symbianLocalizationList) +{ + QStringList locResources; + foreach (QString defaultResource, generator->project->values(deploymentFilesVar)) { + if (defaultResource.endsWith(".rsc")) { + defaultResource.chop(2); + SymbianLocalizationListIterator iter(symbianLocalizationList); + while (iter.hasNext()) { + const SymbianLocalization &loc = iter.next(); + locResources << QString(defaultResource + loc.symbianLanguageCode); + } + } + } + generator->project->values(deploymentFilesVar) << locResources; +} + +QString SymbianCommonGenerator::generateLocFileName() +{ + QString fileName(fixedTarget); + if (!Option::output_dir.isEmpty()) + fileName = Option::output_dir + QLatin1Char('/') + fileName; + fileName.append(".loc"); + return fileName; +} diff --git a/qmake/generators/symbian/symbiancommon.h b/qmake/generators/symbian/symbiancommon.h new file mode 100644 index 0000000000..5182021439 --- /dev/null +++ b/qmake/generators/symbian/symbiancommon.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SYMBIANCOMMON_H +#define SYMBIANCOMMON_H + +#include +#include +#include "initprojectdeploy_symbian.h" + +#define PRINT_FILE_CREATE_ERROR(filename) fprintf(stderr, "Error: Could not create '%s'\n", qPrintable(filename)); + +class SymbianLocalization +{ +public: + QString qtLanguageCode; + QString symbianLanguageCode; + QString shortCaption; + QString longCaption; + QString pkgDisplayName; + QString installerPkgDisplayName; +}; + +typedef QList SymbianLocalizationList; +typedef QListIterator SymbianLocalizationListIterator; + +class SymbianCommonGenerator +{ +public: + enum TargetType { + TypeExe, + TypeDll, + TypeLib, + TypePlugin, + TypeSubdirs + }; + + + SymbianCommonGenerator(MakefileGenerator *generator); + + virtual void init(); + +protected: + + QString removePathSeparators(QString &file); + void removeSpecialCharacters(QString& str); + void generatePkgFile(const QString &iconFile, + bool epocBuild, + const SymbianLocalizationList &symbianLocalizationList); + bool containsStartWithItem(const QChar &c, const QStringList& src); + + void writeRegRssFile(QMap &useritems); + void writeRegRssList(QTextStream &t, QStringList &userList, + const QString &listTag, + const QString &listItem); + void writeRssFile(QString &numberOfIcons, QString &iconfile); + void writeLocFile(const SymbianLocalizationList &symbianLocalizationList); + void readRssRules(QString &numberOfIcons, + QString &iconFile, + QMap &userRssRules); + + void writeCustomDefFile(); + + void parseTsFiles(SymbianLocalizationList *symbianLocalizationList); + void fillQt2SymbianLocalizationList(SymbianLocalizationList *symbianLocalizationList); + + void parsePreRules(const QString &deploymentVariable, + const QString &variableSuffix, + QStringList *rawRuleList, + QStringList *languageRuleList, + QStringList *headerRuleList, + QStringList *vendorRuleList); + void parsePostRules(const QString &deploymentVariable, + const QString &variableSuffix, + QStringList *rawRuleList); + bool parseTsContent(const QString &tsFilename, SymbianLocalization *loc); + QString generatePkgNameForHeader(const SymbianLocalizationList &symbianLocalizationList, + const QString &defaultName, + bool isForSmartInstaller); + void addLocalizedResourcesToDeployment(const QString &deploymentFilesVar, + const SymbianLocalizationList &symbianLocalizationList); + QString generateLocFileName(); + +protected: + MakefileGenerator *generator; + + QStringList generatedFiles; + QStringList generatedDirs; + QString fixedTarget; + QString privateDirUid; + QString uid3; + TargetType targetType; +}; + +#endif // SYMBIANCOMMON_H diff --git a/qmake/generators/symbian/symmake.cpp b/qmake/generators/symbian/symmake.cpp new file mode 100644 index 0000000000..08d33700f7 --- /dev/null +++ b/qmake/generators/symbian/symmake.cpp @@ -0,0 +1,1136 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "symmake.h" + +#include +#include +#include +#include +#include +#include +#include + +// Included from tools/shared +#include + +#define RESOURCE_DIRECTORY_MMP "/resource/apps" +#define REGISTRATION_RESOURCE_DIRECTORY_HW "/private/10003a3f/import/apps" +#define PLUGIN_COMMON_DEF_FILE_FOR_MMP "./plugin_common.def" +#define BLD_INF_FILENAME_LEN (sizeof(BLD_INF_FILENAME) - 1) + +#define BLD_INF_RULES_BASE "BLD_INF_RULES." +#define BLD_INF_TAG_PLATFORMS "prj_platforms" +#define BLD_INF_TAG_MMPFILES "prj_mmpfiles" +#define BLD_INF_TAG_TESTMMPFILES "prj_testmmpfiles" +#define BLD_INF_TAG_EXTENSIONS "prj_extensions" +#define BLD_INF_TAG_TESTEXTENSIONS "prj_testextensions" + +#define MMP_TARGET "TARGET" +#define MMP_TARGETTYPE "TARGETTYPE" +#define MMP_SECUREID "SECUREID" +#define MMP_OPTION "OPTION" +#define MMP_LINKEROPTION "LINKEROPTION" +#define MMP_CAPABILITY "CAPABILITY" +#define MMP_EPOCALLOWDLLDATA "EPOCALLOWDLLDATA" +#define MMP_EPOCHEAPSIZE "EPOCHEAPSIZE" +#define MMP_EPOCSTACKSIZE "EPOCSTACKSIZE" +#define MMP_UID "UID" +#define MMP_VENDORID "VENDORID" +#define MMP_VERSION "VERSION" +#define MMP_START_RESOURCE "START RESOURCE" +#define MMP_END_RESOURCE "END" + +#define VAR_CXXFLAGS "QMAKE_CXXFLAGS" +#define VAR_CFLAGS "QMAKE_CFLAGS" +#define VAR_LFLAGS "QMAKE_LFLAGS" + +#define DEFINE_REPLACE_REGEXP "[^A-Z0-9_]" + +QString SymbianMakefileGenerator::fixPathForMmp(const QString& origPath, const QDir& parentDir) +{ + static QString epocRootStr; + if (epocRootStr.isEmpty()) { + epocRootStr = qt_epocRoot(); + QFileInfo efi(epocRootStr); + if (!efi.exists() || epocRootStr.isEmpty()) { + fprintf(stderr, "Unable to resolve epocRoot '%s' to real dir on current drive, defaulting to '/' for mmp paths\n", qPrintable(qt_epocRoot())); + epocRootStr = "/"; + } else { + epocRootStr = efi.absoluteFilePath(); + } + if (!epocRootStr.endsWith("/")) + epocRootStr += "/"; + + epocRootStr += "epoc32/"; + } + + QString resultPath = origPath; + + // Make it relative, unless it starts with "%epocroot%/epoc32/" + if (resultPath.startsWith(epocRootStr, Qt::CaseInsensitive)) { + resultPath.replace(epocRootStr, "/epoc32/", Qt::CaseInsensitive); + } else { + resultPath = parentDir.relativeFilePath(resultPath); + } + resultPath = QDir::cleanPath(resultPath); + + if (resultPath.isEmpty()) + resultPath = "."; + + return resultPath; +} + +QString SymbianMakefileGenerator::absolutizePath(const QString& origPath) +{ + // Prepend epocroot to any paths beginning with "/epoc32/" + QString resultPath = QDir::fromNativeSeparators(origPath); + if (resultPath.startsWith("/epoc32/", Qt::CaseInsensitive)) + resultPath = QDir::fromNativeSeparators(qt_epocRoot()) + resultPath.mid(1); + + QFileInfo fi(fileInfo(resultPath)); + + // Since origPath can be something given in HEADERS, we need to check if we are dealing + // with a file or a directory. In case the origPath doesn't yet exist, isFile() returns + // false and we default to assuming it is a dir. + if (fi.isFile()) { + resultPath = fi.absolutePath(); + } else { + resultPath = fi.absoluteFilePath(); + } + + resultPath = QDir::cleanPath(resultPath); + + return resultPath; +} + +SymbianMakefileGenerator::SymbianMakefileGenerator() : MakefileGenerator(), SymbianCommonGenerator(this) { } +SymbianMakefileGenerator::~SymbianMakefileGenerator() { } + +void SymbianMakefileGenerator::writeHeader(QTextStream &t) +{ + t << "// ============================================================================" << endl; + t << "// * Makefile for building: " << escapeFilePath(var("TARGET")) << endl; + t << "// * Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: "; + t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl; + t << "// * This file is generated by qmake and should not be modified by the" << endl; + t << "// * user." << endl; + t << "// * Project: " << fileFixify(project->projectFile()) << endl; + t << "// * Template: " << var("TEMPLATE") << endl; + t << "// ============================================================================" << endl; + t << endl; + + // Defining define for bld.inf + + QString shortProFilename = project->projectFile(); + shortProFilename.replace(0, shortProFilename.lastIndexOf("/") + 1, QString("")); + shortProFilename.replace(Option::pro_ext, QString("")); + + QString bldinfDefine = shortProFilename; + bldinfDefine.append("_"); + bldinfDefine.append(generate_uid(project->projectFile())); + bldinfDefine = bldinfDefine.toUpper(); + + // replace anything not alphanumeric with underscore + QRegExp replacementMask(DEFINE_REPLACE_REGEXP); + bldinfDefine.replace(replacementMask, QLatin1String("_")); + + bldinfDefine.prepend("BLD_INF_"); + + t << "#define " << bldinfDefine << endl << endl; +} + +bool SymbianMakefileGenerator::writeMakefile(QTextStream &t) +{ + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + fprintf(stderr, "Project files not generated because all requirements are not met:\n\t%s\n", + qPrintable(var("QMAKE_FAILED_REQUIREMENTS"))); + return false; + } + + writeHeader(t); + + QString numberOfIcons; + QString iconFile; + QMap userRssRules; + readRssRules(numberOfIcons, iconFile, userRssRules); + + SymbianLocalizationList symbianLocalizationList; + parseTsFiles(&symbianLocalizationList); + + // Generate pkg files if there are any actual files to deploy + bool generatePkg = false; + + if (targetType == TypeExe) { + generatePkg = true; + } else { + foreach(QString item, project->values("DEPLOYMENT")) { + // ### Qt 5: remove .sources, inconsistent with INSTALLS + if (!project->values(item + ".sources").isEmpty() || + !project->values(item + ".files").isEmpty()) { + generatePkg = true; + break; + } + } + } + + if (generatePkg) { + generatePkgFile(iconFile, true, symbianLocalizationList); + } + + writeBldInfContent(t, generatePkg, iconFile); + + // Generate empty wrapper makefile here, because wrapper makefile must exist before writeMkFile, + // but all required data is not yet available. + bool isPrimaryMakefile = true; + QString wrapperFileName = Option::output_dir + QLatin1Char('/') + QLatin1String("Makefile"); + QString outputFileName = fileInfo(Option::output.fileName()).fileName(); + if (outputFileName != BLD_INF_FILENAME) { + wrapperFileName.append(".").append(outputFileName.startsWith(BLD_INF_FILENAME) + ? outputFileName.mid(sizeof(BLD_INF_FILENAME)) + : outputFileName); + isPrimaryMakefile = false; + } + + QFile wrapperMakefile(wrapperFileName); + if (wrapperMakefile.open(QIODevice::WriteOnly)) { + generatedFiles << wrapperFileName; + } else { + PRINT_FILE_CREATE_ERROR(wrapperFileName); + return false; + } + + if (targetType == TypeSubdirs) { + // If we have something to deploy, generate extension makefile for just that, since + // normal extension makefile is not getting generated and we need emulator deployment to be done. + if (generatePkg) + writeMkFile(wrapperFileName, true); + writeWrapperMakefile(wrapperMakefile, isPrimaryMakefile); + return true; + } + + writeMkFile(wrapperFileName, false); + + QString absoluteMmpFileName = Option::output_dir + QLatin1Char('/') + mmpFileName; + writeMmpFile(absoluteMmpFileName, symbianLocalizationList); + + if (targetType == TypeExe) { + if (!project->isActiveConfig("no_icon")) { + writeRegRssFile(userRssRules); + writeRssFile(numberOfIcons, iconFile); + writeLocFile(symbianLocalizationList); + } + } + + writeCustomDefFile(); + writeWrapperMakefile(wrapperMakefile, isPrimaryMakefile); + + return true; +} + +void SymbianMakefileGenerator::init() +{ + MakefileGenerator::init(); + SymbianCommonGenerator::init(); + + if (0 != project->values("QMAKE_PLATFORM").size()) + platform = varGlue("QMAKE_PLATFORM", "", " ", ""); + + if (0 == project->values("QMAKESPEC").size()) + project->values("QMAKESPEC").append(qgetenv("QMAKESPEC")); + + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS")); + project->values("QMAKE_LIBS_PRIVATE") += escapeFilePaths(project->values("LIBS_PRIVATE")); + + // Disallow renaming of bld.inf. + project->values("MAKEFILE").clear(); + project->values("MAKEFILE") += BLD_INF_FILENAME; + + // .mmp + mmpFileName = fixedTarget; + if (targetType == TypeExe) + mmpFileName.append("_exe"); + else if (targetType == TypeDll || targetType == TypePlugin) + mmpFileName.append("_dll"); + else if (targetType == TypeLib) + mmpFileName.append("_lib"); + mmpFileName.append(Option::mmp_ext); + + initMmpVariables(); + + uid2 = project->first("TARGET.UID2"); + + uid2 = uid2.trimmed(); +} + +QString SymbianMakefileGenerator::getTargetExtension() +{ + QString ret; + if (targetType == TypeExe) { + ret.append("exe"); + } else if (targetType == TypeLib) { + ret.append("lib"); + } else if (targetType == TypeDll || targetType == TypePlugin) { + ret.append("dll"); + } else if (targetType == TypeSubdirs) { + // Not actually usable, so return empty + } else { + // If nothing else set, default to exe + ret.append("exe"); + } + + return ret; +} + +QString SymbianMakefileGenerator::generateUID3() +{ + QString target = project->first("TARGET"); + QString currPath = qmake_getpwd(); + target.prepend("/").prepend(currPath); + return generate_test_uid(target); +} + +void SymbianMakefileGenerator::initMmpVariables() +{ + QStringList sysincspaths; + QStringList srcincpaths; + QStringList srcpaths; + + srcpaths << project->values("SOURCES") << project->values("GENERATED_SOURCES"); + srcpaths << project->values("UNUSED_SOURCES") << project->values("UI_SOURCES_DIR"); + srcpaths << project->values("UI_DIR"); + + QDir current = QDir::current(); + QString absolutizedCurrent = absolutizePath("."); + + for (int j = 0; j < srcpaths.size(); ++j) { + QFileInfo fi(fileInfo(srcpaths.at(j))); + // Sometimes sources have other than *.c* files (e.g. *.moc); prune them. + if (fi.suffix().startsWith("c")) { + if (fi.filePath().length() > fi.fileName().length()) { + appendIfnotExist(srcincpaths, fi.path()); + sources[absolutizePath(fi.path())] += fi.fileName(); + } else { + sources[absolutizedCurrent] += fi.fileName(); + appendIfnotExist(srcincpaths, absolutizedCurrent); + } + } + } + + QStringList incpaths; + incpaths << project->values("INCLUDEPATH"); + incpaths << QLibraryInfo::location(QLibraryInfo::HeadersPath); + incpaths << project->values("HEADERS"); + incpaths << srcincpaths; + incpaths << project->values("UI_HEADERS_DIR"); + incpaths << project->values("UI_DIR"); + + for (int j = 0; j < incpaths.size(); ++j) { + QString includepath = absolutizePath(incpaths.at(j)); + appendIfnotExist(sysincspaths, includepath); + appendAbldTempDirs(sysincspaths, includepath); + } + + // Remove duplicate include path entries + QStringList temporary; + for (int i = 0; i < sysincspaths.size(); ++i) { + QString origPath = sysincspaths.at(i); + QFileInfo origPathInfo(fileInfo(origPath)); + bool bFound = false; + + for (int j = 0; j < temporary.size(); ++j) { + QString tmpPath = temporary.at(j); + QFileInfo tmpPathInfo(fileInfo(tmpPath)); + + if (origPathInfo.absoluteFilePath() == tmpPathInfo.absoluteFilePath()) { + bFound = true; + if (!tmpPathInfo.isRelative() && origPathInfo.isRelative()) { + // We keep the relative notation + temporary.removeOne(tmpPath); + temporary << origPath; + } + } + } + + if (!bFound) + temporary << origPath; + + } + + sysincspaths.clear(); + sysincspaths << temporary; + + systeminclude.insert("SYSTEMINCLUDE", sysincspaths); + + // Check MMP_RULES for singleton keywords that are overridden + QStringList overridableMmpKeywords; + QStringList restrictableMmpKeywords; + QStringList restrictedMmpKeywords; + bool inResourceBlock = false; + + overridableMmpKeywords << QLatin1String(MMP_TARGETTYPE) << QLatin1String(MMP_EPOCHEAPSIZE); + restrictableMmpKeywords << QLatin1String(MMP_TARGET) << QLatin1String(MMP_SECUREID) + << QLatin1String(MMP_OPTION) << QLatin1String(MMP_LINKEROPTION) + << QLatin1String(MMP_CAPABILITY) << QLatin1String(MMP_EPOCALLOWDLLDATA) + << QLatin1String(MMP_EPOCSTACKSIZE) << QLatin1String(MMP_UID) + << QLatin1String(MMP_VENDORID) << QLatin1String(MMP_VERSION); + + foreach (QString item, project->values("MMP_RULES")) { + if (project->values(item).isEmpty()) { + handleMmpRulesOverrides(item, inResourceBlock, restrictedMmpKeywords, + restrictableMmpKeywords, overridableMmpKeywords); + } else { + foreach (QString itemRow, project->values(item)) { + handleMmpRulesOverrides(itemRow, inResourceBlock, restrictedMmpKeywords, + restrictableMmpKeywords, overridableMmpKeywords); + } + } + } + + if (restrictedMmpKeywords.size()) { + fprintf(stderr, "Warning: Restricted statements detected in MMP_RULES:\n" + " (%s)\n" + " Use corresponding qmake variable(s) instead.\n", + qPrintable(restrictedMmpKeywords.join(", "))); + } +} + +void SymbianMakefileGenerator::handleMmpRulesOverrides(QString &checkString, + bool &inResourceBlock, + QStringList &restrictedMmpKeywords, + const QStringList &restrictableMmpKeywords, + const QStringList &overridableMmpKeywords) +{ + QString simplifiedString = checkString.simplified(); + + if (!inResourceBlock && simplifiedString.startsWith(MMP_START_RESOURCE, Qt::CaseInsensitive)) + inResourceBlock = true; + else if (inResourceBlock && simplifiedString.startsWith(MMP_END_RESOURCE, Qt::CaseInsensitive)) + inResourceBlock = false; + + // Allow restricted and overridable items in RESOURCE blocks as those do not actually + // override anything. + if (!inResourceBlock) { + appendKeywordIfMatchFound(overriddenMmpKeywords, overridableMmpKeywords, simplifiedString); + appendKeywordIfMatchFound(restrictedMmpKeywords, restrictableMmpKeywords, simplifiedString); + } +} + +void SymbianMakefileGenerator::appendKeywordIfMatchFound(QStringList &list, + const QStringList &keywordList, + QString &checkString) +{ + // Check if checkString starts with any supplied keyword and + // add the found keyword to list if it does. + foreach (QString item, keywordList) { + if (checkString.startsWith(QString(item).append(" "), Qt::CaseInsensitive) + || checkString.compare(item, Qt::CaseInsensitive) == 0) { + appendIfnotExist(list, item); + } + } +} + + +bool SymbianMakefileGenerator::removeDuplicatedStrings(QStringList &stringList) +{ + QStringList tmpStringList; + + for (int i = 0; i < stringList.size(); ++i) { + QString string = stringList.at(i); + if (tmpStringList.contains(string)) + continue; + else + tmpStringList.append(string); + } + + stringList.clear(); + stringList = tmpStringList; + return true; +} + +void SymbianMakefileGenerator::writeMmpFileHeader(QTextStream &t) +{ + t << "// ==============================================================================" << endl; + t << "// Generated by qmake (" << qmake_version() << ") (Qt " QT_VERSION_STR ") on: "; + t << QDateTime::currentDateTime().toString(Qt::ISODate) << endl; + t << "// This file is generated by qmake and should not be modified by the" << endl; + t << "// user." << endl; + t << "// Name : " << mmpFileName << endl; + t << "// ==============================================================================" << endl << endl; +} + +void SymbianMakefileGenerator::writeMmpFile(QString &filename, const SymbianLocalizationList &symbianLocalizationList) +{ + QFile ft(filename); + if (ft.open(QIODevice::WriteOnly)) { + generatedFiles << ft.fileName(); + + QTextStream t(&ft); + + writeMmpFileHeader(t); + + writeMmpFileTargetPart(t); + + writeMmpFileResourcePart(t, symbianLocalizationList); + + writeMmpFileMacrosPart(t); + + writeMmpFileIncludePart(t); + + QDir current = QDir::current(); + + for (QMap::iterator it = sources.begin(); it != sources.end(); ++it) { + QStringList values = it.value(); + QString currentSourcePath = it.key(); + + if (values.size()) + t << "SOURCEPATH \t" << fixPathForMmp(currentSourcePath, current) << endl; + + for (int i = 0; i < values.size(); ++i) { + QString sourceFileName = values.at(i); + t << "SOURCE\t\t" << sourceFileName << endl; + } + t << endl; + } + t << endl; + + if (!project->isActiveConfig("static") && !project->isActiveConfig("staticlib")) { + writeMmpFileLibraryPart(t); + } + + writeMmpFileCapabilityPart(t); + + writeMmpFileCompilerOptionPart(t); + + writeMmpFileBinaryVersionPart(t); + + writeMmpFileRulesPart(t); + } else { + PRINT_FILE_CREATE_ERROR(filename) + } +} + +void SymbianMakefileGenerator::writeMmpFileMacrosPart(QTextStream& t) +{ + t << endl; + + QStringList &defines = project->values("DEFINES"); + if (defines.size()) + t << "// Qt Macros" << endl; + for (int i = 0; i < defines.size(); ++i) { + QString def = defines.at(i); + addMacro(t, def); + } + + // These are required in order that all methods will be correctly exported e.g from qtestlib + QStringList &exp_defines = project->values("PRL_EXPORT_DEFINES"); + if (exp_defines.size()) + t << endl << "// Qt Export Defines" << endl; + for (int i = 0; i < exp_defines.size(); ++i) { + QString def = exp_defines.at(i); + addMacro(t, def); + } + + t << endl; +} + +void SymbianMakefileGenerator::addMacro(QTextStream& t, const QString& value) +{ + t << "MACRO\t\t" << value << endl; +} + + +void SymbianMakefileGenerator::writeMmpFileTargetPart(QTextStream& t) +{ + bool skipTargetType = overriddenMmpKeywords.contains(MMP_TARGETTYPE); + bool skipEpocHeapSize = overriddenMmpKeywords.contains(MMP_EPOCHEAPSIZE); + + if (targetType == TypeExe) { + t << MMP_TARGET "\t\t" << fixedTarget << ".exe" << endl; + if (!skipTargetType) { + if (project->isActiveConfig("stdbinary")) + t << MMP_TARGETTYPE "\t\tSTDEXE" << endl; + else + t << MMP_TARGETTYPE "\t\tEXE" << endl; + } + } else if (targetType == TypeDll || targetType == TypePlugin) { + t << MMP_TARGET "\t\t" << fixedTarget << ".dll" << endl; + if (!skipTargetType) { + if (project->isActiveConfig("stdbinary")) + t << MMP_TARGETTYPE "\t\tSTDDLL" << endl; + else + t << MMP_TARGETTYPE "\t\tDLL" << endl; + } + } else if (targetType == TypeLib) { + t << MMP_TARGET "\t\t" << fixedTarget << ".lib" << endl; + if (!skipTargetType) { + if (project->isActiveConfig("stdbinary")) + t << MMP_TARGETTYPE "\t\tSTDLIB" << endl; + else + t << MMP_TARGETTYPE "\t\tLIB" << endl; + } + } else { + fprintf(stderr, "Error: Unexpected targettype (%d) in SymbianMakefileGenerator::writeMmpFileTargetPart\n", targetType); + } + + t << endl; + + t << MMP_UID "\t\t" << uid2 << " " << uid3 << endl; + + if (0 != project->values("TARGET.SID").size()) { + t << MMP_SECUREID "\t\t" << project->values("TARGET.SID").join(" ") << endl; + } else { + if (0 == uid3.size()) + t << MMP_SECUREID "\t\t0" << endl; + else + t << MMP_SECUREID "\t\t" << uid3 << endl; + } + + // default value used from mkspecs is 0 + if (0 != project->values("TARGET.VID").size()) { + t << MMP_VENDORID "\t\t" << project->values("TARGET.VID").join(" ") << endl; + } + + t << endl; + + if (0 != project->first("TARGET.EPOCSTACKSIZE").size()) + t << MMP_EPOCSTACKSIZE "\t\t" << project->first("TARGET.EPOCSTACKSIZE") << endl; + if (!skipEpocHeapSize && 0 != project->values("TARGET.EPOCHEAPSIZE").size()) + t << MMP_EPOCHEAPSIZE "\t\t" << project->values("TARGET.EPOCHEAPSIZE").join(" ") << endl; + if (0 != project->values("TARGET.EPOCALLOWDLLDATA").size()) + t << MMP_EPOCALLOWDLLDATA << endl; + + if (targetType == TypePlugin && !project->isActiveConfig("stdbinary")) { + // Use custom def file for Qt plugins + t << "DEFFILE " PLUGIN_COMMON_DEF_FILE_FOR_MMP << endl; + } + + t << endl; +} + + +/* + Application registration resource files should be installed to the + \private\10003a3f\import\apps directory. +*/ +void SymbianMakefileGenerator::writeMmpFileResourcePart(QTextStream& t, const SymbianLocalizationList &symbianLocalizationList) +{ + if ((targetType == TypeExe) && + !project->isActiveConfig("no_icon")) { + + QString locTarget = fixedTarget; + locTarget.append(".rss"); + + t << "SOURCEPATH\t\t\t. " << endl; + t << "LANG SC "; // no endl + SymbianLocalizationListIterator iter(symbianLocalizationList); + while (iter.hasNext()) { + const SymbianLocalization &loc = iter.next(); + t << loc.symbianLanguageCode << " "; // no endl + } + t << endl; + t << MMP_START_RESOURCE "\t\t" << locTarget << endl; + t << "HEADER" << endl; + t << "TARGETPATH\t\t\t" RESOURCE_DIRECTORY_MMP << endl; + t << MMP_END_RESOURCE << endl << endl; + + QString regTarget = fixedTarget; + regTarget.append("_reg.rss"); + + t << "SOURCEPATH\t\t\t." << endl; + t << MMP_START_RESOURCE "\t\t" << regTarget << endl; + if (isForSymbianSbsv2()) + t << "DEPENDS " << fixedTarget << ".rsg" << endl; + t << "TARGETPATH\t\t" REGISTRATION_RESOURCE_DIRECTORY_HW << endl; + t << MMP_END_RESOURCE << endl << endl; + } +} + +void SymbianMakefileGenerator::writeMmpFileSystemIncludePart(QTextStream& t) +{ + QDir current = QDir::current(); + + for (QMap::iterator it = systeminclude.begin(); it != systeminclude.end(); ++it) { + QStringList values = it.value(); + for (int i = 0; i < values.size(); ++i) { + QString handledPath = values.at(i); + t << "SYSTEMINCLUDE\t\t" << fixPathForMmp(handledPath, current) << endl; + } + } + + t << endl; +} + +void SymbianMakefileGenerator::writeMmpFileIncludePart(QTextStream& t) +{ + writeMmpFileSystemIncludePart(t); +} + +void SymbianMakefileGenerator::writeMmpFileLibraryPart(QTextStream& t) +{ + QStringList &libs = project->values("LIBS"); + libs << project->values("QMAKE_LIBS") << project->values("QMAKE_LIBS_PRIVATE"); + + removeDuplicatedStrings(libs); + + for (int i = 0; i < libs.size(); ++i) { + QString lib = libs.at(i); + // The -L flag is uninteresting, since all symbian libraries exist in the same directory. + if (lib.startsWith("-l")) { + lib.remove(0, 2); + QString mmpStatement; + if (lib.endsWith(".lib")) { + lib.chop(4); + mmpStatement = "STATICLIBRARY\t"; + } else { + if (lib.endsWith(".dll")) + lib.chop(4); + mmpStatement = "LIBRARY\t\t"; + } + t << mmpStatement << lib << ".lib" << endl; + } + } + + t << endl; +} + +void SymbianMakefileGenerator::writeMmpFileCapabilityPart(QTextStream& t) +{ + if (0 != project->first("TARGET.CAPABILITY").size()) { + QStringList &capabilities = project->values("TARGET.CAPABILITY"); + t << MMP_CAPABILITY "\t\t"; + + for (int i = 0; i < capabilities.size(); ++i) { + QString cap = capabilities.at(i); + t << cap << " "; + } + } else { + t << MMP_CAPABILITY "\t\tNone"; + } + t << endl << endl; +} + +void SymbianMakefileGenerator::writeMmpFileConditionalOptions(QTextStream& t, + const QString &optionType, + const QString &optionTag, + const QString &variableBase) +{ + foreach(QString compilerVersion, project->values("VERSION_FLAGS." + optionTag)) { + QStringList currentValues = project->values(variableBase + "." + compilerVersion); + if (currentValues.size()) { + t << "#if defined(" << compilerVersion << ")" << endl; + t << optionType << " " << optionTag << " " << currentValues.join(" ") << endl; + t << "#endif" << endl; + } + } +} + +void SymbianMakefileGenerator::writeMmpFileSimpleOption(QTextStream& t, + const QString &optionType, + const QString &optionTag, + const QString &options) +{ + QString trimmedOptions = options.trimmed(); + if (!trimmedOptions.isEmpty()) + t << optionType << " " << optionTag << " " << trimmedOptions << endl; +} + +void SymbianMakefileGenerator::appendMmpFileOptions(QString &options, const QStringList &list) +{ + if (list.size()) { + options.append(list.join(" ")); + options.append(" "); + } +} + +void SymbianMakefileGenerator::writeMmpFileCompilerOptionPart(QTextStream& t) +{ + QStringList keywords = project->values("MMP_OPTION_KEYWORDS"); + QStringList commonCxxFlags = project->values(VAR_CXXFLAGS); + QStringList commonCFlags = project->values(VAR_CFLAGS); + QStringList commonLFlags = project->values(VAR_LFLAGS); + + foreach(QString item, keywords) { + QString compilerOption; + QString linkerOption; + + appendMmpFileOptions(compilerOption, project->values(VAR_CXXFLAGS "." + item)); + appendMmpFileOptions(compilerOption, project->values(VAR_CFLAGS "." + item)); + appendMmpFileOptions(compilerOption, commonCxxFlags); + appendMmpFileOptions(compilerOption, commonCFlags); + + appendMmpFileOptions(linkerOption, project->values(VAR_LFLAGS "." + item)); + appendMmpFileOptions(linkerOption, commonLFlags); + + writeMmpFileSimpleOption(t, MMP_OPTION, item, compilerOption); + writeMmpFileSimpleOption(t, MMP_LINKEROPTION, item, linkerOption); + + writeMmpFileConditionalOptions(t, MMP_OPTION, item, VAR_CXXFLAGS); + writeMmpFileConditionalOptions(t, MMP_LINKEROPTION, item, VAR_LFLAGS); + } + + t << endl; +} + +void SymbianMakefileGenerator::writeMmpFileBinaryVersionPart(QTextStream& t) +{ + QString applicationVersion = project->first("VERSION"); + QStringList verNumList = applicationVersion.split('.'); + uint major = 0; + uint minor = 0; + uint patch = 0; + bool success = false; + + if (verNumList.size() > 0) { + major = verNumList[0].toUInt(&success); + if (success && verNumList.size() > 1) { + minor = verNumList[1].toUInt(&success); + if (success && verNumList.size() > 2) { + patch = verNumList[2].toUInt(&success); + } + } + } + + QString mmpVersion; + if (success && major <= 0xFFFF && minor <= 0xFF && patch <= 0xFF) { + // Symbian binary version only has major and minor components, so compress + // Qt's minor and patch values into the minor component. Since Symbian's minor + // component is a 16 bit value, only allow 8 bits for each to avoid overflow. + mmpVersion.append(QString::number(major)) + .append('.') + .append(QString::number((minor << 8) + patch)); + } else { + if (!applicationVersion.isEmpty()) + fprintf(stderr, "Invalid VERSION string: %s\n", qPrintable(applicationVersion)); + mmpVersion = "10.0"; // Default binary version for symbian is 10.0 + } + + t << MMP_VERSION " " << mmpVersion << endl; +} + +void SymbianMakefileGenerator::writeMmpFileRulesPart(QTextStream& t) +{ + foreach(QString item, project->values("MMP_RULES")) { + t << endl; + // If there is no stringlist defined for a rule, use rule name directly + // This is convenience for defining single line mmp statements + if (project->values(item).isEmpty()) { + t << item << endl; + } else { + foreach(QString itemRow, project->values(item)) { + t << itemRow << endl; + } + } + } +} + +void SymbianMakefileGenerator::writeBldInfContent(QTextStream &t, bool addDeploymentExtension, const QString &iconFile) +{ + // Read user defined bld inf rules + + QMap userBldInfRules; + for (QMap::iterator it = project->variables().begin(); it != project->variables().end(); ++it) { + if (it.key().startsWith(BLD_INF_RULES_BASE)) { + QString newKey = it.key().mid(sizeof(BLD_INF_RULES_BASE) - 1); + if (newKey.isEmpty()) { + fprintf(stderr, "Warning: Empty BLD_INF_RULES key encountered\n"); + continue; + } + QStringList newValues; + QStringList values = it.value(); + foreach(QString item, values) { + // If there is no stringlist defined for a rule, use rule name directly + // This is convenience for defining single line statements + if (project->values(item).isEmpty()) { + newValues << item; + } else { + foreach(QString itemRow, project->values(item)) { + newValues << itemRow; + } + } + } + userBldInfRules.insert(newKey, newValues); + } + } + + // Add includes of subdirs bld.inf files + + QString currentPath = qmake_getpwd(); + QDir directory(currentPath); + + const QStringList &subdirs = project->values("SUBDIRS"); + foreach(QString item, subdirs) { + bool fromFile = false; + QString fixedItem; + if (!project->isEmpty(item + ".file")) { + fixedItem = project->first(item + ".file"); + fromFile = true; + } else if (!project->isEmpty(item + ".subdir")) { + fixedItem = project->first(item + ".subdir"); + fromFile = false; + } else { + fixedItem = item; + fromFile = item.endsWith(Option::pro_ext); + } + + QString condition; + if (!project->isEmpty(item + ".condition")) + condition = project->first(item + ".condition"); + + QFileInfo subdir(fileInfo(fixedItem)); + QString relativePath = directory.relativeFilePath(fixedItem); + QString fullProName = subdir.absoluteFilePath(); + QString bldinfFilename; + QString subdirFileName; + + if (fromFile) { + subdirFileName = subdir.completeBaseName(); + } else { + subdirFileName = subdir.fileName(); + } + + if (subdir.isDir()) { + // Subdir is a regular project + bldinfFilename = relativePath + QString("/") + QString(BLD_INF_FILENAME); + fullProName += QString("/") + subdirFileName + Option::pro_ext; + } else { + // Subdir is actually a .pro file + if (relativePath.contains("/")) { + // .pro not in same directory as parent .pro + relativePath.remove(relativePath.lastIndexOf("/") + 1, relativePath.length()); + bldinfFilename = relativePath; + } else { + // .pro and parent .pro in same directory + bldinfFilename = QString("./"); + } + bldinfFilename += QString(BLD_INF_FILENAME ".") + subdirFileName; + } + + QString uid = generate_uid(fullProName); + QString bldinfDefine = QString("BLD_INF_") + subdirFileName + QString("_") + uid; + bldinfDefine = bldinfDefine.toUpper(); + + // replace anything not alphanumeric with underscore + QRegExp replacementMask(DEFINE_REPLACE_REGEXP); + bldinfDefine.replace(replacementMask, QLatin1String("_")); + + if (!condition.isEmpty()) + t << "#if defined(" << condition << ")" << endl; + + t << "#ifndef " << bldinfDefine << endl; + t << "\t#include \"" << bldinfFilename << "\"" << endl; + t << "#endif" << endl; + + if (!condition.isEmpty()) + t << "#endif" << endl; + + } + + // Add supported project platforms + + t << endl << BLD_INF_TAG_PLATFORMS << endl << endl; + if (0 != project->values("SYMBIAN_PLATFORMS").size()) + t << project->values("SYMBIAN_PLATFORMS").join(" ") << endl; + + QStringList userItems = userBldInfRules.value(BLD_INF_TAG_PLATFORMS); + foreach(QString item, userItems) + t << item << endl; + userBldInfRules.remove(BLD_INF_TAG_PLATFORMS); + t << endl; + + // Add project mmps and old style extension makefiles + + QString mmpTag; + if (project->isActiveConfig(SYMBIAN_TEST_CONFIG)) + mmpTag = QLatin1String(BLD_INF_TAG_TESTMMPFILES); + else + mmpTag = QLatin1String(BLD_INF_TAG_MMPFILES); + + t << endl << mmpTag << endl << endl; + + writeBldInfMkFilePart(t, addDeploymentExtension); + if (targetType != TypeSubdirs) + t << mmpFileName << endl; + + userItems = userBldInfRules.value(mmpTag); + foreach(QString item, userItems) + t << item << endl; + userBldInfRules.remove(mmpTag); + + QString extensionTag; + if (project->isActiveConfig(SYMBIAN_TEST_CONFIG)) + extensionTag = QLatin1String(BLD_INF_TAG_TESTEXTENSIONS); + else + extensionTag = QLatin1String(BLD_INF_TAG_EXTENSIONS); + + t << endl << extensionTag << endl << endl; + + // Generate extension rules + + writeBldInfExtensionRulesPart(t, iconFile); + + userItems = userBldInfRules.value(extensionTag); + foreach(QString item, userItems) + t << item << endl; + userBldInfRules.remove(extensionTag); + + // Add rest of the user defined content + + for (QMap::iterator it = userBldInfRules.begin(); it != userBldInfRules.end(); ++it) { + t << endl << endl << it.key() << endl << endl; + userItems = it.value(); + foreach(QString item, userItems) + t << item << endl; + } +} + +void SymbianMakefileGenerator::appendIfnotExist(QStringList &list, QString value) +{ + if (!list.contains(value)) + list += value; +} + +void SymbianMakefileGenerator::appendIfnotExist(QStringList &list, QStringList values) +{ + foreach(QString item, values) + appendIfnotExist(list, item); +} + + +QString SymbianMakefileGenerator::removeTrailingPathSeparators(QString &file) +{ + QString ret = file; + if (ret.endsWith(QDir::separator())) { + ret.remove(ret.length() - 1, 1); + } + + return ret; +} + +void SymbianMakefileGenerator::generateCleanCommands(QTextStream& t, + const QStringList& toClean, + const QString& cmd, + const QString& cmdOptions, + const QString& itemPrefix, + const QString& itemSuffix) +{ + for (int i = 0; i < toClean.size(); ++i) { + QString item = toClean.at(i); + item.prepend(itemPrefix).append(itemSuffix); +#if defined(Q_OS_WIN) + t << "\t-@ if EXIST \"" << QDir::toNativeSeparators(item) << "\" "; + t << cmd << " " << cmdOptions << " \"" << QDir::toNativeSeparators(item) << "\"" << endl; +#else + t << "\t-if test -e " << QDir::toNativeSeparators(item) << "; then "; + t << cmd << " " << cmdOptions << " " << QDir::toNativeSeparators(item) << "; fi" << endl; +#endif + } +} + +void SymbianMakefileGenerator::generateDistcleanTargets(QTextStream& t) +{ + t << "dodistclean:" << endl; + const QStringList &subdirs = project->values("SUBDIRS"); + foreach(QString item, subdirs) { + bool fromFile = false; + QString fixedItem; + if (!project->isEmpty(item + ".file")) { + fixedItem = project->first(item + ".file"); + fromFile = true; + } else if (!project->isEmpty(item + ".subdir")) { + fixedItem = project->first(item + ".subdir"); + fromFile = false; + } else { + fromFile = item.endsWith(Option::pro_ext); + fixedItem = item; + } + QFileInfo fi(fileInfo(fixedItem)); + if (!fromFile) { + t << "\t-$(MAKE) -f \"" << Option::fixPathToTargetOS(fi.absoluteFilePath() + "/Makefile") << "\" dodistclean" << endl; + } else { + QString itemName = fi.fileName(); + int extIndex = itemName.lastIndexOf(Option::pro_ext); + if (extIndex) + fixedItem = fi.absolutePath() + "/" + QString("Makefile.") + itemName.mid(0, extIndex); + t << "\t-$(MAKE) -f \"" << Option::fixPathToTargetOS(fixedItem) << "\" dodistclean" << endl; + } + + } + + generatedFiles << Option::fixPathToTargetOS(fileInfo(Option::output.fileName()).absoluteFilePath()); // bld.inf + generatedFiles << project->values("QMAKE_INTERNAL_PRL_FILE"); // Add generated prl files for cleanup + generatedFiles << project->values("QMAKE_DISTCLEAN"); // Add any additional files marked for distclean + QStringList fixedFiles; + QStringList fixedDirs; + foreach(QString item, generatedFiles) { + QString fixedItem = Option::fixPathToTargetOS(fileInfo(item).absoluteFilePath()); + if (!fixedFiles.contains(fixedItem)) { + fixedFiles << fixedItem; + } + } + foreach(QString item, generatedDirs) { + QString fixedItem = Option::fixPathToTargetOS(fileInfo(item).absoluteFilePath()); + if (!fixedDirs.contains(fixedItem)) { + fixedDirs << fixedItem; + } + } + generateCleanCommands(t, fixedFiles, "$(DEL_FILE)", "", "", ""); + generateCleanCommands(t, fixedDirs, "$(DEL_DIR)", "", "", ""); + t << endl; + + t << "distclean: clean dodistclean" << endl; + t << endl; +} + +// Returns a string that can be used as a dependency to loc file on other targets +QString SymbianMakefileGenerator::generateLocFileTarget(QTextStream& t, const QString& locCmd) +{ + QString locFile; + if (targetType == TypeExe && !project->isActiveConfig("no_icon")) { + locFile = Option::fixPathToLocalOS(generateLocFileName()); + t << locFile << QLatin1String(": ") << project->values("SYMBIAN_MATCHED_TRANSLATIONS").join(" ") << endl; + t << locCmd << endl; + t << endl; + locFile += QLatin1Char(' '); + } + + return locFile; +} diff --git a/qmake/generators/symbian/symmake.h b/qmake/generators/symbian/symmake.h new file mode 100644 index 0000000000..a38fbe54ed --- /dev/null +++ b/qmake/generators/symbian/symmake.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SYMMAKEFILE_H +#define SYMMAKEFILE_H + +#include "initprojectdeploy_symbian.h" +#include "symbiancommon.h" +#include + +QT_BEGIN_NAMESPACE + +#define BLD_INF_FILENAME "bld.inf" +#define MAKEFILE_DEPENDENCY_SEPARATOR " \\\n\t" +#define QT_EXTRA_INCLUDE_DIR "tmp" +#define MAKE_CACHE_NAME ".make.cache" +#define SYMBIAN_TEST_CONFIG "symbian_test" + +class SymbianMakefileGenerator : public MakefileGenerator, public SymbianCommonGenerator +{ +protected: + QString platform; + QString uid2; + QString mmpFileName; + QMap sources; + QMap systeminclude; + QMap library; + // (output file) (source , command) + QMap makmakeCommands; + QStringList overriddenMmpKeywords; + + QString fixPathForMmp(const QString& origPath, const QDir& parentDir); + QString absolutizePath(const QString& origPath); + + virtual bool writeMakefile(QTextStream &t); + + virtual void init(); + + QString getTargetExtension(); + + QString generateUID3(); + + void initMmpVariables(); + void generateMmpFileName(); + void handleMmpRulesOverrides(QString &checkString, + bool &inResourceBlock, + QStringList &restrictedMmpKeywords, + const QStringList &restrictableMmpKeywords, + const QStringList &overridableMmpKeywords); + void appendKeywordIfMatchFound(QStringList &list, + const QStringList &keywordList, + QString &checkString); + + void writeHeader(QTextStream &t); + void writeBldInfContent(QTextStream& t, + bool addDeploymentExtension, + const QString &iconFile); + + static bool removeDuplicatedStrings(QStringList& stringList); + + void writeMmpFileHeader(QTextStream &t); + void writeMmpFile(QString &filename, const SymbianLocalizationList &symbianLocalizationList); + void writeMmpFileMacrosPart(QTextStream& t); + void addMacro(QTextStream& t, const QString& value); + void writeMmpFileTargetPart(QTextStream& t); + void writeMmpFileResourcePart(QTextStream& t, const SymbianLocalizationList &symbianLocalizationList); + void writeMmpFileSystemIncludePart(QTextStream& t); + void writeMmpFileIncludePart(QTextStream& t); + void writeMmpFileLibraryPart(QTextStream& t); + void writeMmpFileCapabilityPart(QTextStream& t); + void writeMmpFileConditionalOptions(QTextStream& t, + const QString &optionType, + const QString &optionTag, + const QString &variableBase); + void writeMmpFileSimpleOption(QTextStream& t, + const QString &optionType, + const QString &optionTag, + const QString &options); + void appendMmpFileOptions(QString &options, const QStringList &list); + void writeMmpFileCompilerOptionPart(QTextStream& t); + void writeMmpFileBinaryVersionPart(QTextStream& t); + void writeMmpFileRulesPart(QTextStream& t); + + void appendIfnotExist(QStringList &list, QString value); + void appendIfnotExist(QStringList &list, QStringList values); + + QString removeTrailingPathSeparators(QString &file); + void generateCleanCommands(QTextStream& t, + const QStringList& toClean, + const QString& cmd, + const QString& cmdOptions, + const QString& itemPrefix, + const QString& itemSuffix); + + void generateDistcleanTargets(QTextStream& t); + QString generateLocFileTarget(QTextStream& t, const QString& locCmd); + + // Subclass implements + virtual void writeBldInfExtensionRulesPart(QTextStream& t, const QString &iconTargetFile) = 0; + virtual void writeBldInfMkFilePart(QTextStream& t, bool addDeploymentExtension) = 0; + virtual void writeMkFile(const QString& wrapperFileName, bool deploymentOnly) = 0; + virtual void writeWrapperMakefile(QFile& wrapperFile, bool isPrimaryMakefile) = 0; + virtual void appendAbldTempDirs(QStringList& sysincspaths, QString includepath) = 0; + +public: + + SymbianMakefileGenerator(); + ~SymbianMakefileGenerator(); +}; + +#endif // SYMMAKEFILE_H diff --git a/qmake/generators/symbian/symmake_abld.cpp b/qmake/generators/symbian/symmake_abld.cpp new file mode 100644 index 0000000000..b582257519 --- /dev/null +++ b/qmake/generators/symbian/symmake_abld.cpp @@ -0,0 +1,523 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "symmake_abld.h" +#include "initprojectdeploy_symbian.h" + +#include +#include +#include +#include +#include + +// Included from tools/shared +#include + +#define DO_NOTHING_TARGET "do_nothing" +#define CREATE_TEMPS_TARGET "create_temps" +#define EXTENSION_CLEAN "extension_clean" +#define PRE_TARGETDEPS_TARGET "pre_targetdeps" +#define COMPILER_CLEAN_TARGET "compiler_clean" +#define FINALIZE_TARGET "finalize" +#define GENERATED_SOURCES_TARGET "generated_sources" +#define ALL_SOURCE_DEPS_TARGET "all_source_deps" +#define DEPLOYMENT_TARGET "deployment" +#define DEPLOYMENT_CLEAN_TARGET "deployment_clean" +#define WINSCW_DEPLOYMENT_TARGET "winscw_deployment" +#define WINSCW_DEPLOYMENT_CLEAN_TARGET "winscw_deployment_clean" +#define STORE_BUILD_TARGET "store_build" + +SymbianAbldMakefileGenerator::SymbianAbldMakefileGenerator() : SymbianMakefileGenerator() { } +SymbianAbldMakefileGenerator::~SymbianAbldMakefileGenerator() { } + +void SymbianAbldMakefileGenerator::writeMkFile(const QString& wrapperFileName, bool deploymentOnly) +{ + QFile ft(gnuMakefileName); + if (ft.open(QIODevice::WriteOnly)) { + generatedFiles << ft.fileName(); + QTextStream t(&ft); + + t << "# ==============================================================================" << endl; + t << "# Generated by qmake (" << qmake_version() << ") (Qt " << QT_VERSION_STR << ") on: "; + t << QDateTime::currentDateTime().toString() << endl; + t << "# This file is generated by qmake and should not be modified by the" << endl; + t << "# user." << endl; + t << "# Name : " << gnuMakefileName << endl; + t << "# Part of : " << project->values("TARGET").join(" ") << endl; + t << "# Description : This file is used to call necessary targets on wrapper makefile" << endl; + t << "# during normal Symbian build process." << endl; + t << "# Version : " << endl; + t << "#" << endl; + t << "# ==============================================================================" << "\n" << endl; + + t << endl << endl; + + t << "MAKE = make" << endl; + t << endl; + + t << "VISUAL_CFG = RELEASE" << endl; + t << "ifeq \"$(CFG)\" \"UDEB\"" << endl; + t << "VISUAL_CFG = DEBUG" << endl; + t << "endif" << endl; + t << endl; + + t << DO_NOTHING_TARGET " :" << endl; + t << "\t" << "@rem " DO_NOTHING_TARGET << endl << endl; + + QString buildDeps; + QString cleanDeps; + QString finalDeps; + QString cleanDepsWinscw; + QString finalDepsWinscw; + QStringList wrapperTargets; + if (deploymentOnly) { + buildDeps.append(STORE_BUILD_TARGET); + cleanDeps.append(DEPLOYMENT_CLEAN_TARGET); + cleanDepsWinscw.append(WINSCW_DEPLOYMENT_CLEAN_TARGET " " DEPLOYMENT_CLEAN_TARGET); + finalDeps.append(DEPLOYMENT_TARGET); + finalDepsWinscw.append(WINSCW_DEPLOYMENT_TARGET " " DEPLOYMENT_TARGET); + wrapperTargets << WINSCW_DEPLOYMENT_TARGET + << WINSCW_DEPLOYMENT_CLEAN_TARGET + << DEPLOYMENT_TARGET + << DEPLOYMENT_CLEAN_TARGET + << STORE_BUILD_TARGET; + } else { + buildDeps.append(CREATE_TEMPS_TARGET " " PRE_TARGETDEPS_TARGET " " STORE_BUILD_TARGET); + cleanDeps.append(EXTENSION_CLEAN " " DEPLOYMENT_CLEAN_TARGET); + cleanDepsWinscw.append(EXTENSION_CLEAN " " WINSCW_DEPLOYMENT_CLEAN_TARGET " " DEPLOYMENT_CLEAN_TARGET); + finalDeps.append(FINALIZE_TARGET " " DEPLOYMENT_TARGET); + finalDepsWinscw.append(FINALIZE_TARGET " " WINSCW_DEPLOYMENT_TARGET " " DEPLOYMENT_TARGET); + wrapperTargets << PRE_TARGETDEPS_TARGET + << CREATE_TEMPS_TARGET + << EXTENSION_CLEAN + << FINALIZE_TARGET + << WINSCW_DEPLOYMENT_CLEAN_TARGET + << WINSCW_DEPLOYMENT_TARGET + << DEPLOYMENT_CLEAN_TARGET + << DEPLOYMENT_TARGET + << STORE_BUILD_TARGET; + } + + t << "MAKMAKE: " << buildDeps << endl << endl; + t << "LIB: " << buildDeps << endl << endl; + t << "BLD: " << buildDeps << endl << endl; + t << "ifeq \"$(PLATFORM)\" \"WINSCW\"" << endl; + t << "CLEAN: " << cleanDepsWinscw << endl; + t << "else" << endl; + t << "CLEAN: " << cleanDeps << endl; + t << "endif" << endl << endl; + t << "CLEANLIB: " DO_NOTHING_TARGET << endl << endl; + t << "RESOURCE: " DO_NOTHING_TARGET << endl << endl; + t << "FREEZE: " DO_NOTHING_TARGET << endl << endl; + t << "SAVESPACE: " DO_NOTHING_TARGET << endl << endl; + t << "RELEASABLES: " DO_NOTHING_TARGET << endl << endl; + t << "ifeq \"$(PLATFORM)\" \"WINSCW\"" << endl; + t << "FINAL: " << finalDepsWinscw << endl; + t << "else" << endl; + t << "FINAL: " << finalDeps << endl; + t << "endif" << endl << endl; + + QString makefile(Option::fixPathToTargetOS(fileInfo(wrapperFileName).canonicalFilePath())); + foreach(QString target, wrapperTargets) { + t << target << " : " << makefile << endl; + t << "\t-$(MAKE) -f \"" << makefile << "\" " << target << " QT_SIS_TARGET=$(VISUAL_CFG)-$(PLATFORM)" << endl << endl; + } + + t << endl; + } // if(ft.open(QIODevice::WriteOnly)) +} + +void SymbianAbldMakefileGenerator::writeWrapperMakefile(QFile& wrapperFile, bool isPrimaryMakefile) +{ + QStringList allPlatforms; + foreach(QString platform, project->values("SYMBIAN_PLATFORMS")) { + allPlatforms << platform.toLower(); + } + + QStringList debugPlatforms = allPlatforms; + QStringList releasePlatforms = allPlatforms; + releasePlatforms.removeAll("winscw"); // No release for emulator + + QString testClause; + if (project->isActiveConfig(SYMBIAN_TEST_CONFIG)) + testClause = QLatin1String(" test"); + else + testClause = QLatin1String(""); + + QTextStream t(&wrapperFile); + + MakefileGenerator::writeHeader(t); + + t << "MAKEFILE = " << fileInfo(wrapperFile.fileName()).fileName() << endl; + t << "QMAKE = " << var("QMAKE_QMAKE") << endl; + t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl; + t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl; + t << "DEL_TREE = " << var("QMAKE_DEL_TREE") << endl; + t << "MOVE = " << var("QMAKE_MOVE") << endl; + t << "CHK_DIR_EXISTS = " << var("QMAKE_CHK_DIR_EXISTS") << endl; + t << "MKDIR = " << var("QMAKE_MKDIR") << endl; +#ifdef Q_OS_WIN32 + t << "XCOPY = xcopy /d /f /h /r /y /i" << endl; + t << "ABLD = ABLD.BAT" << endl; +#elif defined(Q_OS_MAC) + t << "XCOPY = cp -R -v" << endl; + t << "ABLD = abld" << endl; +#else + t << "XCOPY = cp -R -u -v" << endl; + t << "ABLD = abld" << endl; +#endif + t << "DEBUG_PLATFORMS = " << debugPlatforms.join(" ") << endl; + t << "RELEASE_PLATFORMS = " << releasePlatforms.join(" ") << endl; + t << "MAKE = make" << endl; + t << endl; + t << "ifeq (WINS,$(findstring WINS, $(PLATFORM)))" << endl; + t << "ZDIR=$(EPOCROOT)" << QDir::toNativeSeparators("epoc32/release/$(PLATFORM)/$(CFG)/z") << endl; + t << "else" << endl; + t << "ZDIR=$(EPOCROOT)" << QDir::toNativeSeparators("epoc32/data/z") << endl; + t << "endif" << endl; + t << endl; + t << "DEFINES" << '\t' << " = " + << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ") + << varGlue("QMAKE_COMPILER_DEFINES", "-D", "-D", " ") + << varGlue("DEFINES","-D"," -D","") << endl; + + t << "INCPATH" << '\t' << " = "; + + for (QMap::iterator it = systeminclude.begin(); it != systeminclude.end(); ++it) { + QStringList values = it.value(); + for (int i = 0; i < values.size(); ++i) { + t << " -I\"" << values.at(i) << "\""; + } + } + t << endl; + t << "first: default" << endl; + if (debugPlatforms.contains("winscw")) + t << "default: debug-winscw"; + else if (debugPlatforms.contains("armv5")) + t << "default: debug-armv5"; + else if (debugPlatforms.size()) + t << "default: debug-" << debugPlatforms.first(); + else + t << "default: all"; + + t << endl; + if (!isPrimaryMakefile) { + t << "all:" << endl; + } else { + t << "all: debug release" << endl; + t << endl; + + QString qmakeCmd = "\t$(QMAKE) \"" + project->projectFile() + "\" " + buildArgs(); + + t << "qmake:" << endl; + t << qmakeCmd << endl; + t << endl; + + t << BLD_INF_FILENAME ": " << project->projectFile() << endl; + t << qmakeCmd << endl; + t << endl; + + t << "$(ABLD): " BLD_INF_FILENAME << endl; + t << "\tbldmake bldfiles" << endl; + t << endl; + + QString locFileDep = generateLocFileTarget(t, qmakeCmd); + + t << "debug: " << locFileDep << "$(ABLD)" << endl; + foreach(QString item, debugPlatforms) { + t << "\t$(ABLD)" << testClause << " build " << item << " udeb" << endl; + } + t << endl; + t << "release: " << locFileDep << "$(ABLD)" << endl; + foreach(QString item, releasePlatforms) { + t << "\t$(ABLD)" << testClause << " build " << item << " urel" << endl; + } + t << endl; + + // For more specific builds, targets are in this form: build-platform, e.g. release-armv5 + foreach(QString item, debugPlatforms) { + t << "debug-" << item << ": " << locFileDep << "$(ABLD)" << endl; + t << "\t$(ABLD)" << testClause << " build " << item << " udeb" << endl; + } + + foreach(QString item, releasePlatforms) { + t << "release-" << item << ": " << locFileDep << "$(ABLD)" << endl; + t << "\t$(ABLD)" << testClause << " build " << item << " urel" << endl; + } + + t << endl; + t << "export: $(ABLD)" << endl; + t << "\t$(ABLD)" << testClause << " export" << endl; + t << endl; + + t << "cleanexport: $(ABLD)" << endl; + t << "\t$(ABLD)" << testClause << " cleanexport" << endl; + t << endl; + + } + + // pre_targetdeps target depends on: + // - all targets specified in PRE_TARGETDEPS + // - the GENERATED_SOURCES sources (so that they get generated) + // - all dependencies of sources targeted for compilation + // (mainly to ensure that any included UNUSED_SOURCES that need to be generated get generated) + // + // Unfortunately, Symbian build chain doesn't support linking generated objects to target, + // so supporting generating sources is the best we can do. This is enough for mocs. + + if (targetType != TypeSubdirs) { + writeExtraTargets(t); + writeExtraCompilerTargets(t); + + t << CREATE_TEMPS_TARGET ":" << endl; + // generate command lines like this ... + // -@ if NOT EXIST ".\somedir" mkdir ".\somedir" + QStringList dirsToClean; + QString dirExists = var("QMAKE_CHK_DIR_EXISTS"); + QString mkdir = var("QMAKE_MKDIR"); + for (QMap::iterator it = systeminclude.begin(); it != systeminclude.end(); ++it) { + QStringList values = it.value(); + for (int i = 0; i < values.size(); ++i) { + if (values.at(i).endsWith("/" QT_EXTRA_INCLUDE_DIR)) { + QString fixedValue(QDir::toNativeSeparators(values.at(i))); + dirsToClean << fixedValue; + t << "\t-@ " << dirExists << " \"" << fixedValue << "\" " + << (isWindowsShell() ? "" : "|| ") + << mkdir << " \"" << fixedValue << "\"" << endl; + } + } + } + t << endl; + + // Note: EXTENSION_CLEAN will get called many times when doing reallyclean + // This is why the "2> NUL" gets appended to generated clean targets in makefile.cpp. + t << EXTENSION_CLEAN ": " COMPILER_CLEAN_TARGET << endl; + generateCleanCommands(t, dirsToClean, "$(DEL_TREE)", "", "", ""); + generateCleanCommands(t, project->values("QMAKE_CLEAN"), "$(DEL_FILE)", "", "", ""); + t << endl; + + t << PRE_TARGETDEPS_TARGET ":" + << MAKEFILE_DEPENDENCY_SEPARATOR GENERATED_SOURCES_TARGET + << MAKEFILE_DEPENDENCY_SEPARATOR ALL_SOURCE_DEPS_TARGET; + if (project->values("PRE_TARGETDEPS").size()) + t << MAKEFILE_DEPENDENCY_SEPARATOR << project->values("PRE_TARGETDEPS").join(MAKEFILE_DEPENDENCY_SEPARATOR); + t << endl << endl; + t << GENERATED_SOURCES_TARGET ":"; + if (project->values("GENERATED_SOURCES").size()) + t << MAKEFILE_DEPENDENCY_SEPARATOR << project->values("GENERATED_SOURCES").join(MAKEFILE_DEPENDENCY_SEPARATOR); + t << endl << endl; + t << ALL_SOURCE_DEPS_TARGET ":"; + + QStringList allDeps; + for (QMap::iterator it = sources.begin(); it != sources.end(); ++it) { + QString currentSourcePath = it.key(); + QStringList values = it.value(); + for (int i = 0; i < values.size(); ++i) { + // we need additional check + QString sourceFile = currentSourcePath + "/" + values.at(i); + QStringList deps = findDependencies(QDir::toNativeSeparators(sourceFile)); + appendIfnotExist(allDeps, deps); + } + } + + foreach(QString item, allDeps) { + t << MAKEFILE_DEPENDENCY_SEPARATOR << item; + } + t << endl << endl; + + // Post link operations + t << FINALIZE_TARGET ":" << endl; + if (!project->isEmpty("QMAKE_POST_LINK")) { + t << '\t' << var("QMAKE_POST_LINK"); + t << endl; + } + t << endl; + } else { + QList subtargets = findSubDirsSubTargets(); + writeSubTargets(t, subtargets, SubTargetSkipDefaultVariables | SubTargetSkipDefaultTargets); + qDeleteAll(subtargets); + } + + // Deploymend targets for both emulator and rom deployment + writeDeploymentTargets(t, false); + writeDeploymentTargets(t, true); + + generateDistcleanTargets(t); + + t << "clean: $(ABLD)" << endl; + t << "\t-$(ABLD)" << testClause << " reallyclean" << endl; + t << "\t-bldmake clean" << endl; + t << endl; + + t << "clean-debug: $(ABLD)" << endl; + foreach(QString item, debugPlatforms) { + t << "\t$(ABLD)" << testClause << " reallyclean " << item << " udeb" << endl; + } + t << endl; + t << "clean-release: $(ABLD)" << endl; + foreach(QString item, releasePlatforms) { + t << "\t$(ABLD)" << testClause << " reallyclean " << item << " urel" << endl; + } + t << endl; + + // For more specific builds, targets are in this form: clean-build-platform, e.g. clean-release-armv5 + foreach(QString item, debugPlatforms) { + t << "clean-debug-" << item << ": $(ABLD)" << endl; + t << "\t$(ABLD)" << testClause << " reallyclean " << item << " udeb" << endl; + } + foreach(QString item, releasePlatforms) { + t << "clean-release-" << item << ": $(ABLD)" << endl; + t << "\t$(ABLD)" << testClause << " reallyclean " << item << " urel" << endl; + } + t << endl; + + t << "freeze: $(ABLD)" << endl; + t << "\t$(ABLD)" << testClause << " freeze" << endl; + t << endl; + + // Abld toolchain doesn't differentiate between freezing release or debug + t << "freeze-debug: freeze" << endl << endl; + t << "freeze-release: freeze" << endl << endl; + + // For more specific builds, targets are in this form: freeze-build-platform, e.g. freeze-release-armv5, + // though note that debug and release targets of each platform are identical in symbian-abld. + foreach(QString item, debugPlatforms) { + t << "freeze-debug-" << item << ": $(ABLD)" << endl; + t << "\t$(ABLD)" << testClause << " freeze " << item << endl; + } + foreach(QString item, releasePlatforms) { + t << "freeze-release-" << item << ": $(ABLD)" << endl; + t << "\t$(ABLD)" << testClause << " freeze " << item << endl; + } + + t << endl; +} + +void SymbianAbldMakefileGenerator::writeBldInfExtensionRulesPart(QTextStream& t, const QString &iconTargetFile) +{ + // We don't use extensions for anything in abld + Q_UNUSED(t); + Q_UNUSED(iconTargetFile); +} + +bool SymbianAbldMakefileGenerator::writeDeploymentTargets(QTextStream &t, bool isRom) +{ + if (isRom) + t << DEPLOYMENT_TARGET ":" << endl; + else + t << WINSCW_DEPLOYMENT_TARGET ":" << endl; + + QString remoteTestPath = qt_epocRoot() + + QDir::toNativeSeparators(QLatin1String(isRom ? "epoc32/data/z/private/" + : "epoc32/winscw/c/private/")) + + privateDirUid; + DeploymentList depList; + + initProjectDeploySymbian(project, depList, remoteTestPath, false, true, + QLatin1String(isRom ? ROM_DEPLOYMENT_PLATFORM : EMULATOR_DEPLOYMENT_PLATFORM), + QString(), generatedDirs, generatedFiles); + + if (depList.size()) + t << "\t-echo Deploying changed files..." << endl; + + for (int i = 0; i < depList.size(); ++i) { +#ifdef Q_OS_WIN32 + // Xcopy prompts for selecting file or directory if target doesn't exist, + // and doesn't provide switch to force file selection. It does provide dir forcing, though, + // so strip the last part of the destination. + t << "\t-$(XCOPY) \"" << depList.at(i).from << "\" \"" + << depList.at(i).to.left(depList.at(i).to.lastIndexOf("\\") + 1) << "\"" << endl; +#else + QString dirExists = var("QMAKE_CHK_DIR_EXISTS"); + QString mkdir = var("QMAKE_MKDIR"); + QString dir = QFileInfo(depList.at(i).to).dir().path(); + t << "\t-@ " << dirExists << " \"" << dir << "\" || " + << mkdir << " \"" << dir << "\"" << endl; + t << "\t-$(XCOPY) \"" << QDir::toNativeSeparators(depList.at(i).from) << "\" \"" + << QDir::toNativeSeparators(depList.at(i).to) << "\"" << endl; +#endif + } + + t << endl; + + if (isRom) + t << DEPLOYMENT_CLEAN_TARGET ":" << endl; + else + t << WINSCW_DEPLOYMENT_CLEAN_TARGET ":" << endl; + + QStringList cleanList; + for (int i = 0; i < depList.size(); ++i) { + cleanList.append(QDir::toNativeSeparators(depList.at(i).to)); + } + generateCleanCommands(t, cleanList, "$(DEL_FILE)", "", "", ""); + + // Note: If deployment creates any directories, they will not get deleted after cleanup. + // To do this in robust fashion could be quite complex. + + t << endl; + + return true; +} + +void SymbianAbldMakefileGenerator::writeBldInfMkFilePart(QTextStream& t, bool addDeploymentExtension) +{ + // Normally emulator deployment gets done via regular makefile, but since subdirs + // do not get that, special deployment only makefile is generated for them if needed. + if (targetType != TypeSubdirs || addDeploymentExtension) { + gnuMakefileName = QLatin1String("Makefile_") + fileInfo(mmpFileName).completeBaseName() + + QLatin1String(".mk"); + t << "gnumakefile " << gnuMakefileName << endl; + } +} + +void SymbianAbldMakefileGenerator::appendAbldTempDirs(QStringList& sysincspaths, QString includepath) +{ + // As a workaround for Symbian toolchain insistence to treat include + // statements as relative to source file rather than the file they appear in, + // we generate extra temporary include directories to make + // relative include paths used in various headers to work properly. + // Note that this is not a fix-all solution; it's just a stop-gap measure + // to make Qt itself build until toolchain can support relative includes in + // a way that Qt expects. + QString epocPath("epoc32"); + if (!includepath.contains(epocPath)) // No temp dirs for epoc includes + appendIfnotExist(sysincspaths, includepath + QString("/" QT_EXTRA_INCLUDE_DIR)); +} diff --git a/qmake/generators/symbian/symmake_abld.h b/qmake/generators/symbian/symmake_abld.h new file mode 100644 index 0000000000..3ac27b0ccc --- /dev/null +++ b/qmake/generators/symbian/symmake_abld.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SYMMAKE_ABLD_H +#define SYMMAKE_ABLD_H + +#include + +QT_BEGIN_NAMESPACE + +class SymbianAbldMakefileGenerator : public SymbianMakefileGenerator +{ +protected: + + // Inherited from parent + virtual void writeBldInfExtensionRulesPart(QTextStream& t, const QString &iconTargetFile); + virtual void writeBldInfMkFilePart(QTextStream& t, bool addDeploymentExtension); + virtual void writeMkFile(const QString& wrapperFileName, bool deploymentOnly); + virtual void writeWrapperMakefile(QFile& wrapperFile, bool isPrimaryMakefile); + virtual void appendAbldTempDirs(QStringList& sysincspaths, QString includepath); + + bool writeDeploymentTargets(QTextStream &t, bool isRom); + QString gnuMakefileName; +public: + + SymbianAbldMakefileGenerator(); + ~SymbianAbldMakefileGenerator(); +}; + +#endif // SYMMAKE_ABLD_H diff --git a/qmake/generators/symbian/symmake_sbsv2.cpp b/qmake/generators/symbian/symmake_sbsv2.cpp new file mode 100644 index 0000000000..767645ac2d --- /dev/null +++ b/qmake/generators/symbian/symmake_sbsv2.cpp @@ -0,0 +1,760 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "symmake_sbsv2.h" +#include "initprojectdeploy_symbian.h" + +#include +#include +#include +#include +#include + +// Included from tools/shared +#include + +SymbianSbsv2MakefileGenerator::SymbianSbsv2MakefileGenerator() : SymbianMakefileGenerator() { } +SymbianSbsv2MakefileGenerator::~SymbianSbsv2MakefileGenerator() { } + +#define FLM_DEST_DIR "epoc32/tools/makefile_templates/qt" +#define FLM_SOURCE_DIR "/mkspecs/symbian-sbsv2/flm/qt" +#define PLATFORM_GCCE "gcce" +#define PLATFORM_WINSCW "winscw" +#define PLATFORM_ARM_PREFIX "arm" +#define BUILD_DEBUG "udeb" +#define BUILD_RELEASE "urel" +#define SBS_RVCT_PREFIX "rvct" + +static QString winscwPlatform; +static QString armPlatformPrefix; +static QString gccePlatform; +static QString sbsRvctPrefix; + +#if defined(Q_OS_UNIX) + extern char **environ; +#endif + +static void fixFlmCmd(QString *cmdLine, const QMap &commandsToReplace) +{ + // If commandItem starts with any $$QMAKE_* commands, do a replace for SBS equivalent. + // Command replacement is done only for the start of the command or right after + // concatenation operators (&& and ||), as otherwise unwanted replacements might occur. + static QString cmdFind(QLatin1String("(^|&&\\s*|\\|\\|\\s*)%1")); + static QString cmdReplace(QLatin1String("\\1%1")); + + // $$escape_expand(\\n\\t) doesn't work for bld.inf files, but is often used as command + // separator, so replace it with "&&" command concatenator. + cmdLine->replace("\n\t", "&&"); + + // Strip output suppression, as sbsv2 can't handle it in FLMs. Cannot be done by simply + // adding "@" to commandsToReplace, as it'd get handled last due to alphabetical ordering, + // potentially masking other commands that need replacing. + if (cmdLine->contains("@")) + cmdLine->replace(QRegExp(cmdFind.arg("@")), cmdReplace.arg("")); + + // Iterate command replacements in reverse alphabetical order of keys so + // that keys which are starts of other longer keys are iterated after longer keys. + QMapIterator cmdIter(commandsToReplace); + cmdIter.toBack(); + while (cmdIter.hasPrevious()) { + cmdIter.previous(); + if (cmdLine->contains(cmdIter.key())) + cmdLine->replace(QRegExp(cmdFind.arg(cmdIter.key())), cmdReplace.arg(cmdIter.value())); + } + + // Sbsv2 toolchain strips all backslashes (even double ones) from option parameters, so just + // assume all backslashes are directory separators and replace them with slashes. + // Problem: If some command actually needs backslashes for something else than dir separator, + // we are out of luck. + cmdLine->replace("\\", "/"); +} + +// Copies Qt FLMs to correct location under epocroot. +// This is not done by configure as it is possible to change epocroot after configure. +void SymbianSbsv2MakefileGenerator::exportFlm() +{ + static bool flmExportDone = false; + + if (!flmExportDone) { + QDir sourceDir = QDir(QLibraryInfo::location(QLibraryInfo::PrefixPath) + FLM_SOURCE_DIR); + QFileInfoList sourceInfos = sourceDir.entryInfoList(QDir::Files); + + QDir destDir(qt_epocRoot() + FLM_DEST_DIR); + if (!destDir.exists()) { + if (destDir.mkpath(destDir.absolutePath())) + generatedDirs << destDir.absolutePath(); + } + + foreach(QFileInfo item, sourceInfos) { + QFileInfo destInfo = QFileInfo(destDir.absolutePath() + "/" + item.fileName()); + if (!destInfo.exists() || destInfo.lastModified() != item.lastModified()) { + if (destInfo.exists()) + QFile::remove(destInfo.absoluteFilePath()); + if (QFile::copy(item.absoluteFilePath(), destInfo.absoluteFilePath())) + generatedFiles << destInfo.absoluteFilePath(); + else + fprintf(stderr, "Error: Could not copy '%s' -> '%s'\n", + qPrintable(item.absoluteFilePath()), + qPrintable(destInfo.absoluteFilePath())); + } + } + flmExportDone = true; + } +} + +void SymbianSbsv2MakefileGenerator::findInstalledCompilerVersions(const QString &matchExpression, + const QString &versionPrefix, + QStringList *versionList) +{ + // No need to be able to find env variables on other operating systems, + // as only linux and windows have support for symbian-sbsv2 toolchain +#if defined(Q_OS_UNIX) || defined(Q_OS_WIN) + char *entry = 0; + int count = 0; + QRegExp matcher(matchExpression); + while ((entry = environ[count++])) { + if (matcher.exactMatch(QString::fromLocal8Bit(entry)) + && fileInfo(matcher.cap(matcher.captureCount())).exists()) { + // First capture (index 0) is the whole match, which is skipped. + // Next n captures are version numbers, which are interesting. + // Final capture is the env var value, which we already used, so that is skipped, too. + int capture = 1; + int finalCapture = matcher.captureCount() - 1; + QString version = versionPrefix; + while (capture <= finalCapture) { + version.append(matcher.cap(capture)); + if (capture != finalCapture) + version.append(QLatin1Char('.')); + capture++; + } + *versionList << version; + } + } +#endif +} + +void SymbianSbsv2MakefileGenerator::findGcceVersions(QStringList *gcceVersionList, + QString *defaultVersion) +{ + QString matchStr = QLatin1String("SBS_GCCE(\\d)(\\d)(\\d)BIN=(.*)"); + findInstalledCompilerVersions(matchStr, gccePlatform, gcceVersionList); + + QString qtGcceVersion = QString::fromLocal8Bit(qgetenv("QT_GCCE_VERSION")); + + if (!qtGcceVersion.isEmpty()) { + if (QRegExp("\\d+\\.\\d+\\.\\d+").exactMatch(qtGcceVersion)) { + *defaultVersion = gccePlatform + qtGcceVersion; + } else { + fprintf(stderr, "Warning: Variable QT_GCCE_VERSION ('%s') is in incorrect " + "format, expected format is: 'x.y.z'. Attempting to autodetect GCCE version.\n", + qPrintable(qtGcceVersion)); + } + } + + if (defaultVersion->isEmpty() && gcceVersionList->size()) { + gcceVersionList->sort(); + *defaultVersion = gcceVersionList->last(); + } +} + +void SymbianSbsv2MakefileGenerator::findRvctVersions(QStringList *rvctVersionList, + QString *defaultVersion) +{ + QString matchStr = QLatin1String("RVCT(\\d)(\\d)BIN=(.*)"); + findInstalledCompilerVersions(matchStr, sbsRvctPrefix, rvctVersionList); + + QString qtRvctVersion = QString::fromLocal8Bit(qgetenv("QT_RVCT_VERSION")); + + if (!qtRvctVersion.isEmpty()) { + if (QRegExp("\\d+\\.\\d+").exactMatch(qtRvctVersion)) { + *defaultVersion = sbsRvctPrefix + qtRvctVersion; + } else { + fprintf(stderr, "Warning: Variable QT_RVCT_VERSION ('%s') is in incorrect " + "format, expected format is: 'x.y'.\n", + qPrintable(qtRvctVersion)); + } + } +} + +QString SymbianSbsv2MakefileGenerator::configClause(const QString &platform, + const QString &build, + const QString &compilerVersion, + const QString &clauseTemplate) +{ + QString retval; + if (QString::compare(platform, winscwPlatform) == 0) { + retval = clauseTemplate.arg(build); + } else if (platform.startsWith(armPlatformPrefix)) { + QString fixedCompilerVersion = compilerVersion; + fixedCompilerVersion.replace(".","_"); + retval = clauseTemplate.arg(platform.mid(sizeof(PLATFORM_ARM_PREFIX)-1)) + .arg(build) + .arg(fixedCompilerVersion); + } // else - Unsupported platform for makefile target, return empty clause + return retval; +} + +void SymbianSbsv2MakefileGenerator::writeSbsDeploymentList(const DeploymentList& depList, QTextStream& t) +{ + for (int i = 0; i < depList.size(); ++i) { + t << "START EXTENSION qt/qmake_emulator_deployment" << endl; + QString fromItem = depList.at(i).from; + QString toItem = depList.at(i).to; + fromItem.replace("\\", "/"); + toItem.replace("\\", "/"); +#if defined(Q_OS_WIN) + // add drive if it doesn't have one yet + if (toItem.size() > 1 && toItem[1] != QLatin1Char(':')) + toItem.prepend(QDir::current().absolutePath().left(2)); +#endif + t << "OPTION DEPLOY_SOURCE " << fromItem << endl; + t << "OPTION DEPLOY_TARGET " << toItem << endl; + t << "END" << endl; + } +} + +void SymbianSbsv2MakefileGenerator::writeMkFile(const QString& wrapperFileName, bool deploymentOnly) +{ + // Can't use extension makefile with sbsv2 + Q_UNUSED(wrapperFileName); + Q_UNUSED(deploymentOnly); +} + +void SymbianSbsv2MakefileGenerator::writeWrapperMakefile(QFile& wrapperFile, bool isPrimaryMakefile) +{ + static QString debugBuild; + static QString releaseBuild; + static QString defaultGcceCompilerVersion; + static QString defaultRvctCompilerVersion; + static QStringList rvctVersions; + static QStringList gcceVersions; + static QStringList allArmCompilerVersions; + + // Initialize static variables used in makefile creation + if (debugBuild.isEmpty()) { + debugBuild.append(QLatin1String(BUILD_DEBUG)); + releaseBuild.append(QLatin1String(BUILD_RELEASE)); + winscwPlatform.append(QLatin1String(PLATFORM_WINSCW)); + gccePlatform.append(QLatin1String(PLATFORM_GCCE)); + armPlatformPrefix.append(QLatin1String(PLATFORM_ARM_PREFIX)); + sbsRvctPrefix.append(QLatin1String(SBS_RVCT_PREFIX)); + + findGcceVersions(&gcceVersions, &defaultGcceCompilerVersion); + findRvctVersions(&rvctVersions, &defaultRvctCompilerVersion); + + allArmCompilerVersions << rvctVersions << gcceVersions; + + if (!allArmCompilerVersions.size()) { + fprintf(stderr, "Warning: No HW compilers detected. " + "Please install either GCCE or RVCT compiler to enable release builds.\n"); + } + } + + QStringList allPlatforms; + foreach(QString platform, project->values("SYMBIAN_PLATFORMS")) { + allPlatforms << platform.toLower(); + } + + if (!gcceVersions.size()) + allPlatforms.removeAll(gccePlatform); + + QString testClause; + if (project->isActiveConfig(SYMBIAN_TEST_CONFIG)) + testClause = QLatin1String(".test"); + else + testClause = QLatin1String(""); + + // Note: armClause is used for gcce, too, which has a side effect + // of requiring armv* platform(s) in SYMBIAN_PLATFORMS in order + // to get any compiler version specific targets. + QString armClause = " -c " PLATFORM_ARM_PREFIX ".%1.%2.%3" + testClause; + QString genericArmClause; + if (defaultRvctCompilerVersion.isEmpty()) { + // Note: Argument %3 needs to be empty string in this version of clause + genericArmClause = " -c " PLATFORM_ARM_PREFIX "%1_%2%3" + testClause; + } else { + // If defaultRvctCompilerVersion is defined, use specific sbs clause for "generic" clause + genericArmClause = armClause; + } + QString winscwClause = " -c " PLATFORM_WINSCW "_%1.mwccinc" + testClause;; + + QStringList armPlatforms = allPlatforms.filter(QRegExp("^" PLATFORM_ARM_PREFIX)); + + if (!allArmCompilerVersions.size()) { + foreach (QString item, armPlatforms) { + allPlatforms.removeAll(item); + } + armPlatforms.clear(); + } + + QStringList allClauses; + QStringList debugClauses; + QStringList releaseClauses; + + // Only winscw and arm platforms are supported + QStringList debugPlatforms = allPlatforms; + QStringList releasePlatforms = allPlatforms; + releasePlatforms.removeAll(winscwPlatform); // No release for emulator + + if (!releasePlatforms.size()) { + fprintf(stderr, "Warning: No valid release platforms in SYMBIAN_PLATFORMS (%s)\n" + "Most likely required compiler(s) are not properly installed.\n", + qPrintable(project->values("SYMBIAN_PLATFORMS").join(" "))); + } + + if (debugPlatforms.contains(winscwPlatform)) + debugClauses << configClause(winscwPlatform, debugBuild, QString(), winscwClause); + + foreach(QString item, armPlatforms) { + // Only use single clause per arm platform even if multiple compiler versions were found, + // otherwise we get makefile target collisions from sbsv2 toolchain. + if (rvctVersions.size()) { + debugClauses << configClause(item, debugBuild, defaultRvctCompilerVersion, genericArmClause); + releaseClauses << configClause(item, releaseBuild, defaultRvctCompilerVersion, genericArmClause); + } else { + debugClauses << configClause(item, debugBuild, defaultGcceCompilerVersion, armClause); + releaseClauses << configClause(item, releaseBuild, defaultGcceCompilerVersion, armClause); + } + } + + allClauses << debugClauses << releaseClauses; + + QTextStream t(&wrapperFile); + + MakefileGenerator::writeHeader(t); + + t << "MAKEFILE = " << fileInfo(wrapperFile.fileName()).fileName() << endl; + t << "QMAKE = " << var("QMAKE_QMAKE") << endl; + t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl; + t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl; + t << "CHK_DIR_EXISTS = " << var("QMAKE_CHK_DIR_EXISTS") << endl; + t << "MKDIR = " << var("QMAKE_MKDIR") << endl; + t << "MOVE = " << var("QMAKE_MOVE") << endl; + t << "DEBUG_PLATFORMS = " << debugPlatforms.join(" ") << endl; + t << "RELEASE_PLATFORMS = " << releasePlatforms.join(" ") << endl; + t << "MAKE = make" << endl; + t << "SBS = sbs" << endl; + t << endl; + t << "DEFINES" << '\t' << " = " + << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ") + << varGlue("QMAKE_COMPILER_DEFINES", "-D", "-D", " ") + << varGlue("DEFINES","-D"," -D","") << endl; + + t << "INCPATH" << '\t' << " = "; + + for (QMap::iterator it = systeminclude.begin(); it != systeminclude.end(); ++it) { + QStringList values = it.value(); + for (int i = 0; i < values.size(); ++i) { + t << " -I\"" << values.at(i) << "\" "; + } + } + + t << endl; + t << "first: default" << endl << endl; + if (!isPrimaryMakefile) { + t << "all:" << endl << endl; + t << "default: all" << endl << endl; + } else { + t << "all: debug release" << endl << endl; + if (debugPlatforms.contains(winscwPlatform)) + t << "default: debug-winscw"; + else if (debugPlatforms.size()) + t << "default: debug-" << debugPlatforms.first(); + else + t << "default: all"; + t << endl; + + QString qmakeCmd = "\t$(QMAKE) \"" + project->projectFile() + "\" " + buildArgs(); + + t << "qmake:" << endl; + t << qmakeCmd << endl; + t << endl; + + t << BLD_INF_FILENAME ": " << project->projectFile() << endl; + t << qmakeCmd << endl; + t << endl; + + QString locFileDep = generateLocFileTarget(t, qmakeCmd); + + t << "debug: " << locFileDep << BLD_INF_FILENAME << endl; + t << "\t$(SBS)"; + foreach(QString clause, debugClauses) { + t << clause; + } + t << endl; + t << "clean-debug: " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) reallyclean --toolcheck=off"; + foreach(QString clause, debugClauses) { + t << clause; + } + t << endl; + + t << "freeze-debug: " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) freeze"; + foreach(QString clause, debugClauses) { + t << clause; + } + t << endl; + + t << "release: " << locFileDep << BLD_INF_FILENAME << endl; + t << "\t$(SBS)"; + foreach(QString clause, releaseClauses) { + t << clause; + } + t << endl; + t << "clean-release: " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) reallyclean --toolcheck=off"; + foreach(QString clause, releaseClauses) { + t << clause; + } + t << endl; + + t << "freeze-release: " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) freeze"; + foreach(QString clause, releaseClauses) { + t << clause; + } + t << endl << endl; + + QString defaultGcceArmVersion; + if (armPlatforms.size()) { + defaultGcceArmVersion = armPlatforms.first(); + } else { + defaultGcceArmVersion = QLatin1String("armv5"); + } + + // For more specific builds, targets are in this form: + // release-armv5 - generic target, compiler version determined by toolchain or autodetection + // release-armv5-rvct4.0 - compiler version specific target + foreach(QString item, debugPlatforms) { + QString clause; + if (item.compare(winscwPlatform) == 0) + clause = configClause(item, debugBuild, QString(), winscwClause); + else if (item.compare(gccePlatform) == 0 ) + clause = configClause(defaultGcceArmVersion, debugBuild, defaultGcceCompilerVersion, armClause); + else // use generic arm clause + clause = configClause(item, debugBuild, defaultRvctCompilerVersion, genericArmClause); + + t << "debug-" << item << ": " << locFileDep << BLD_INF_FILENAME << endl; + t << "\t$(SBS)" << clause << endl; + t << "clean-debug-" << item << ": " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) reallyclean" << clause << endl; + t << "freeze-debug-" << item << ": " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) freeze" << clause << endl; + } + + foreach(QString item, releasePlatforms) { + QString clause; + if (item.compare(gccePlatform) == 0 ) + clause = configClause(defaultGcceArmVersion, releaseBuild, defaultGcceCompilerVersion, armClause); + else // use generic arm clause + clause = configClause(item, releaseBuild, defaultRvctCompilerVersion, genericArmClause); + + t << "release-" << item << ": " << locFileDep << BLD_INF_FILENAME << endl; + t << "\t$(SBS)" << clause << endl; + t << "clean-release-" << item << ": " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) reallyclean" << clause << endl; + t << "freeze-release-" << item << ": " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) freeze" << clause << endl; + } + + foreach(QString item, armPlatforms) { + foreach(QString compilerVersion, allArmCompilerVersions) { + QString debugClause = configClause(item, debugBuild, compilerVersion, armClause); + QString releaseClause = configClause(item, releaseBuild, compilerVersion, armClause); + t << "debug-" << item << "-" << compilerVersion << ": " << locFileDep << BLD_INF_FILENAME << endl; + t << "\t$(SBS)" << debugClause << endl; + t << "clean-debug-" << item << "-" << compilerVersion << ": " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) reallyclean" << debugClause << endl; + t << "freeze-debug-" << item << "-" << compilerVersion << ": " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) freeze" << debugClause << endl; + t << "release-" << item << "-" << compilerVersion << ": " << locFileDep << BLD_INF_FILENAME << endl; + t << "\t$(SBS)" << releaseClause << endl; + t << "clean-release-" << item << "-" << compilerVersion << ": " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) reallyclean" << releaseClause << endl; + t << "freeze-release-" << item << "-" << compilerVersion << ": " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) freeze" << releaseClause << endl; + } + } + + t << endl; + t << "export: " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) export"; + foreach(QString clause, allClauses) { + t << clause; + } + t << endl << endl; + + t << "cleanexport: " << BLD_INF_FILENAME << endl; + t << "\t$(SBS) cleanexport"; + foreach(QString clause, allClauses) { + t << clause; + } + t << endl << endl; + + // Typically one wants to freeze release binaries, so make plain freeze target equal to + // freeze-release. If freezing of debug binaries is needed for some reason, then + // freeze-debug target should be used. There is no point to try freezing both with one + // target as both produce the same def file. + t << "freeze: freeze-release" << endl << endl; + } + + // Add all extra targets including extra compiler targets also to wrapper makefile, + // even though many of them may have already been added to bld.inf as FLMs. + // This is to enable use of targets like 'mocables', which call targets generated by extra compilers. + if (targetType != TypeSubdirs) { + t << extraTargetsCache; + t << extraCompilersCache; + } else { + QList subtargets = findSubDirsSubTargets(); + writeSubTargets(t, subtargets, SubTargetSkipDefaultVariables|SubTargetSkipDefaultTargets); + qDeleteAll(subtargets); + } + + generateDistcleanTargets(t); + + // Do not check for tools when doing generic clean, as most tools are not actually needed for + // cleaning. Mainly this is relevant for environments that do not have winscw compiler. + t << "clean: " << BLD_INF_FILENAME << endl; + t << "\t-$(SBS) reallyclean --toolcheck=off"; + foreach(QString clause, allClauses) { + t << clause; + } + t << endl << endl; + + t << endl; +} + +void SymbianSbsv2MakefileGenerator::writeBldInfExtensionRulesPart(QTextStream& t, const QString &iconTargetFile) +{ + // Makes sure we have needed FLMs in place. + exportFlm(); + + // Parse extra compilers data + QStringList defines; + QStringList incPath; + + defines << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ") + << varGlue("QMAKE_COMPILER_DEFINES", "-D", "-D", " ") + << varGlue("DEFINES","-D"," -D",""); + for (QMap::iterator it = systeminclude.begin(); it != systeminclude.end(); ++it) { + QStringList values = it.value(); + for (int i = 0; i < values.size(); ++i) { + incPath << QLatin1String(" -I\"") + values.at(i) + "\""; + } + } + + QMap commandsToReplace; + commandsToReplace.insert(project->values("QMAKE_COPY").join(" "), + project->values("QMAKE_SBSV2_COPY").join(" ")); + commandsToReplace.insert(project->values("QMAKE_COPY_DIR").join(" "), + project->values("QMAKE_SBSV2_COPY_DIR").join(" ")); + commandsToReplace.insert(project->values("QMAKE_MOVE").join(" "), + project->values("QMAKE_SBSV2_MOVE").join(" ")); + commandsToReplace.insert(project->values("QMAKE_DEL_FILE").join(" "), + project->values("QMAKE_SBSV2_DEL_FILE").join(" ")); + commandsToReplace.insert(project->values("QMAKE_MKDIR").join(" "), + project->values("QMAKE_SBSV2_MKDIR").join(" ")); + commandsToReplace.insert(project->values("QMAKE_DEL_DIR").join(" "), + project->values("QMAKE_SBSV2_DEL_DIR").join(" ")); + commandsToReplace.insert(project->values("QMAKE_DEL_TREE").join(" "), + project->values("QMAKE_SBSV2_DEL_TREE").join(" ")); + + // Write extra compilers and targets to initialize QMAKE_ET_* variables + // Cache results to avoid duplicate calls when creating wrapper makefile + QTextStream extraCompilerStream(&extraCompilersCache); + QTextStream extraTargetStream(&extraTargetsCache); + writeExtraCompilerTargets(extraCompilerStream); + writeExtraTargets(extraTargetStream); + + // Figure out everything the target depends on as we don't want to run extra targets that + // are not necessary. + QStringList allPreDeps; + foreach(QString item, project->values("PRE_TARGETDEPS")) { + allPreDeps.append(fileInfo(item).absoluteFilePath()); + } + + foreach (QString item, project->values("GENERATED_SOURCES")) { + allPreDeps.append(fileInfo(item).absoluteFilePath()); + } + + for (QMap::iterator it = sources.begin(); it != sources.end(); ++it) { + QString currentSourcePath = it.key(); + QStringList values = it.value(); + for (int i = 0; i < values.size(); ++i) { + QString sourceFile = currentSourcePath + "/" + values.at(i); + QStringList deps = findDependencies(QDir::toNativeSeparators(sourceFile)); + foreach(QString depItem, deps) { + appendIfnotExist(allPreDeps, fileInfo(depItem).absoluteFilePath()); + } + } + } + + // Write FLM rules for all extra targets and compilers that we depend on to build the target. + QStringList extraTargets; + extraTargets << project->values("QMAKE_EXTRA_TARGETS") << project->values("QMAKE_EXTRA_COMPILERS"); + foreach(QString item, extraTargets) { + foreach(QString targetItem, project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_TARGETS.") + item)) { + // Make sure targetpath is absolute + QString absoluteTarget = fileInfo(targetItem).absoluteFilePath(); + if (allPreDeps.contains(absoluteTarget)) { + QStringList deps = project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_DEPS.") + item + targetItem); + QString commandItem = project->values(QLatin1String("QMAKE_INTERNAL_ET_PARSED_CMD.") + item + targetItem).join(" "); + + // Make sure all deps paths are absolute + QString absoluteDeps; + foreach (QString depItem, deps) { + if (!depItem.isEmpty()) { + absoluteDeps.append(fileInfo(depItem).absoluteFilePath()); + absoluteDeps.append(" "); + } + } + + t << "START EXTENSION qt/qmake_extra_pre_targetdep.export" << endl; + t << "OPTION PREDEP_TARGET " << absoluteTarget << endl; + t << "OPTION DEPS " << absoluteDeps << endl; + + if (commandItem.indexOf("$(INCPATH)") != -1) + commandItem.replace("$(INCPATH)", incPath.join(" ")); + if (commandItem.indexOf("$(DEFINES)") != -1) + commandItem.replace("$(DEFINES)", defines.join(" ")); + + fixFlmCmd(&commandItem, commandsToReplace); + + t << "OPTION COMMAND " << commandItem << endl; + t << "END" << endl; + } + } + } + + t << endl; + + // Write deployment rules + QString remoteTestPath = qt_epocRoot() + QLatin1String("epoc32/winscw/c/private/") + privateDirUid; + DeploymentList depList; + + //write emulator deployment + t << "#if defined(WINSCW)" << endl; + initProjectDeploySymbian(project, depList, remoteTestPath, false, true, + QLatin1String(EMULATOR_DEPLOYMENT_PLATFORM), QString(), generatedDirs, generatedFiles); + writeSbsDeploymentList(depList, t); + t << "#endif" << endl; + + //write ROM deployment + remoteTestPath = qt_epocRoot() + QLatin1String("epoc32/data/z/private/") + privateDirUid; + depList.clear(); + initProjectDeploySymbian(project, depList, remoteTestPath, false, true, + QLatin1String(ROM_DEPLOYMENT_PLATFORM), QString(), generatedDirs, generatedFiles); + writeSbsDeploymentList(depList, t); + t << endl; + + // Write post link rules + if (!project->isEmpty("QMAKE_POST_LINK")) { + QString postLinkCmd = var("QMAKE_POST_LINK"); + fixFlmCmd(&postLinkCmd, commandsToReplace); + t << "START EXTENSION qt/qmake_post_link" << endl; + t << "OPTION POST_LINK_CMD " << postLinkCmd << endl; + t << "OPTION LINK_TARGET " << fixedTarget << QLatin1String(".") << getTargetExtension() << endl; + t << "END" << endl; + t << endl; + } + + // Application icon generation + QStringList icons = project->values("ICON"); + if (icons.size()) { + QString icon = icons.first(); + if (icons.size() > 1) + fprintf(stderr, "Warning: Only first icon specified in ICON variable is used: '%s'.", qPrintable(icon)); + + t << "START EXTENSION s60/mifconv" << endl; + + QFileInfo iconInfo = fileInfo(icon); + + QFileInfo bldinf(project->values("MAKEFILE").first()); + QString iconPath = bldinf.dir().relativeFilePath(iconInfo.path()); + + QString iconFile = iconInfo.baseName(); + + QFileInfo iconTargetInfo = fileInfo(iconTargetFile); + QString iconTarget = iconTargetInfo.fileName(); + + t << "OPTION SOURCES -c32 " << iconFile << endl; + t << "OPTION SOURCEDIR " << iconPath << endl; + t << "OPTION TARGETFILE " << iconTarget << endl; + t << "OPTION SVGENCODINGVERSION 3" << endl; // Compatibility with S60 3.1 devices and up + t << "END" << endl; + } + + t << "START EXTENSION qt/qmake_store_build" << endl; + t << "END" << endl; + t << endl; + + // Handle QMAKE_CLEAN + QStringList cleanFiles = project->values("QMAKE_CLEAN"); + if (!cleanFiles.isEmpty()) { + QStringList absoluteCleanFiles; + foreach (QString cleanFile, cleanFiles) { + QFileInfo fi(cleanFile); + QString fileName = QLatin1String("\""); + fileName.append(fi.absoluteFilePath()); + fileName.append(QLatin1String("\"")); + absoluteCleanFiles << fileName; + } + t << "START EXTENSION qt/qmake_clean" << endl; + t << "OPTION CLEAN_FILES " << absoluteCleanFiles.join(" ") << endl; + t << "END" << endl; + } + t << endl; +} + +void SymbianSbsv2MakefileGenerator::writeBldInfMkFilePart(QTextStream& t, bool addDeploymentExtension) +{ + // We don't generate extension makefile in sbsb2 + Q_UNUSED(t); + Q_UNUSED(addDeploymentExtension); +} + +void SymbianSbsv2MakefileGenerator::appendAbldTempDirs(QStringList& sysincspaths, QString includepath) +{ + //Do nothing + Q_UNUSED(sysincspaths); + Q_UNUSED(includepath); +} diff --git a/qmake/generators/symbian/symmake_sbsv2.h b/qmake/generators/symbian/symmake_sbsv2.h new file mode 100644 index 0000000000..bec1034fbe --- /dev/null +++ b/qmake/generators/symbian/symmake_sbsv2.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SYMMAKE_SBSV2_H +#define SYMMAKE_SBSV2_H + +#include + +QT_BEGIN_NAMESPACE + +class SymbianSbsv2MakefileGenerator : public SymbianMakefileGenerator +{ +protected: + + // Inherited from parent + virtual void writeBldInfExtensionRulesPart(QTextStream& t, const QString &iconTargetFile); + virtual void writeBldInfMkFilePart(QTextStream& t, bool addDeploymentExtension); + virtual void writeMkFile(const QString& wrapperFileName, bool deploymentOnly); + virtual void writeWrapperMakefile(QFile& wrapperFile, bool isPrimaryMakefile); + virtual void appendAbldTempDirs(QStringList& sysincspaths, QString includepath); + virtual bool isForSymbianSbsv2() const { return true; } // FIXME: killme - i'm ugly! + +public: + + SymbianSbsv2MakefileGenerator(); + ~SymbianSbsv2MakefileGenerator(); + +private: + void exportFlm(); + void findGcceVersions(QStringList *gcceVersionList, QString *defaultVersion); + void findRvctVersions(QStringList *rvctVersionList, QString *defaultVersion); + void findInstalledCompilerVersions(const QString &matchExpression, + const QString &versionPrefix, + QStringList *versionList); + QString configClause(const QString &platform, + const QString &build, + const QString &compilerVersion, + const QString &clauseTemplate); + + void writeSbsDeploymentList(const DeploymentList& depList, QTextStream& t); + + QString extraTargetsCache; + QString extraCompilersCache; +}; + +#endif // SYMMAKE_SBSV2_H diff --git a/qmake/generators/unix/unixmake.cpp b/qmake/generators/unix/unixmake.cpp new file mode 100644 index 0000000000..e659e62288 --- /dev/null +++ b/qmake/generators/unix/unixmake.cpp @@ -0,0 +1,925 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "unixmake.h" +#include "option.h" +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +void +UnixMakefileGenerator::init() +{ + if(init_flag) + return; + init_flag = true; + + if(project->isEmpty("QMAKE_EXTENSION_SHLIB")) { + if(project->isEmpty("QMAKE_CYGWIN_SHLIB")) { + project->values("QMAKE_EXTENSION_SHLIB").append("so"); + } else { + project->values("QMAKE_EXTENSION_SHLIB").append("dll"); + } + } + + if (project->isEmpty("QMAKE_PREFIX_SHLIB")) + // Prevent crash when using the empty variable. + project->values("QMAKE_PREFIX_SHLIB").append(""); + + if(!project->isEmpty("QMAKE_FAILED_REQUIREMENTS")) /* no point */ + return; + + QStringList &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")); + if(project->isEmpty("QMAKE_COPY_FILE")) + project->values("QMAKE_COPY_FILE").append("$(COPY)"); + if(project->isEmpty("QMAKE_STREAM_EDITOR")) + project->values("QMAKE_STREAM_EDITOR").append("sed"); + if(project->isEmpty("QMAKE_COPY_DIR")) + project->values("QMAKE_COPY_DIR").append("$(COPY) -R"); + if(project->isEmpty("QMAKE_INSTALL_FILE")) + project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_DIR")) + project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)"); + if(project->isEmpty("QMAKE_INSTALL_PROGRAM")) + project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_LIBTOOL")) + project->values("QMAKE_LIBTOOL").append("libtool --silent"); + if(project->isEmpty("QMAKE_SYMBOLIC_LINK")) + project->values("QMAKE_SYMBOLIC_LINK").append("ln -f -s"); + + /* 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"); + if(project->values("QMAKE_INTERNAL_QMAKE_DEPS").indexOf("qmake_all") == -1) + project->values("QMAKE_INTERNAL_QMAKE_DEPS").append("qmake_all"); + return; /* subdirs is done */ + } + + //If the TARGET looks like a path split it into DESTDIR and the resulting TARGET + if(!project->isEmpty("TARGET")) { + project->values("TARGET") = escapeFilePaths(project->values("TARGET")); + QString targ = unescapeFilePath(project->first("TARGET")); + int slsh = qMax(targ.lastIndexOf('/'), targ.lastIndexOf(Option::dir_sep)); + if(slsh != -1) { + if(project->isEmpty("DESTDIR")) + project->values("DESTDIR").append(""); + else if(project->first("DESTDIR").right(1) != Option::dir_sep) + project->values("DESTDIR") = QStringList(project->first("DESTDIR") + Option::dir_sep); + project->values("DESTDIR") = QStringList(project->first("DESTDIR") + targ.left(slsh+1)); + project->values("TARGET") = QStringList(targ.mid(slsh+1)); + } + } + + project->values("QMAKE_ORIG_TARGET") = project->values("TARGET"); + project->values("QMAKE_ORIG_DESTDIR") = project->values("DESTDIR"); + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS")); + project->values("QMAKE_LIBS_PRIVATE") += escapeFilePaths(project->values("LIBS_PRIVATE")); + 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"); + if(!project->isEmpty("QMAKE_INCDIR")) + project->values("INCLUDEPATH") += project->values("QMAKE_INCDIR"); + if(!project->isEmpty("QMAKE_LIBDIR")) { + const QStringList &libdirs = project->values("QMAKE_LIBDIR"); + for(int i = 0; i < libdirs.size(); ++i) { + if(!project->isEmpty("QMAKE_LFLAGS_RPATH") && project->isActiveConfig("rpath_libdirs")) + project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATH") + libdirs[i]; + if (project->isActiveConfig("rvct_linker")) { + project->values("QMAKE_LIBDIR_FLAGS") += "--userlibpath " + escapeFilePath(libdirs[i]); + } else if (project->isActiveConfig("armcc_linker")) { + project->values("QMAKE_LIBDIR_FLAGS") += "-L--userlibpath=" + escapeFilePath(libdirs[i]); + } else { + project->values("QMAKE_LIBDIR_FLAGS") += "-L" + escapeFilePath(libdirs[i]); + } + } + } + if(project->isActiveConfig("macx") && !project->isEmpty("QMAKE_FRAMEWORKPATH")) { + const QStringList &fwdirs = project->values("QMAKE_FRAMEWORKPATH"); + for(int i = 0; i < fwdirs.size(); ++i) { + project->values("QMAKE_FRAMEWORKPATH_FLAGS") += "-F" + escapeFilePath(fwdirs[i]); + } + } + if(!project->isEmpty("QMAKE_RPATHDIR")) { + const QStringList &rpathdirs = project->values("QMAKE_RPATHDIR"); + for(int i = 0; i < rpathdirs.size(); ++i) { + if(!project->isEmpty("QMAKE_LFLAGS_RPATH")) + project->values("QMAKE_LFLAGS") += var("QMAKE_LFLAGS_RPATH") + escapeFilePath(QFileInfo(rpathdirs[i]).absoluteFilePath()); + } + } + + project->values("QMAKE_FILETAGS") << "SOURCES" << "GENERATED_SOURCES" << "TARGET" << "DESTDIR"; + if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) { + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(int i = 0; i < quc.size(); ++i) + project->values("QMAKE_FILETAGS") += project->values(quc[i]+".input"); + } + + if(project->isActiveConfig("GNUmake") && !project->isEmpty("QMAKE_CFLAGS_DEPS")) + include_deps = true; //do not generate deps + if(project->isActiveConfig("compile_libtool")) + Option::obj_ext = ".lo"; //override the .o + + MakefileGenerator::init(); + + QString comps[] = { "C", "CXX", "OBJC", "OBJCXX", QString() }; + for(int i = 0; !comps[i].isNull(); i++) { + QString compile_flag = var("QMAKE_COMPILE_FLAG"); + if(compile_flag.isEmpty()) + compile_flag = "-c"; + + if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) { + QString pchFlags = var("QMAKE_" + comps[i] + "FLAGS_USE_PRECOMPILE"); + + QString pchBaseName; + if(!project->isEmpty("PRECOMPILED_DIR")) { + pchBaseName = Option::fixPathToTargetOS(project->first("PRECOMPILED_DIR")); + if(!pchBaseName.endsWith(Option::dir_sep)) + pchBaseName += Option::dir_sep; + } + pchBaseName += project->first("QMAKE_ORIG_TARGET"); + + // replace place holders + pchFlags = pchFlags.replace("${QMAKE_PCH_INPUT}", + fileFixify(project->first("PRECOMPILED_HEADER"))); + pchFlags = pchFlags.replace("${QMAKE_PCH_OUTPUT_BASE}", pchBaseName); + if (project->isActiveConfig("icc_pch_style")) { + // icc style + pchFlags = pchFlags.replace("${QMAKE_PCH_OUTPUT}", + pchBaseName + project->first("QMAKE_PCH_OUTPUT_EXT")); + } else { + // gcc style (including clang_pch_style) + QString headerPrefix = project->first("QMAKE_PRECOMP_PREFIX"); + QString headerSuffix; + if (project->isActiveConfig("clang_pch_style")) + headerSuffix = project->first("QMAKE_PCH_OUTPUT_EXT"); + else + pchBaseName += project->first("QMAKE_PCH_OUTPUT_EXT"); + + pchBaseName += Option::dir_sep; + QString pchOutputFile; + + if(comps[i] == "C") { + pchOutputFile = "c"; + } else if(comps[i] == "CXX") { + pchOutputFile = "c++"; + } else if(project->isActiveConfig("objective_c")) { + if(comps[i] == "OBJC") + pchOutputFile = "objective-c"; + else if(comps[i] == "OBJCXX") + pchOutputFile = "objective-c++"; + } + + if(!pchOutputFile.isEmpty()) { + pchFlags = pchFlags.replace("${QMAKE_PCH_OUTPUT}", + pchBaseName + pchOutputFile + headerSuffix); + } + } + + if (!pchFlags.isEmpty()) + compile_flag += " " + pchFlags; + } + + QString cflags; + if(comps[i] == "OBJC" || comps[i] == "OBJCXX") + cflags += " $(CFLAGS)"; + else + cflags += " $(" + comps[i] + "FLAGS)"; + compile_flag += cflags + " $(INCPATH)"; + + QString compiler = comps[i]; + if (compiler == "C") + compiler = "CC"; + + QString runComp = "QMAKE_RUN_" + compiler; + if(project->isEmpty(runComp)) + project->values(runComp).append("$(" + compiler + ") " + compile_flag + " -o $obj $src"); + QString runCompImp = "QMAKE_RUN_" + compiler + "_IMP"; + if(project->isEmpty(runCompImp)) + project->values(runCompImp).append("$(" + compiler + ") " + compile_flag + " -o \"$@\" \"$<\""); + } + + if(project->isActiveConfig("macx") && !project->isEmpty("TARGET") && !project->isActiveConfig("compile_libtool") && + ((project->isActiveConfig("build_pass") || project->isEmpty("BUILDS")))) { + QString bundle; + if(project->isActiveConfig("bundle") && !project->isEmpty("QMAKE_BUNDLE_EXTENSION")) { + bundle = unescapeFilePath(project->first("TARGET")); + if(!project->isEmpty("QMAKE_BUNDLE_NAME")) + bundle = unescapeFilePath(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 = unescapeFilePath(project->first("TARGET")); + if(!project->isEmpty("QMAKE_APPLICATION_BUNDLE_NAME")) + bundle = unescapeFilePath(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"); + project->values("QMAKE_BUNDLE_RESOURCE_FILE").append(project->first("DESTDIR") + bundle + "/Contents/Resources/empty.lproj"); + } else if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") && + ((!project->isActiveConfig("plugin") && project->isActiveConfig("lib_bundle")) || + (project->isActiveConfig("plugin") && project->isActiveConfig("plugin_bundle")))) { + bundle = unescapeFilePath(project->first("TARGET")); + if(project->isActiveConfig("plugin")) { + if(!project->isEmpty("QMAKE_PLUGIN_BUNDLE_NAME")) + bundle = unescapeFilePath(project->first("QMAKE_PLUGIN_BUNDLE_NAME")); + if(!project->isEmpty("QMAKE_BUNDLE_EXTENSION") && !bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION"))) + bundle += project->first("QMAKE_BUNDLE_EXTENSION"); + else if(!bundle.endsWith(".plugin")) + bundle += ".plugin"; + if(project->isEmpty("QMAKE_BUNDLE_LOCATION")) + project->values("QMAKE_BUNDLE_LOCATION").append("Contents/MacOS"); + } else { + if(!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME")) + bundle = unescapeFilePath(project->first("QMAKE_FRAMEWORK_BUNDLE_NAME")); + if(!project->isEmpty("QMAKE_BUNDLE_EXTENSION") && !bundle.endsWith(project->first("QMAKE_BUNDLE_EXTENSION"))) + bundle += project->first("QMAKE_BUNDLE_EXTENSION"); + else if(!bundle.endsWith(".framework")) + bundle += ".framework"; + } + } + if(!bundle.isEmpty()) { + project->values("QMAKE_BUNDLE") = QStringList(bundle); + project->values("ALL_DEPS") += project->first("QMAKE_PKGINFO"); + project->values("ALL_DEPS") += project->first("QMAKE_BUNDLE_RESOURCE_FILE"); + } 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(); + } + + if(!project->isEmpty("QMAKE_INTERNAL_INCLUDED_FILES")) + project->values("DISTFILES") += project->values("QMAKE_INTERNAL_INCLUDED_FILES"); + project->values("DISTFILES") += project->projectFile(); + + init2(); + project->values("QMAKE_INTERNAL_PRL_LIBS") << "QMAKE_LIBDIR_FLAGS" << "QMAKE_FRAMEWORKPATH_FLAGS" << "QMAKE_LIBS"; + if(!project->isEmpty("QMAKE_MAX_FILES_PER_AR")) { + bool ok; + int max_files = project->first("QMAKE_MAX_FILES_PER_AR").toInt(&ok); + QStringList ar_sublibs, objs = project->values("OBJECTS"); + if(ok && max_files > 5 && max_files < (int)objs.count()) { + QString lib; + for(int i = 0, obj_cnt = 0, lib_cnt = 0; i != objs.size(); ++i) { + if((++obj_cnt) >= max_files) { + if(lib_cnt) { + lib.sprintf("lib%s-tmp%d.a", + project->first("QMAKE_ORIG_TARGET").toLatin1().constData(), lib_cnt); + ar_sublibs << lib; + obj_cnt = 0; + } + lib_cnt++; + } + } + } + if(!ar_sublibs.isEmpty()) { + project->values("QMAKE_AR_SUBLIBS") = ar_sublibs; + project->values("QMAKE_INTERNAL_PRL_LIBS") << "QMAKE_AR_SUBLIBS"; + } + } + + if(project->isActiveConfig("compile_libtool")) { + const QString libtoolify[] = { "QMAKE_RUN_CC", "QMAKE_RUN_CC_IMP", + "QMAKE_RUN_CXX", "QMAKE_RUN_CXX_IMP", + "QMAKE_LINK_THREAD", "QMAKE_LINK", "QMAKE_AR_CMD", "QMAKE_LINK_SHLIB_CMD", + QString() }; + for(int i = 0; !libtoolify[i].isNull(); i++) { + QStringList &l = project->values(libtoolify[i]); + if(!l.isEmpty()) { + QString libtool_flags, comp_flags; + if(libtoolify[i].startsWith("QMAKE_LINK") || libtoolify[i] == "QMAKE_AR_CMD") { + libtool_flags += " --mode=link"; + if(project->isActiveConfig("staticlib")) { + libtool_flags += " -static"; + } else { + if(!project->isEmpty("QMAKE_LIB_FLAG")) { + int maj = project->first("VER_MAJ").toInt(); + int min = project->first("VER_MIN").toInt(); + int pat = project->first("VER_PAT").toInt(); + comp_flags += " -version-info " + QString::number(10*maj + min) + + ":" + QString::number(pat) + ":0"; + if(libtoolify[i] != "QMAKE_AR_CMD") { + QString rpath = Option::output_dir; + if(!project->isEmpty("DESTDIR")) { + rpath = project->first("DESTDIR"); + if(QDir::isRelativePath(rpath)) + rpath.prepend(Option::output_dir + Option::dir_sep); + } + comp_flags += " -rpath " + Option::fixPathToTargetOS(rpath, false); + } + } + } + if(project->isActiveConfig("plugin")) + libtool_flags += " -module"; + } else { + libtool_flags += " --mode=compile"; + } + l.first().prepend("$(LIBTOOL)" + libtool_flags + " "); + if(!comp_flags.isEmpty()) + l.first() += comp_flags; + } + } + } +} + +void +UnixMakefileGenerator::processPrlVariable(const QString &var, const QStringList &l) +{ + if(var == "QMAKE_PRL_LIBS") { + project->values("QMAKE_CURRENT_PRL_LIBS") += l; + } else + MakefileGenerator::processPrlVariable(var, l); +} + +QStringList +&UnixMakefileGenerator::findDependencies(const QString &file) +{ + QStringList &ret = MakefileGenerator::findDependencies(file); + // Note: The QMAKE_IMAGE_COLLECTION file have all images + // as dependency, so don't add precompiled header then + if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER") + && file != project->first("QMAKE_IMAGE_COLLECTION")) { + QString header_prefix; + if(!project->isEmpty("PRECOMPILED_DIR")) + header_prefix = project->first("PRECOMPILED_DIR"); + header_prefix += project->first("QMAKE_ORIG_TARGET"); + if (!project->isActiveConfig("clang_pch_style")) + header_prefix += project->first("QMAKE_PCH_OUTPUT_EXT"); + if (project->isActiveConfig("icc_pch_style")) { + // icc style + for(QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) { + if(file.endsWith(*it)) { + ret += header_prefix; + break; + } + } + } else { + // gcc style (including clang_pch_style) + QString header_suffix = project->isActiveConfig("clang_pch_style") + ? project->first("QMAKE_PCH_OUTPUT_EXT") : ""; + header_prefix += Option::dir_sep + project->first("QMAKE_PRECOMP_PREFIX"); + for(QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) { + if(file.endsWith(*it)) { + if(!project->isEmpty("QMAKE_CFLAGS_PRECOMPILE")) { + QString precomp_c_h = header_prefix + "c" + header_suffix; + if(!ret.contains(precomp_c_h)) + ret += precomp_c_h; + } + if(project->isActiveConfig("objective_c")) { + if(!project->isEmpty("QMAKE_OBJCFLAGS_PRECOMPILE")) { + QString precomp_objc_h = header_prefix + "objective-c" + header_suffix; + if(!ret.contains(precomp_objc_h)) + ret += precomp_objc_h; + } + if(!project->isEmpty("QMAKE_OBJCXXFLAGS_PRECOMPILE")) { + QString precomp_objcpp_h = header_prefix + "objective-c++" + header_suffix; + if(!ret.contains(precomp_objcpp_h)) + ret += precomp_objcpp_h; + } + } + break; + } + } + for(QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) { + if(file.endsWith(*it)) { + if(!project->isEmpty("QMAKE_CXXFLAGS_PRECOMPILE")) { + QString precomp_cpp_h = header_prefix + "c++" + header_suffix; + if(!ret.contains(precomp_cpp_h)) + ret += precomp_cpp_h; + } + if(project->isActiveConfig("objective_c")) { + if(!project->isEmpty("QMAKE_OBJCXXFLAGS_PRECOMPILE")) { + QString precomp_objcpp_h = header_prefix + "objective-c++" + header_suffix; + if(!ret.contains(precomp_objcpp_h)) + ret += precomp_objcpp_h; + } + } + break; + } + } + } + } + return ret; +} + +bool +UnixMakefileGenerator::findLibraries() +{ + QList libdirs, frameworkdirs; + frameworkdirs.append(QMakeLocalFileName("/System/Library/Frameworks")); + frameworkdirs.append(QMakeLocalFileName("/Library/Frameworks")); + const QString lflags[] = { "QMAKE_LIBDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS", "QMAKE_LFLAGS", "QMAKE_LIBS", "QMAKE_LIBS_PRIVATE", QString() }; + for(int i = 0; !lflags[i].isNull(); i++) { + QStringList &l = project->values(lflags[i]); + for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + bool do_suffix = true; + QString stub, dir, extn, opt = (*it).trimmed(); + if(opt.startsWith("-")) { + if(opt.startsWith("-L")) { + QMakeLocalFileName f(opt.right(opt.length()-2)); + if(!libdirs.contains(f)) + libdirs.append(f); + } else if(opt.startsWith("-l")) { + if (!project->isEmpty("QMAKE_RVCT_LINKSTYLE")) { + (*it) = opt.mid(2); + } else if (project->isActiveConfig("rvct_linker") || project->isActiveConfig("armcc_linker")) { + (*it) = "lib" + opt.mid(2) + ".so"; + } else { + stub = opt.mid(2); + } + } else if(Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-F")) { + frameworkdirs.append(QMakeLocalFileName(opt.right(opt.length()-2))); + } else if(Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-framework")) { + if(opt.length() > 11) { + opt = opt.mid(11); + } else { + ++it; + opt = (*it); + } + do_suffix = false; + extn = ""; + dir = "/System/Library/Frameworks/" + opt + ".framework/"; + stub = opt; + } + } else { + extn = dir = ""; + stub = opt; + int slsh = opt.lastIndexOf(Option::dir_sep); + if(slsh != -1) { + dir = opt.left(slsh); + stub = opt.mid(slsh+1); + } + QRegExp stub_reg("^.*lib(" + stub + "[^./=]*)\\.(.*)$"); + if(stub_reg.exactMatch(stub)) { + stub = stub_reg.cap(1); + extn = stub_reg.cap(2); + } + } + if(!stub.isEmpty()) { + if(do_suffix && !project->isEmpty("QMAKE_" + stub.toUpper() + "_SUFFIX")) + stub += project->first("QMAKE_" + stub.toUpper() + "_SUFFIX"); + bool found = false; + QStringList extens; + if(!extn.isNull()) + extens << extn; + else if (!project->isEmpty("QMAKE_SYMBIAN_SHLIB")) + // In Symbian you link to the stub .lib file, but run with the .dll file. + extens << "lib"; + else + extens << project->values("QMAKE_EXTENSION_SHLIB").first() << "a"; + for(QStringList::Iterator extit = extens.begin(); extit != extens.end(); ++extit) { + if(dir.isNull()) { + for(QList::Iterator dep_it = libdirs.begin(); dep_it != libdirs.end(); ++dep_it) { + QString pathToLib = ((*dep_it).local() + Option::dir_sep + + project->values("QMAKE_PREFIX_SHLIB").first() + + stub + "." + (*extit)); + if(exists(pathToLib)) { + if (!project->isEmpty("QMAKE_RVCT_LINKSTYLE")) + (*it) = pathToLib; + else + (*it) = "-l" + stub; + found = true; + break; + } + } + } else { + if(exists(project->values("QMAKE_PREFIX_SHLIB").first() + stub + "." + (*extit))) { + (*it) = project->values("QMAKE_PREFIX_SHLIB").first() + stub + "." + (*extit); + found = true; + break; + } + } + } + if(!found && project->isActiveConfig("compile_libtool")) { + for(int dep_i = 0; dep_i < libdirs.size(); ++dep_i) { + if(exists(libdirs[dep_i].local() + Option::dir_sep + project->values("QMAKE_PREFIX_SHLIB").first() + stub + Option::libtool_ext)) { + (*it) = libdirs[dep_i].real() + Option::dir_sep + project->values("QMAKE_PREFIX_SHLIB").first() + stub + Option::libtool_ext; + found = true; + break; + } + } + } + } + } + } + return false; +} + +QString linkLib(const QString &file, const QString &libName) { + QString ret; + QRegExp reg("^.*lib(" + QRegExp::escape(libName) + "[^./=]*).*$"); + if(reg.exactMatch(file)) + ret = "-l" + reg.cap(1); + return ret; +} + +void +UnixMakefileGenerator::processPrlFiles() +{ + QList libdirs, frameworkdirs; + frameworkdirs.append(QMakeLocalFileName("/System/Library/Frameworks")); + const QString lflags[] = { "QMAKE_LIBDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS", "QMAKE_LFLAGS", "QMAKE_LIBS", QString() }; + for(int i = 0; !lflags[i].isNull(); i++) { + QStringList &l = project->values(lflags[i]); + for(int lit = 0; lit < l.size(); ++lit) { + QString opt = l.at(lit).trimmed(); + if(opt.startsWith("-")) { + if(opt.startsWith("-L")) { + QMakeLocalFileName l(opt.right(opt.length()-2)); + if(!libdirs.contains(l)) + libdirs.append(l); + } else if(opt.startsWith("-l")) { + QString lib = opt.right(opt.length() - 2); + for(int dep_i = 0; dep_i < libdirs.size(); ++dep_i) { + const QMakeLocalFileName &lfn = libdirs[dep_i]; + if(!project->isActiveConfig("compile_libtool")) { //give them the .libs.. + QString la = lfn.local() + Option::dir_sep + project->values("QMAKE_PREFIX_SHLIB").first() + lib + Option::libtool_ext; + if(exists(la) && QFile::exists(lfn.local() + Option::dir_sep + ".libs")) { + QString dot_libs = lfn.real() + Option::dir_sep + ".libs"; + l.append("-L" + dot_libs); + libdirs.append(QMakeLocalFileName(dot_libs)); + } + } + + QString prl = lfn.local() + Option::dir_sep + project->values("QMAKE_PREFIX_SHLIB").first() + lib; + if(!project->isEmpty("QMAKE_" + lib.toUpper() + "_SUFFIX")) + prl += project->first("QMAKE_" + lib.toUpper() + "_SUFFIX"); + if(processPrlFile(prl)) { + if(prl.startsWith(lfn.local())) + prl.replace(0, lfn.local().length(), lfn.real()); + opt = linkLib(prl, lib); + break; + } + } + } else if(Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-F")) { + QMakeLocalFileName f(opt.right(opt.length()-2)); + if(!frameworkdirs.contains(f)) + frameworkdirs.append(f); + } else if(Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-framework")) { + if(opt.length() > 11) + opt = opt.mid(11); + else + opt = l.at(++lit); + opt = opt.trimmed(); + const QList dirs = frameworkdirs + libdirs; + for(int dep_i = 0; dep_i < dirs.size(); ++dep_i) { + QString prl = dirs[dep_i].local() + "/" + opt + ".framework/" + opt + Option::prl_ext; + if(processPrlFile(prl)) + break; + } + } + } else if(!opt.isNull()) { + QString lib = opt; + processPrlFile(lib); +#if 0 + if(ret) + opt = linkLib(lib, ""); +#endif + if(!opt.isEmpty()) + l.replaceInStrings(lib, opt); + } + + QStringList &prl_libs = project->values("QMAKE_CURRENT_PRL_LIBS"); + if(!prl_libs.isEmpty()) { + for(int prl = 0; prl < prl_libs.size(); ++prl) + l.insert(lit+prl+1, prl_libs.at(prl)); + prl_libs.clear(); + } + } + + //merge them into a logical order + if(!project->isActiveConfig("no_smart_library_merge") && !project->isActiveConfig("no_lflags_merge")) { + QHash lflags; + for(int lit = 0; lit < l.size(); ++lit) { + QString arch("default"); + QString opt = l.at(lit).trimmed(); + if(opt.startsWith("-")) { + if (Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-Xarch")) { + if (opt.length() > 7) { + arch = opt.mid(7); + opt = l.at(++lit); + } + } + + if(opt.startsWith("-L") || + (Option::target_mode == Option::TARG_MACX_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 + if (lflags[arch].contains(opt)) + lflags[arch].removeAll(opt); + lflags[arch].append(opt); + } else if(Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-framework")) { + if(opt.length() > 11) + opt = opt.mid(11); + else { + opt = l.at(++lit); + if (Option::target_mode == Option::TARG_MACX_MODE && opt.startsWith("-Xarch")) + opt = l.at(++lit); // The user has done the right thing and prefixed each part + } + bool found = false; + for(int x = 0; x < lflags[arch].size(); ++x) { + QString xf = lflags[arch].at(x); + if(xf.startsWith("-framework")) { + QString framework; + if(xf.length() > 11) + framework = xf.mid(11); + else + framework = lflags[arch].at(++x); + if(framework == opt) { + found = true; + break; + } + } + } + if(!found) { + lflags[arch].append("-framework"); + lflags[arch].append(opt); + } + } else { + lflags[arch].append(opt); + } + } else if(!opt.isNull()) { + if(!lflags[arch].contains(opt)) + lflags[arch].append(opt); + } + } + + l = lflags.take("default"); + + // Process architecture specific options (Xarch) + QHash::const_iterator archIterator = lflags.constBegin(); + while (archIterator != lflags.constEnd()) { + const QStringList archOptions = archIterator.value(); + for (int i = 0; i < archOptions.size(); ++i) { + l.append(QLatin1String("-Xarch_") + archIterator.key()); + l.append(archOptions.at(i)); + } + ++archIterator; + } + } + } +} + +QString +UnixMakefileGenerator::defaultInstall(const QString &t) +{ + if(t != "target" || project->first("TEMPLATE") == "subdirs") + return QString(); + + bool bundle = false; + const QString root = "$(INSTALL_ROOT)"; + QStringList &uninst = project->values(t + ".uninstall"); + QString ret, destdir=project->first("DESTDIR"); + QString targetdir = Option::fixPathToTargetOS(project->first("target.path"), false); + if(!destdir.isEmpty() && destdir.right(1) != Option::dir_sep) + destdir += Option::dir_sep; + targetdir = fileFixify(targetdir, FileFixifyAbsolute); + if(targetdir.right(1) != Option::dir_sep) + targetdir += Option::dir_sep; + + QStringList links; + QString target="$(TARGET)"; + QStringList &targets = project->values(t + ".targets"); + if(!project->isEmpty("QMAKE_BUNDLE")) { + target = project->first("QMAKE_BUNDLE"); + bundle = true; + } else if(project->first("TEMPLATE") == "app") { + target = "$(QMAKE_TARGET)"; + } else if(project->first("TEMPLATE") == "lib") { + if(project->isEmpty("QMAKE_CYGWIN_SHLIB")) { + if(!project->isActiveConfig("staticlib") && !project->isActiveConfig("plugin")) { + 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), + dst = filePrefixRoot(root, targetdir + src.section('/', -1)); + if(!ret.isEmpty()) + ret += "\n\t"; + ret += "-$(INSTALL_FILE) \"" + src + "\" \"" + dst + "\""; + if(!uninst.isEmpty()) + uninst.append("\n\t"); + uninst.append("-$(DEL_FILE) \"" + dst + "\""); + } + + if(!bundle && project->isActiveConfig("compile_libtool")) { + QString src_targ = target; + if(src_targ == "$(TARGET)") + src_targ = "$(TARGETL)"; + QString dst_dir = fileFixify(targetdir, FileFixifyAbsolute); + if(QDir::isRelativePath(dst_dir)) + dst_dir = Option::fixPathToTargetOS(Option::output_dir + Option::dir_sep + dst_dir); + ret = "-$(LIBTOOL) --mode=install cp \"" + src_targ + "\" \"" + filePrefixRoot(root, dst_dir) + "\""; + uninst.append("-$(LIBTOOL) --mode=uninstall \"" + src_targ + "\""); + } else { + QString src_targ = target; + if(!destdir.isEmpty()) + src_targ = Option::fixPathToTargetOS(destdir + target, false); + QString dst_targ = filePrefixRoot(root, fileFixify(targetdir + target, FileFixifyAbsolute)); + if(bundle) { + if(!ret.isEmpty()) + ret += "\n\t"; + ret += "$(DEL_FILE) -r \"" + dst_targ + "\"\n\t"; + } + if(!ret.isEmpty()) + ret += "\n\t"; + + QString copy_cmd("-"); + if (bundle) + copy_cmd += "$(INSTALL_DIR)"; + else if (project->first("TEMPLATE") == "lib" && project->isActiveConfig("staticlib")) + copy_cmd += "$(INSTALL_FILE)"; + else + copy_cmd += "$(INSTALL_PROGRAM)"; + copy_cmd += " \"" + src_targ + "\" \"" + dst_targ + "\""; + if(project->first("TEMPLATE") == "lib" && !project->isActiveConfig("staticlib") + && project->values(t + ".CONFIG").indexOf("fix_rpath") != -1) { + 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 { + ret += copy_cmd; + } + + 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") && !project->isActiveConfig("nostrip") && !project->isEmpty("QMAKE_STRIP")) { + ret += "\n\t-$(STRIP)"; + if(project->first("TEMPLATE") == "lib" && !project->isEmpty("QMAKE_STRIPFLAGS_LIB")) + ret += " " + var("QMAKE_STRIPFLAGS_LIB"); + else if(project->first("TEMPLATE") == "app" && !project->isEmpty("QMAKE_STRIPFLAGS_APP")) + ret += " " + var("QMAKE_STRIPFLAGS_APP"); + if(bundle) + ret = " \"" + dst_targ + "/Contents/MacOS/$(QMAKE_TARGET)\""; + else + ret += " \"" + dst_targ + "\""; + } + if(!uninst.isEmpty()) + uninst.append("\n\t"); + if(bundle) + uninst.append("-$(DEL_FILE) -r \"" + dst_targ + "\""); + else + uninst.append("-$(DEL_FILE) \"" + dst_targ + "\""); + if(!links.isEmpty()) { + for(int i = 0; i < links.size(); ++i) { + if(Option::target_mode == Option::TARG_UNIX_MODE || + Option::target_mode == Option::TARG_MACX_MODE) { + QString link = Option::fixPathToTargetOS(destdir + links[i], false); + int lslash = link.lastIndexOf(Option::dir_sep); + if(lslash != -1) + link = link.right(link.length() - (lslash + 1)); + QString dst_link = 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(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") && !project->isActiveConfig("compile_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) \"" + dst_meta + "\""); + const QString replace_rule("QMAKE_" + type.toUpper() + "_INSTALL_REPLACE"); + 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); + } + QString install_meta = "$(INSTALL_FILE) \"" + src_meta + "\" \"" + dst_meta + "\""; + if(project->isEmpty(replace_rule) || project->isActiveConfig("no_sed_meta_install")) { + if(!ret.isEmpty()) + ret += "\n\t"; + ret += "-" + install_meta; + } else { + if(!ret.isEmpty()) + ret += "\n\t"; + ret += "-$(SED)"; + QStringList replace_rules = project->values(replace_rule); + for(int r = 0; r < replace_rules.size(); ++r) { + const QString match = project->first(replace_rules.at(r) + ".match"), + replace = project->first(replace_rules.at(r) + ".replace"); + if(!match.isEmpty() /*&& match != replace*/) + ret += " -e \"s," + match + "," + replace + ",g\""; + } + ret += " \"" + src_meta + "\" >\"" + dst_meta + "\""; + //ret += " || " + install_meta; + } + } + } + } + return ret; +} + +QString +UnixMakefileGenerator::escapeFilePath(const QString &path) const +{ + QString ret = path; + if(!ret.isEmpty()) { + ret = unescapeFilePath(ret).replace(QLatin1Char(' '), QLatin1String("\\ ")); + debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData()); + } + return ret; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/unix/unixmake.h b/qmake/generators/unix/unixmake.h new file mode 100644 index 0000000000..acbe8c4a6b --- /dev/null +++ b/qmake/generators/unix/unixmake.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef UNIXMAKE_H +#define UNIXMAKE_H + +#include "makefile.h" + +QT_BEGIN_NAMESPACE + +class UnixMakefileGenerator : public MakefileGenerator +{ + bool init_flag, include_deps; + QString libtoolFileName(bool fixify=true); + void writeLibtoolFile(); // for libtool + void writePrlFile(QTextStream &); + +public: + UnixMakefileGenerator(); + ~UnixMakefileGenerator(); + +protected: + virtual bool doPrecompiledHeaders() const { return project->isActiveConfig("precompile_header"); } + virtual bool doDepends() const { return !include_deps && !Option::mkfile::do_stub_makefile && MakefileGenerator::doDepends(); } + virtual QString defaultInstall(const QString &); + virtual void processPrlVariable(const QString &, const QStringList &); + virtual void processPrlFiles(); + + virtual bool findLibraries(); + virtual QString escapeFilePath(const QString &path) const; + virtual QStringList &findDependencies(const QString &); + virtual void init(); + + void writeMakeParts(QTextStream &); + bool writeMakefile(QTextStream &); + +private: + void init2(); +}; + +inline UnixMakefileGenerator::~UnixMakefileGenerator() +{ } + +QT_END_NAMESPACE + +#endif // UNIXMAKE_H diff --git a/qmake/generators/unix/unixmake2.cpp b/qmake/generators/unix/unixmake2.cpp new file mode 100644 index 0000000000..9f144929ed --- /dev/null +++ b/qmake/generators/unix/unixmake2.cpp @@ -0,0 +1,1352 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "unixmake.h" +#include "option.h" +#include "meta.h" +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +UnixMakefileGenerator::UnixMakefileGenerator() : MakefileGenerator(), init_flag(false), include_deps(false) +{ + +} + +void +UnixMakefileGenerator::writePrlFile(QTextStream &t) +{ + MakefileGenerator::writePrlFile(t); + // libtool support + + if(project->isActiveConfig("create_libtool") && project->first("TEMPLATE") == "lib") { //write .la + if(project->isActiveConfig("compile_libtool")) + warn_msg(WarnLogic, "create_libtool specified with compile_libtool can lead to conflicting .la\n" + "formats, create_libtool has been disabled\n"); + else + writeLibtoolFile(); + } + // pkg-config support + if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib") + writePkgConfigFile(); +} + +bool +UnixMakefileGenerator::writeMakefile(QTextStream &t) +{ + + writeHeader(t); + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + t << "QMAKE = " << var("QMAKE_QMAKE") << endl; + QStringList &qut = project->values("QMAKE_EXTRA_TARGETS"); + for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it) + t << *it << " "; + t << "first all clean install distclean uninstall qmake_all:" << "\n\t" + << "@echo \"Some of the required modules (" + << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t" + << "@echo \"Skipped.\"" << endl << endl; + writeMakeQmake(t); + if(project->isEmpty("QMAKE_NOFORCE")) + t << "FORCE:" << endl << endl; + return true; + } + + if (project->values("TEMPLATE").first() == "app" || + project->values("TEMPLATE").first() == "lib") { + if(Option::mkfile::do_stub_makefile && MakefileGenerator::writeStubMakefile(t)) + return true; + writeMakeParts(t); + return MakefileGenerator::writeMakefile(t); + } else if(project->values("TEMPLATE").first() == "subdirs") { + MakefileGenerator::writeSubDirs(t); + return true; + } + return false; +} + +void +UnixMakefileGenerator::writeMakeParts(QTextStream &t) +{ + QString deps = fileFixify(Option::output.fileName()), target_deps, prl; + bool do_incremental = (project->isActiveConfig("incremental") && + !project->values("QMAKE_INCREMENTAL").isEmpty() && + (!project->values("QMAKE_APP_FLAG").isEmpty() || + (!project->isActiveConfig("staticlib")))), + src_incremental=false; + + t << "####### Compiler, tools and options" << endl << endl; + t << "CC = " << var("QMAKE_CC") << endl; + t << "CXX = " << var("QMAKE_CXX") << endl; + t << "DEFINES = " + << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ") + << varGlue("DEFINES","-D"," -D","") << endl; + t << "CFLAGS = " << var("QMAKE_CFLAGS") << " $(DEFINES)" << endl; + t << "CXXFLAGS = " << var("QMAKE_CXXFLAGS") << " $(DEFINES)" << endl; + t << "INCPATH = " << "-I" << specdir(); + if(!project->isActiveConfig("no_include_pwd")) { + QString pwd = escapeFilePath(fileFixify(qmake_getpwd())); + if(pwd.isEmpty()) + pwd = "."; + t << " -I" << pwd; + } + { + const QStringList &incs = project->values("INCLUDEPATH"); + for(int i = 0; i < incs.size(); ++i) { + QString inc = escapeFilePath(incs.at(i)); + if(!inc.isEmpty()) + t << " " << "-I" << inc; + } + } + if(!project->isEmpty("QMAKE_FRAMEWORKPATH_FLAGS")) + t << " " << var("QMAKE_FRAMEWORKPATH_FLAGS"); + t << endl; + + if(!project->isActiveConfig("staticlib")) { + t << "LINK = " << var("QMAKE_LINK") << endl; + t << "LFLAGS = " << var("QMAKE_LFLAGS") << endl; + t << "LIBS = " << "$(SUBLIBS) " << var("QMAKE_FRAMEWORKPATH_FLAGS") << " " + << var("QMAKE_LIBDIR_FLAGS") << " " << var("QMAKE_LIBS") << " " << var("QMAKE_LIBS_PRIVATE") << endl; + } + + t << "AR = " << var("QMAKE_AR") << endl; + t << "RANLIB = " << var("QMAKE_RANLIB") << endl; + t << "QMAKE = " << var("QMAKE_QMAKE") << endl; + t << "TAR = " << var("QMAKE_TAR") << endl; + t << "COMPRESS = " << var("QMAKE_GZIP") << endl; + if(project->isActiveConfig("compile_libtool")) + t << "LIBTOOL = " << var("QMAKE_LIBTOOL") << endl; + t << "COPY = " << var("QMAKE_COPY") << endl; + t << "SED = " << var("QMAKE_STREAM_EDITOR") << endl; + t << "COPY_FILE = " << var("QMAKE_COPY_FILE") << endl; + t << "COPY_DIR = " << var("QMAKE_COPY_DIR") << endl; + t << "STRIP = " << var("QMAKE_STRIP") << endl; + t << "INSTALL_FILE = " << var("QMAKE_INSTALL_FILE") << endl; + t << "INSTALL_DIR = " << var("QMAKE_INSTALL_DIR") << endl; + t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl; + + t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl; + t << "SYMLINK = " << var("QMAKE_SYMBOLIC_LINK") << endl; + t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl; + t << "MOVE = " << var("QMAKE_MOVE") << endl; + t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl; + t << "MKDIR = " << var("QMAKE_MKDIR") << endl; + if(!project->isEmpty("QMAKE_MACOSX_DEPLOYMENT_TARGET")) + t << "export MACOSX_DEPLOYMENT_TARGET = " //exported to children processes + << project->first("QMAKE_MACOSX_DEPLOYMENT_TARGET") << endl; + + if (!project->isEmpty("QMAKE_SYMBIAN_SHLIB")) { + t << "vpath %.dso " << project->values("QMAKE_LIBDIR").join(":") << endl; + t << "vpath %.lib " << project->values("QMAKE_LIBDIR").join(":") << endl; + } + + t << endl; + + t << "####### Output directory" << endl << endl; + if (! project->values("OBJECTS_DIR").isEmpty()) + t << "OBJECTS_DIR = " << var("OBJECTS_DIR") << endl; + else + t << "OBJECTS_DIR = ./" << endl; + t << endl; + + /* files */ + t << "####### Files" << endl << endl; + t << "SOURCES = " << valList(escapeFilePaths(project->values("SOURCES"))) << " " + << valList(escapeFilePaths(project->values("GENERATED_SOURCES"))) << endl; + if(do_incremental) { + QStringList &objs = project->values("OBJECTS"), &incrs = project->values("QMAKE_INCREMENTAL"), incrs_out; + t << "OBJECTS = "; + for(QStringList::Iterator objit = objs.begin(); objit != objs.end(); ++objit) { + bool increment = false; + for(QStringList::Iterator incrit = incrs.begin(); incrit != incrs.end(); ++incrit) { + if((*objit).indexOf(QRegExp((*incrit), Qt::CaseSensitive, + QRegExp::Wildcard)) != -1) { + increment = true; + incrs_out.append((*objit)); + break; + } + } + if(!increment) + t << "\\\n\t\t" << (*objit); + } + if(incrs_out.count() == objs.count()) { //we just switched places, no real incrementals to be done! + t << escapeFilePaths(incrs_out).join(" \\\n\t\t") << endl; + } else if(!incrs_out.count()) { + t << endl; + } else { + src_incremental = true; + t << endl; + t << "INCREMENTAL_OBJECTS = " << escapeFilePaths(incrs_out).join(" \\\n\t\t") << endl; + } + } else { + t << "OBJECTS = " << valList(escapeFilePaths(project->values("OBJECTS"))) << endl; + } + if(do_incremental && !src_incremental) + do_incremental = false; + t << "DIST = " << valList(fileFixify(project->values("DISTFILES"))) << endl; + t << "QMAKE_TARGET = " << var("QMAKE_ORIG_TARGET") << endl; + t << "DESTDIR = " << var("DESTDIR") << endl; + if(project->isActiveConfig("compile_libtool")) + t << "TARGETL = " << var("TARGET_la") << endl; + t << "TARGET = " << escapeFilePath(var("TARGET")) << endl; + if(project->isActiveConfig("plugin")) { + t << "TARGETD = " << escapeFilePath(var("TARGET")) << endl; + } else if(!project->isActiveConfig("staticlib") && project->values("QMAKE_APP_FLAG").isEmpty()) { + t << "TARGETA = " << escapeFilePath(var("TARGETA")) << endl; + if(!project->isEmpty("QMAKE_BUNDLE")) { + t << "TARGETD = " << escapeFilePath(var("TARGET_x.y")) << endl; + t << "TARGET0 = " << escapeFilePath(var("TARGET_")) << endl; + } else if(!project->isEmpty("QMAKE_SYMBIAN_SHLIB")) { + t << "TARGETD = " << escapeFilePath(var("TARGET")) << endl; + } else if(project->isEmpty("QMAKE_HPUX_SHLIB")) { + t << "TARGETD = " << escapeFilePath(var("TARGET_x.y.z")) << endl; + t << "TARGET0 = " << escapeFilePath(var("TARGET_")) << endl; + t << "TARGET1 = " << escapeFilePath(var("TARGET_x")) << endl; + t << "TARGET2 = " << escapeFilePath(var("TARGET_x.y")) << endl; + } else { + t << "TARGETD = " << escapeFilePath(var("TARGET_x")) << endl; + t << "TARGET0 = " << escapeFilePath(var("TARGET_")) << endl; + } + } + writeExtraCompilerVariables(t); + writeExtraVariables(t); + t << endl; + + // blasted includes + QStringList &qeui = project->values("QMAKE_EXTRA_INCLUDES"); + QStringList::Iterator it; + for(it = qeui.begin(); it != qeui.end(); ++it) + t << "include " << (*it) << endl; + + /* rules */ + t << "first: all" << endl; + t << "####### Implicit rules" << endl << endl; + t << ".SUFFIXES: " << Option::obj_ext; + 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 << endl << endl; + 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") << endl << 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") << endl << endl; + + if(include_deps) { + QString cmd=var("QMAKE_CFLAGS_DEPS") + " "; + cmd += varGlue("DEFINES","-D"," -D","") + varGlue("PRL_EXPORT_DEFINES"," -D"," -D",""); + if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH")) + cmd += " -I" + project->first("QMAKE_ABSOLUTE_SOURCE_PATH") + " "; + cmd += " $(INCPATH) " + varGlue("DEPENDPATH", "-I", " -I", ""); + QString odir; + if(!project->values("OBJECTS_DIR").isEmpty()) + odir = project->first("OBJECTS_DIR"); + t << "###### Dependencies" << endl << endl; + t << odir << ".deps/%.d: %.cpp\n\t"; + if(project->isActiveConfig("echo_depend_creation")) + t << "@echo Creating depend for $<" << "\n\t"; + t << mkdir_p_asstring("$(@D)") << "\n\t" + << "@$(CXX) " << cmd << " $< | sed \"s,^\\($(*F).o\\):," << odir << "\\1:,g\" >$@" << endl << endl; + + t << odir << ".deps/%.d: %.c\n\t"; + if(project->isActiveConfig("echo_depend_creation")) + t << "@echo Creating depend for $<" << "\n\t"; + t << mkdir_p_asstring("$(@D)") << "\n\t" + << "@$(CC) " << cmd << " $< | sed \"s,^\\($(*F).o\\):," << odir << "\\1:,g\" >$@" << endl << endl; + + QString src[] = { "SOURCES", "GENERATED_SOURCES", QString() }; + for(int x = 0; !src[x].isNull(); x++) { + QStringList &l = project->values(src[x]); + for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + if(!(*it).isEmpty()) { + QString d_file; + for(QStringList::Iterator cit = Option::c_ext.begin(); + cit != Option::c_ext.end(); ++cit) { + if((*it).endsWith((*cit))) { + d_file = (*it).left((*it).length() - (*cit).length()); + break; + } + } + if(d_file.isEmpty()) { + for(QStringList::Iterator cppit = Option::cpp_ext.begin(); + cppit != Option::cpp_ext.end(); ++cppit) { + if((*it).endsWith((*cppit))) { + d_file = (*it).left((*it).length() - (*cppit).length()); + break; + } + } + } + if(!d_file.isEmpty()) { + d_file = odir + ".deps/" + d_file + ".d"; + QStringList deps = findDependencies((*it)).filter(QRegExp(Option::cpp_moc_ext + "$")); + if(!deps.isEmpty()) + t << d_file << ": " << deps.join(" ") << endl; + t << "-include " << d_file << endl; + project->values("QMAKE_DISTCLEAN") += d_file; + } + } + } + } + } + + t << "####### Build rules" << endl << endl; + if(!project->values("SUBLIBS").isEmpty()) { + QString libdir = "tmp/"; + if(!project->isEmpty("SUBLIBS_DIR")) + libdir = project->first("SUBLIBS_DIR"); + t << "SUBLIBS = "; + QStringList &l = project->values("SUBLIBS"); + for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) + t << libdir << project->first("QMAKE_PREFIX_STATICLIB") << (*it) << "." + << project->first("QMAKE_EXTENSION_STATICLIB") << " "; + t << endl << endl; + } + if(project->isActiveConfig("depend_prl") && !project->isEmpty("QMAKE_PRL_INTERNAL_FILES")) { + QStringList &l = project->values("QMAKE_PRL_INTERNAL_FILES"); + QStringList::Iterator it; + for(it = l.begin(); it != l.end(); ++it) { + QMakeMetaInfo libinfo; + if(libinfo.readLib((*it)) && !libinfo.isEmpty("QMAKE_PRL_BUILD_DIR")) { + QString dir; + int slsh = (*it).lastIndexOf(Option::dir_sep); + if(slsh != -1) + dir = (*it).left(slsh + 1); + QString targ = dir + libinfo.first("QMAKE_PRL_TARGET"); + target_deps += " " + targ; + t << targ << ":" << "\n\t" + << "@echo \"Creating '" << targ << "'\"" << "\n\t" + << "(cd " << libinfo.first("QMAKE_PRL_BUILD_DIR") << ";" + << "$(MAKE))" << endl; + } + } + } + if(!project->values("QMAKE_APP_FLAG").isEmpty()) { + QString destdir = project->first("DESTDIR"); + if(!project->isEmpty("QMAKE_BUNDLE")) { + QString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION"); + if(!bundle_loc.isEmpty() && !bundle_loc.startsWith("/")) + bundle_loc.prepend("/"); + if(!bundle_loc.endsWith("/")) + bundle_loc += "/"; + destdir += project->first("QMAKE_BUNDLE") + bundle_loc; + } + if(do_incremental) { + //incremental target + QString incr_target = var("TARGET") + "_incremental"; + if(incr_target.indexOf(Option::dir_sep) != -1) + incr_target = incr_target.right(incr_target.length() - + (incr_target.lastIndexOf(Option::dir_sep) + 1)); + QString incr_deps, incr_objs; + if(project->first("QMAKE_INCREMENTAL_STYLE") == "ld") { + QString incr_target_dir = var("OBJECTS_DIR") + incr_target + Option::obj_ext; + //actual target + t << incr_target_dir << ": $(OBJECTS)" << "\n\t" + << "ld -r -o "<< incr_target_dir << " $(OBJECTS)" << endl; + //communicated below + deps.prepend(incr_target_dir + " "); + incr_deps = "$(INCREMENTAL_OBJECTS)"; + if(!incr_objs.isEmpty()) + incr_objs += " "; + incr_objs += incr_target_dir; + } else { + //actual target + QString incr_target_dir = var("DESTDIR") + "lib" + incr_target + "." + + project->values("QMAKE_EXTENSION_SHLIB").first(); + QString incr_lflags = var("QMAKE_LFLAGS_SHLIB") + " "; + if(project->isActiveConfig("debug")) + incr_lflags += var("QMAKE_LFLAGS_DEBUG"); + else + incr_lflags += var("QMAKE_LFLAGS_RELEASE"); + t << incr_target_dir << ": $(INCREMENTAL_OBJECTS)" << "\n\t"; + if(!destdir.isEmpty()) + t << "\n\t" << mkdir_p_asstring(destdir) << "\n\t"; + t << "$(LINK) " << incr_lflags << " -o "<< incr_target_dir << + " $(INCREMENTAL_OBJECTS)" << endl; + //communicated below + if(!destdir.isEmpty()) { + if(!incr_objs.isEmpty()) + incr_objs += " "; + incr_objs += "-L" + destdir; + } else { + if(!incr_objs.isEmpty()) + incr_objs += " "; + incr_objs += "-L" + qmake_getpwd(); + } + if(!incr_objs.isEmpty()) + incr_objs += " "; + incr_objs += " -l" + incr_target; + deps.prepend(incr_target_dir + " "); + incr_deps = "$(OBJECTS)"; + } + t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << "$(TARGET)" + << endl << endl; + + //real target + t << var("TARGET") << ": " << var("PRE_TARGETDEPS") << " " << incr_deps << " " << target_deps + << " " << var("POST_TARGETDEPS") << "\n\t"; + if(!destdir.isEmpty()) + t << "\n\t" << mkdir_p_asstring(destdir) << "\n\t"; + if(!project->isEmpty("QMAKE_PRE_LINK")) + t << var("QMAKE_PRE_LINK") << "\n\t"; + t << "$(LINK) $(LFLAGS) -o $(TARGET) " << incr_deps << " " << incr_objs << " $(OBJCOMP) $(LIBS)"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } else { + t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << "$(TARGET)" + << endl << endl; + + t << "$(TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) " + << target_deps << " " << var("POST_TARGETDEPS") << "\n\t"; + if(!destdir.isEmpty()) + t << mkdir_p_asstring(destdir) << "\n\t"; + if(!project->isEmpty("QMAKE_PRE_LINK")) + t << var("QMAKE_PRE_LINK") << "\n\t"; + t << "$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS)"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } + } else if(!project->isActiveConfig("staticlib")) { + QString destdir = unescapeFilePath(project->first("DESTDIR")), incr_deps; + if(!project->isEmpty("QMAKE_BUNDLE")) { + QString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION"); + if(!bundle_loc.isEmpty() && !bundle_loc.startsWith("/")) + bundle_loc.prepend("/"); + if(!bundle_loc.endsWith("/")) + bundle_loc += "/"; + destdir += project->first("QMAKE_BUNDLE") + bundle_loc; + } + destdir = escapeFilePath(destdir); + + if(do_incremental) { + QString s_ext = project->values("QMAKE_EXTENSION_SHLIB").first(); + QString incr_target = var("QMAKE_ORIG_TARGET").replace( + QRegExp("\\." + s_ext), "").replace(QRegExp("^lib"), "") + "_incremental"; + if(incr_target.indexOf(Option::dir_sep) != -1) + incr_target = incr_target.right(incr_target.length() - + (incr_target.lastIndexOf(Option::dir_sep) + 1)); + incr_target = escapeFilePath(incr_target); + + if(project->first("QMAKE_INCREMENTAL_STYLE") == "ld") { + QString incr_target_dir = escapeFilePath(var("OBJECTS_DIR") + incr_target + Option::obj_ext); + //actual target + const QString link_deps = "$(OBJECTS) "; + t << incr_target_dir << ": " << link_deps << "\n\t" + << "ld -r -o " << incr_target_dir << " " << link_deps << endl; + //communicated below + QStringList &cmd = project->values("QMAKE_LINK_SHLIB_CMD"); + cmd.first().replace("$(OBJECTS) ", "$(INCREMENTAL_OBJECTS)"); //ick + cmd.append(incr_target_dir); + deps.prepend(incr_target_dir + " "); + incr_deps = "$(INCREMENTAL_OBJECTS)"; + } else { + //actual target + QString incr_target_dir = escapeFilePath(destdir + "lib" + incr_target + "." + s_ext); + QString incr_lflags = var("QMAKE_LFLAGS_SHLIB") + " "; + if(!project->isEmpty("QMAKE_LFLAGS_INCREMENTAL")) + incr_lflags += var("QMAKE_LFLAGS_INCREMENTAL") + " "; + if(project->isActiveConfig("debug")) + incr_lflags += var("QMAKE_LFLAGS_DEBUG"); + else + incr_lflags += var("QMAKE_LFLAGS_RELEASE"); + t << incr_target_dir << ": $(INCREMENTAL_OBJECTS)" << "\n\t"; + if(!destdir.isEmpty()) + t << mkdir_p_asstring(destdir) << "\n\t"; + t << "$(LINK) " << incr_lflags << " -o "<< incr_target_dir << + " $(INCREMENTAL_OBJECTS)" << endl; + //communicated below + QStringList &cmd = project->values("QMAKE_LINK_SHLIB_CMD"); + if(!destdir.isEmpty()) + cmd.append(" -L" + destdir); + cmd.append(" -l" + incr_target); + deps.prepend(incr_target_dir + " "); + incr_deps = "$(OBJECTS)"; + } + + t << "all: " << " " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") + << " " << destdir << "$(TARGET)" << endl << endl; + + //real target + t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") << " " + << incr_deps << " $(SUBLIBS) " << target_deps << " " << var("POST_TARGETDEPS"); + } else { + t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << " " << + destdir << "$(TARGET)" << endl << endl; + t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") + << " $(OBJECTS) $(SUBLIBS) $(OBJCOMP) " << target_deps + << " " << var("POST_TARGETDEPS"); + } + if(!destdir.isEmpty()) + t << "\n\t" << mkdir_p_asstring(destdir); + if(!project->isEmpty("QMAKE_PRE_LINK")) + t << "\n\t" << var("QMAKE_PRE_LINK"); + + if(project->isActiveConfig("compile_libtool")) { + t << "\n\t" + << var("QMAKE_LINK_SHLIB_CMD"); + } else if(project->isActiveConfig("plugin")) { + t << "\n\t" + << "-$(DEL_FILE) $(TARGET)" << "\n\t" + << var("QMAKE_LINK_SHLIB_CMD"); + if(!destdir.isEmpty()) + t << "\n\t" + << "-$(MOVE) $(TARGET) " << destdir; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } else if(!project->isEmpty("QMAKE_BUNDLE")) { + t << "\n\t" + << "-$(DEL_FILE) $(TARGET) $(TARGET0) $(DESTDIR)$(TARGET0)" << "\n\t" + << var("QMAKE_LINK_SHLIB_CMD") << "\n\t" + << mkdir_p_asstring("\"`dirname $(DESTDIR)$(TARGETD)`\"", false) << "\n\t" + << "-$(MOVE) $(TARGET) $(DESTDIR)$(TARGETD)" << "\n\t" + << mkdir_p_asstring("\"`dirname $(DESTDIR)$(TARGET0)`\"", false) << "\n\t" + << varGlue("QMAKE_LN_SHLIB","-"," "," Versions/" + + project->first("QMAKE_FRAMEWORK_VERSION") + + "/$(TARGET) $(DESTDIR)$(TARGET0)") << "\n\t" + << "-$(DEL_FILE) " << destdir << "Versions/Current" << "\n\t" + << varGlue("QMAKE_LN_SHLIB","-"," ", " " + project->first("QMAKE_FRAMEWORK_VERSION") + + " " + destdir + "Versions/Current") << "\n\t"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } else if(!project->isEmpty("QMAKE_SYMBIAN_SHLIB")) { + t << "\n\t" + << "-$(DEL_FILE) $(TARGET)" << "\n\t" + << var("QMAKE_LINK_SHLIB_CMD"); + if(!destdir.isEmpty()) + t << "\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET)\n\t" + << "-$(MOVE) $(TARGET) " << destdir; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } else if(project->isEmpty("QMAKE_HPUX_SHLIB")) { + t << "\n\t" + << "-$(DEL_FILE) $(TARGET) $(TARGET0) $(TARGET1) $(TARGET2)" << "\n\t" + << var("QMAKE_LINK_SHLIB_CMD") << "\n\t"; + t << varGlue("QMAKE_LN_SHLIB","-"," "," $(TARGET) $(TARGET0)") << "\n\t" + << varGlue("QMAKE_LN_SHLIB","-"," "," $(TARGET) $(TARGET1)") << "\n\t" + << varGlue("QMAKE_LN_SHLIB","-"," "," $(TARGET) $(TARGET2)"); + if(!destdir.isEmpty()) + t << "\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET)\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET0)\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET1)\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET2)\n\t" + << "-$(MOVE) $(TARGET) $(TARGET0) $(TARGET1) $(TARGET2) " << destdir; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } else { + t << "\n\t" + << "-$(DEL_FILE) $(TARGET) $(TARGET0)" << "\n\t" + << var("QMAKE_LINK_SHLIB_CMD") << "\n\t"; + t << varGlue("QMAKE_LN_SHLIB",""," "," $(TARGET) $(TARGET0)"); + if(!destdir.isEmpty()) + t << "\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET)\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET0)\n\t" + << "-$(MOVE) $(TARGET) $(TARGET0) " << destdir; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } + t << endl << endl; + + if (! project->isActiveConfig("plugin")) { + t << "staticlib: $(TARGETA)" << endl << endl; + t << "$(TARGETA): " << var("PRE_TARGETDEPS") << " $(OBJECTS) $(OBJCOMP)"; + if(do_incremental) + t << " $(INCREMENTAL_OBJECTS)"; + t << " " << var("POST_TARGETDEPS") << "\n\t" + << "-$(DEL_FILE) $(TARGETA) " << "\n\t" + << var("QMAKE_AR_CMD"); + if(do_incremental) + t << " $(INCREMENTAL_OBJECTS)"; + if(!project->isEmpty("QMAKE_RANLIB")) + t << "\n\t" << "$(RANLIB) $(TARGETA)"; + t << endl << endl; + } + } else { + QString destdir = project->first("DESTDIR"); + t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << destdir << "$(TARGET) " + << varGlue("QMAKE_AR_SUBLIBS", destdir, " " + destdir, "") << "\n\n" + << "staticlib: " << destdir << "$(TARGET)" << "\n\n"; + if(project->isEmpty("QMAKE_AR_SUBLIBS")) { + t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") + << " $(OBJECTS) $(OBJCOMP) " << var("POST_TARGETDEPS") << "\n\t"; + if(!destdir.isEmpty()) + t << mkdir_p_asstring(destdir) << "\n\t"; + t << "-$(DEL_FILE) $(TARGET)" << "\n\t" + << var("QMAKE_AR_CMD") << "\n"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\t" << var("QMAKE_POST_LINK") << "\n"; + if(!project->isEmpty("QMAKE_RANLIB")) + t << "\t" << "$(RANLIB) $(TARGET)" << "\n"; + if(!destdir.isEmpty()) + t << "\t" << "-$(DEL_FILE) " << destdir << "$(TARGET)" << "\n" + << "\t" << "-$(MOVE) $(TARGET) " << destdir << "\n"; + } else { + int max_files = project->first("QMAKE_MAX_FILES_PER_AR").toInt(); + QStringList objs = project->values("OBJECTS") + project->values("OBJCOMP"), + libs = project->values("QMAKE_AR_SUBLIBS"); + libs.prepend("$(TARGET)"); + for(QStringList::Iterator libit = libs.begin(), objit = objs.begin(); + libit != libs.end(); ++libit) { + QStringList build; + for(int cnt = 0; cnt < max_files && objit != objs.end(); ++objit, cnt++) + build << (*objit); + QString ar; + if((*libit) == "$(TARGET)") { + t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") + << " " << var("POST_TARGETDEPS") << valList(build) << "\n\t"; + ar = project->values("QMAKE_AR_CMD").first(); + ar = ar.replace("$(OBJECTS)", build.join(" ")); + } else { + t << (*libit) << ": " << valList(build) << "\n\t"; + ar = "$(AR) " + (*libit) + " " + build.join(" "); + } + if(!destdir.isEmpty()) + t << mkdir_p_asstring(destdir) << "\n\t"; + t << "-$(DEL_FILE) " << (*libit) << "\n\t" + << ar << "\n"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\t" << var("QMAKE_POST_LINK") << "\n"; + if(!project->isEmpty("QMAKE_RANLIB")) + t << "\t" << "$(RANLIB) " << (*libit) << "\n"; + if(!destdir.isEmpty()) + t << "\t" << "-$(DEL_FILE) " << destdir << (*libit) << "\n" + << "\t" << "-$(MOVE) " << (*libit) << " " << destdir << "\n"; + } + } + t << endl << endl; + } + + writeMakeQmake(t); + if(project->isEmpty("QMAKE_FAILED_REQUIREMENTS") && !project->isActiveConfig("no_autoqmake")) { + QString meta_files; + if(project->isActiveConfig("create_libtool") && project->first("TEMPLATE") == "lib" && + !project->isActiveConfig("compile_libtool")) { //libtool + if(!meta_files.isEmpty()) + meta_files += " "; + meta_files += libtoolFileName(); + } + if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib") { //pkg-config + if(!meta_files.isEmpty()) + meta_files += " "; + meta_files += pkgConfigFileName(); + } + if(!meta_files.isEmpty()) + t << escapeDependencyPath(meta_files) << ": " << "\n\t" + << "@$(QMAKE) -prl " << buildArgs() << " " << project->projectFile() << endl; + } + + if(!project->first("QMAKE_PKGINFO").isEmpty()) { + QString pkginfo = escapeFilePath(project->first("QMAKE_PKGINFO")); + QString destdir = escapeFilePath(project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents"); + t << pkginfo << ": " << "\n\t"; + if(!destdir.isEmpty()) + t << mkdir_p_asstring(destdir) << "\n\t"; + t << "@$(DEL_FILE) " << pkginfo << "\n\t" + << "@echo \"APPL" + << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) + << "\" >" << pkginfo << endl; + } + if(!project->first("QMAKE_BUNDLE_RESOURCE_FILE").isEmpty()) { + QString resources = escapeFilePath(project->first("QMAKE_BUNDLE_RESOURCE_FILE")); + QString destdir = escapeFilePath(project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents/Resources"); + t << resources << ": " << "\n\t"; + t << mkdir_p_asstring(destdir) << "\n\t"; + t << "@touch " << resources << "\n\t" << endl; + } + if(!project->isEmpty("QMAKE_BUNDLE")) { + //copy the plist + QString info_plist = escapeFilePath(fileFixify(project->first("QMAKE_INFO_PLIST"))), + info_plist_out = escapeFilePath(project->first("QMAKE_INFO_PLIST_OUT")); + QString destdir = info_plist_out.section(Option::dir_sep, 0, -2); + t << info_plist_out << ": " << "\n\t"; + if(!destdir.isEmpty()) + t << mkdir_p_asstring(destdir) << "\n\t"; + QStringList commonSedArgs; + if (!project->values("VERSION").isEmpty()) + commonSedArgs << "-e \"s,@SHORT_VERSION@," << project->first("VER_MAJ") << "." << project->first("VER_MIN") << ",g\" "; + commonSedArgs << "-e \"s,@TYPEINFO@,"<< (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? + QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" "; + if(project->first("TEMPLATE") == "app") { + QString icon = fileFixify(var("ICON")); + t << "@$(DEL_FILE) " << info_plist_out << "\n\t" + << "@sed "; + foreach (const QString &arg, commonSedArgs) + t << arg; + t << "-e \"s,@ICON@," << icon.section(Option::dir_sep, -1) << ",g\" " + << "-e \"s,@EXECUTABLE@," << var("QMAKE_ORIG_TARGET") << ",g\" " + << "-e \"s,@TYPEINFO@,"<< (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? + QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" " + << "" << info_plist << " >" << info_plist_out << endl; + //copy the icon + if(!project->isEmpty("ICON")) { + QString dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents/Resources/"; + const QString icon_path = escapeFilePath(dir + icon.section(Option::dir_sep, -1)); + t << icon_path << ": " << icon << "\n\t" + << mkdir_p_asstring(dir) << "\n\t" + << "@$(DEL_FILE) " << icon_path << "\n\t" + << "@$(COPY_FILE) " << escapeFilePath(icon) << " " << icon_path << endl; + } + } else { + t << "@$(DEL_FILE) " << info_plist_out << "\n\t" + << "@sed "; + foreach (const QString &arg, commonSedArgs) + t << arg; + t << "-e \"s,@LIBRARY@," << var("QMAKE_ORIG_TARGET") << ",g\" " + << "-e \"s,@TYPEINFO@," + << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? + QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" " + << "" << info_plist << " >" << info_plist_out << endl; + } + //copy other data + if(!project->isEmpty("QMAKE_BUNDLE_DATA")) { + QString bundle_dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/"; + const QStringList &bundle_data = project->values("QMAKE_BUNDLE_DATA"); + for(int i = 0; i < bundle_data.count(); i++) { + const QStringList &files = project->values(bundle_data[i] + ".files"); + QString path = bundle_dir; + if(!project->isEmpty(bundle_data[i] + ".version")) { + QString version = project->first(bundle_data[i] + ".version") + "/" + + project->first("QMAKE_FRAMEWORK_VERSION") + "/"; + t << Option::fixPathToLocalOS(path + project->first(bundle_data[i] + ".path")) << ": " << "\n\t" + << mkdir_p_asstring(path) << "\n\t" + << "@$(SYMLINK) " << version << project->first(bundle_data[i] + ".path") << " " << path << endl; + path += version; + } + path += project->first(bundle_data[i] + ".path"); + path = Option::fixPathToLocalOS(path); + for(int file = 0; file < files.count(); file++) { + QString src = fileFixify(files[file], FileFixifyAbsolute); + if (!QFile::exists(src)) + src = files[file]; + src = escapeFilePath(src); + const QString dst = escapeFilePath(path + Option::dir_sep + fileInfo(files[file]).fileName()); + t << dst << ": " << src << "\n\t" + << mkdir_p_asstring(path) << "\n\t"; + QFileInfo fi(fileInfo(files[file])); + if(fi.isDir()) + t << "@$(DEL_FILE) -r " << dst << "\n\t" + << "@$(COPY_DIR) " << src << " " << dst << endl; + else + t << "@$(DEL_FILE) " << dst << "\n\t" + << "@$(COPY_FILE) " << src << " " << dst << endl; + } + } + } + } + + QString ddir; + QString packageName(project->first("QMAKE_ORIG_TARGET")); + if(!project->isActiveConfig("no_dist_version")) + packageName += var("VERSION"); + if (project->isEmpty("QMAKE_DISTDIR")) + ddir = packageName; + else + ddir = project->first("QMAKE_DISTDIR"); + + QString ddir_c = escapeFilePath(fileFixify((project->isEmpty("OBJECTS_DIR") ? QString(".tmp/") : + project->first("OBJECTS_DIR")) + ddir, + Option::output_dir, Option::output_dir)); + t << "dist: " << "\n\t" + << mkdir_p_asstring(ddir_c) << "\n\t" + << "$(COPY_FILE) --parents $(SOURCES) $(DIST) " << ddir_c << Option::dir_sep << " && "; + if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) { + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + const QStringList &var = project->values((*it)+".input"); + for(QStringList::ConstIterator var_it = var.begin(); var_it != var.end(); ++var_it) { + const QStringList &val = project->values((*var_it)); + if(val.isEmpty()) + continue; + t << "$(COPY_FILE) --parents " << val.join(" ") << " " << ddir_c << Option::dir_sep << " && "; + } + } + } + if(!project->isEmpty("TRANSLATIONS")) + t << "$(COPY_FILE) --parents " << var("TRANSLATIONS") << " " << ddir_c << Option::dir_sep << " && "; + t << "(cd `dirname " << ddir_c << "` && " + << "$(TAR) " << packageName << ".tar " << ddir << " && " + << "$(COMPRESS) " << packageName << ".tar) && " + << "$(MOVE) `dirname " << ddir_c << "`" << Option::dir_sep << packageName << ".tar.gz . && " + << "$(DEL_FILE) -r " << ddir_c + << endl << endl; + + t << endl; + + QString clean_targets = "compiler_clean " + var("CLEAN_DEPS"); + if(do_incremental) { + t << "incrclean:" << "\n"; + if(src_incremental) + t << "\t-$(DEL_FILE) $(INCREMENTAL_OBJECTS)" << "\n"; + t << endl; + } + + t << "clean:" << clean_targets << "\n\t"; + if(!project->isEmpty("OBJECTS")) { + if(project->isActiveConfig("compile_libtool")) + t << "-$(LIBTOOL) --mode=clean $(DEL_FILE) $(OBJECTS)" << "\n\t"; + else + t << "-$(DEL_FILE) $(OBJECTS)" << "\n\t"; + } + if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) { + QStringList precomp_files; + QString precomph_out_dir; + + if(!project->isEmpty("PRECOMPILED_DIR")) + precomph_out_dir = project->first("PRECOMPILED_DIR"); + precomph_out_dir += project->first("QMAKE_ORIG_TARGET"); + if (!project->isActiveConfig("clang_pch_style")) + precomph_out_dir += project->first("QMAKE_PCH_OUTPUT_EXT"); + + if (project->isActiveConfig("icc_pch_style")) { + // icc style + QString pchBaseName = project->first("QMAKE_ORIG_TARGET"); + QString pchOutput; + if(!project->isEmpty("PRECOMPILED_DIR")) + pchOutput = project->first("PRECOMPILED_DIR"); + pchOutput += pchBaseName + project->first("QMAKE_PCH_OUTPUT_EXT"); + QString sourceFile = pchOutput + Option::cpp_ext.first(); + QString objectFile = createObjectList(QStringList(sourceFile)).first(); + + precomp_files << precomph_out_dir << sourceFile << objectFile; + } else { + // gcc style (including clang_pch_style) + precomph_out_dir += Option::dir_sep; + + QString header_prefix = project->first("QMAKE_PRECOMP_PREFIX"); + QString header_suffix = project->isActiveConfig("clang_pch_style") + ? project->first("QMAKE_PCH_OUTPUT_EXT") : ""; + + if(!project->isEmpty("QMAKE_CFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "c" + header_suffix; + if(!project->isEmpty("QMAKE_CXXFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "c++" + header_suffix; + if(project->isActiveConfig("objective_c")) { + if(!project->isEmpty("QMAKE_OBJCFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "objective-c" + header_suffix; + if(!project->isEmpty("QMAKE_OBJCXXFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "objective-c++" + header_suffix; + } + } + t << "-$(DEL_FILE) " << precomp_files.join(" ") << "\n\t"; + } + if(!project->isEmpty("IMAGES")) + t << varGlue("QMAKE_IMAGE_COLLECTION", "\t-$(DEL_FILE) ", " ", "") << "\n\t"; + if(src_incremental) + t << "-$(DEL_FILE) $(INCREMENTAL_OBJECTS)" << "\n\t"; + t << varGlue("QMAKE_CLEAN","-$(DEL_FILE) "," ","\n\t") + << "-$(DEL_FILE) *~ core *.core" << "\n" + << varGlue("CLEAN_FILES","\t-$(DEL_FILE) "," ","") << endl << endl; + t << "####### Sub-libraries" << endl << endl; + if (!project->values("SUBLIBS").isEmpty()) { + QString libdir = "tmp/"; + if(!project->isEmpty("SUBLIBS_DIR")) + libdir = project->first("SUBLIBS_DIR"); + QStringList &l = project->values("SUBLIBS"); + for(it = l.begin(); it != l.end(); ++it) + t << libdir << project->first("QMAKE_PREFIX_STATICLIB") << (*it) << "." + << project->first("QMAKE_EXTENSION_STATICLIB") << ":\n\t" + << var(QString("MAKELIB") + (*it)) << endl << endl; + } + + QString destdir = project->first("DESTDIR"); + if(!destdir.isEmpty() && destdir.right(1) != Option::dir_sep) + destdir += Option::dir_sep; + t << "distclean: " << "clean\n"; + if(!project->isEmpty("QMAKE_BUNDLE")) { + QString bundlePath = escapeFilePath(destdir + project->first("QMAKE_BUNDLE")); + t << "\t-$(DEL_FILE) -r " << bundlePath << endl; + } else if(project->isActiveConfig("compile_libtool")) { + t << "\t-$(LIBTOOL) --mode=clean $(DEL_FILE) " << "$(TARGET)" << endl; + } else if(!project->isActiveConfig("staticlib") && project->values("QMAKE_APP_FLAG").isEmpty() && + !project->isActiveConfig("plugin")) { + t << "\t-$(DEL_FILE) " << destdir << "$(TARGET)" << " " << endl; + if (project->values("QMAKE_SYMBIAN_SHLIB").isEmpty()) + t << "\t-$(DEL_FILE) " << destdir << "$(TARGET0) " << destdir << "$(TARGET1) " + << destdir << "$(TARGET2) $(TARGETA)" << endl; + } else { + t << "\t-$(DEL_FILE) " << "$(TARGET)" << " " << endl; + } + t << varGlue("QMAKE_DISTCLEAN","\t-$(DEL_FILE) "," ","\n"); + { + QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName())); + if(!ofile.isEmpty()) + t << "\t-$(DEL_FILE) " << ofile << endl; + } + t << endl << endl; + + if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) { + QString pchInput = project->first("PRECOMPILED_HEADER"); + t << "###### Prefix headers" << endl; + QString comps[] = { "C", "CXX", "OBJC", "OBJCXX", QString() }; + for(int i = 0; !comps[i].isNull(); i++) { + QString pchFlags = var("QMAKE_" + comps[i] + "FLAGS_PRECOMPILE"); + if(pchFlags.isEmpty()) + continue; + + QString cflags; + if(comps[i] == "OBJC" || comps[i] == "OBJCXX") + cflags += " $(CFLAGS)"; + else + cflags += " $(" + comps[i] + "FLAGS)"; + + QString pchBaseName = project->first("QMAKE_ORIG_TARGET"); + QString pchOutput; + if(!project->isEmpty("PRECOMPILED_DIR")) + pchOutput = project->first("PRECOMPILED_DIR"); + pchOutput += pchBaseName; + if (!project->isActiveConfig("clang_pch_style")) + pchOutput += project->first("QMAKE_PCH_OUTPUT_EXT"); + + if (project->isActiveConfig("icc_pch_style")) { + // icc style + QString sourceFile = pchOutput + Option::cpp_ext.first(); + QString objectFile = createObjectList(QStringList(sourceFile)).first(); + t << pchOutput << ": " << pchInput << " " << findDependencies(pchInput).join(" \\\n\t\t") + << "\n\techo \"// Automatically generated, do not modify\" > " << sourceFile + << "\n\trm -f " << pchOutput; + + pchFlags = pchFlags.replace("${QMAKE_PCH_TEMP_SOURCE}", sourceFile) + .replace("${QMAKE_PCH_TEMP_OBJECT}", objectFile); + } else { + // gcc style (including clang_pch_style) + QString header_prefix = project->first("QMAKE_PRECOMP_PREFIX"); + QString header_suffix = project->isActiveConfig("clang_pch_style") + ? project->first("QMAKE_PCH_OUTPUT_EXT") : ""; + pchOutput += Option::dir_sep; + QString pchOutputDir = pchOutput, pchOutputFile; + + if(comps[i] == "C") { + pchOutputFile = "c"; + } else if(comps[i] == "CXX") { + pchOutputFile = "c++"; + } else if(project->isActiveConfig("objective_c")) { + if(comps[i] == "OBJC") + pchOutputFile = "objective-c"; + else if(comps[i] == "OBJCXX") + pchOutputFile = "objective-c++"; + } + if(pchOutputFile.isEmpty()) + continue; + pchOutput += header_prefix + pchOutputFile + header_suffix; + + t << pchOutput << ": " << pchInput << " " << findDependencies(pchInput).join(" \\\n\t\t") + << "\n\t" << mkdir_p_asstring(pchOutputDir); + } + pchFlags = pchFlags.replace("${QMAKE_PCH_INPUT}", pchInput) + .replace("${QMAKE_PCH_OUTPUT_BASE}", pchBaseName) + .replace("${QMAKE_PCH_OUTPUT}", pchOutput); + + QString compiler; + if(comps[i] == "C" || comps[i] == "OBJC" || comps[i] == "OBJCXX") + compiler = "$(CC)"; + else + compiler = "$(CXX)"; + + // compile command + t << "\n\t" << compiler << cflags << " $(INCPATH) " << pchFlags << endl << endl; + } + } + + writeExtraTargets(t); + writeExtraCompilerTargets(t); +} + +void UnixMakefileGenerator::init2() +{ + //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").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]); + if(project->isEmpty("QMAKE_FRAMEWORK_VERSION")) + project->values("QMAKE_FRAMEWORK_VERSION").append(project->values("VER_MAJ").first()); + + if (!project->values("QMAKE_APP_FLAG").isEmpty()) { + if(!project->isEmpty("QMAKE_BUNDLE")) { + QString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION"); + if(!bundle_loc.isEmpty() && !bundle_loc.startsWith("/")) + bundle_loc.prepend("/"); + if(!bundle_loc.endsWith("/")) + bundle_loc += "/"; + project->values("TARGET").first().prepend(project->first("QMAKE_BUNDLE") + bundle_loc); + } + if(!project->isEmpty("TARGET")) + project->values("TARGET").first().prepend(project->first("DESTDIR")); + if (!project->values("QMAKE_CYGWIN_EXE").isEmpty()) + project->values("TARGET_EXT").append(".exe"); + } else if (project->isActiveConfig("staticlib")) { + project->values("TARGET").first().prepend(project->first("QMAKE_PREFIX_STATICLIB")); + project->values("TARGET").first() += "." + project->first("QMAKE_EXTENSION_STATICLIB"); + if(project->values("QMAKE_AR_CMD").isEmpty()) + project->values("QMAKE_AR_CMD").append("$(AR) $(TARGET) $(OBJECTS)"); + } else { + project->values("TARGETA").append(project->first("DESTDIR") + project->first("QMAKE_PREFIX_STATICLIB") + + project->first("TARGET") + "." + project->first("QMAKE_EXTENSION_STATICLIB")); + if(project->isActiveConfig("compile_libtool")) + project->values("TARGET_la") = QStringList(project->first("DESTDIR") + "lib" + project->first("TARGET") + Option::libtool_ext); + + if (!project->values("QMAKE_AR_CMD").isEmpty()) + project->values("QMAKE_AR_CMD").first().replace("(TARGET)","(TARGETA)"); + else + project->values("QMAKE_AR_CMD").append("$(AR) $(TARGETA) $(OBJECTS)"); + if(project->isActiveConfig("compile_libtool")) { + project->values("TARGET") = project->values("TARGET_la"); + } else if(!project->isEmpty("QMAKE_BUNDLE")) { + QString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION"); + if(!bundle_loc.isEmpty() && !bundle_loc.startsWith("/")) + bundle_loc.prepend("/"); + if(!bundle_loc.endsWith("/")) + bundle_loc += "/"; + project->values("TARGET_").append(project->first("QMAKE_BUNDLE") + + bundle_loc + unescapeFilePath(project->first("TARGET"))); + project->values("TARGET_x.y").append(project->first("QMAKE_BUNDLE") + + "/Versions/" + + project->first("QMAKE_FRAMEWORK_VERSION") + + bundle_loc + unescapeFilePath(project->first("TARGET"))); + } else if(project->isActiveConfig("plugin")) { + QString prefix; + if(!project->isActiveConfig("no_plugin_name_prefix")) + prefix = "lib"; + project->values("TARGET_x.y.z").append(prefix + + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_PLUGIN")); + if(project->isActiveConfig("lib_version_first")) + project->values("TARGET_x").append(prefix + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("QMAKE_EXTENSION_PLUGIN")); + else + project->values("TARGET_x").append(prefix + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_PLUGIN") + + "." + project->first("VER_MAJ")); + project->values("TARGET") = project->values("TARGET_x.y.z"); + } else if (!project->isEmpty("QMAKE_HPUX_SHLIB")) { + project->values("TARGET_").append("lib" + project->first("TARGET") + ".sl"); + if(project->isActiveConfig("lib_version_first")) + project->values("TARGET_x").append("lib" + project->first("VER_MAJ") + "." + + project->first("TARGET")); + else + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ")); + project->values("TARGET") = project->values("TARGET_x"); + } else if (!project->isEmpty("QMAKE_AIX_SHLIB")) { + project->values("TARGET_").append(project->first("QMAKE_PREFIX_STATICLIB") + project->first("TARGET") + + "." + project->first("QMAKE_EXTENSION_STATICLIB")); + if(project->isActiveConfig("lib_version_first")) { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + + "." + project->first("VER_MIN") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + } else { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ") + + "." + project->first("VER_MIN")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT")); + } + project->values("TARGET") = project->values("TARGET_x.y.z"); + } else if (!project->isEmpty("QMAKE_SYMBIAN_SHLIB")) { + project->values("TARGET_").append(project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET") = project->values("TARGET_"); + } else { + project->values("TARGET_").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + if(project->isActiveConfig("lib_version_first")) { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + + "." + project->first("VER_MIN") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT") + "." + + project->values("QMAKE_EXTENSION_SHLIB").first()); + } else { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ") + + "." + project->first("VER_MIN")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + + "." + + project->values( + "QMAKE_EXTENSION_SHLIB").first() + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT")); + } + project->values("TARGET") = project->values("TARGET_x.y.z"); + } + if(project->isEmpty("QMAKE_LN_SHLIB")) + project->values("QMAKE_LN_SHLIB").append("ln -s"); + if (!project->values("QMAKE_LFLAGS_SONAME").isEmpty()) { + QString soname; + if(project->isActiveConfig("plugin")) { + if(!project->values("TARGET").isEmpty()) + soname += project->first("TARGET"); + } else if(!project->isEmpty("QMAKE_BUNDLE")) { + soname += project->first("TARGET_x.y"); + } else if(!project->values("TARGET_x").isEmpty()) { + soname += project->first("TARGET_x"); + } + if(!soname.isEmpty()) { + if(project->isActiveConfig("absolute_library_soname") && + project->values("INSTALLS").indexOf("target") != -1 && + !project->isEmpty("target.path")) { + QString instpath = Option::fixPathToTargetOS(project->first("target.path")); + if(!instpath.endsWith(Option::dir_sep)) + instpath += Option::dir_sep; + soname.prepend(instpath); + } + project->values("QMAKE_LFLAGS_SONAME").first() += escapeFilePath(soname); + } + } + if (project->values("QMAKE_LINK_SHLIB_CMD").isEmpty()) + project->values("QMAKE_LINK_SHLIB_CMD").append( + "$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS) $(OBJCOMP)"); + } + if (!project->values("QMAKE_APP_FLAG").isEmpty()) { + 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->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"); + if(project->isActiveConfig("plugin_with_soname") && !project->isActiveConfig("compile_libtool")) + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_SONAME"); + } else { + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_SHLIB"); + if(!project->isEmpty("QMAKE_LFLAGS_COMPAT_VERSION")) { + if(project->isEmpty("COMPAT_VERSION")) + project->values("QMAKE_LFLAGS") += QString(project->first("QMAKE_LFLAGS_COMPAT_VERSION") + + project->first("VER_MAJ") + "." + + project->first("VER_MIN")); + else + project->values("QMAKE_LFLAGS") += QString(project->first("QMAKE_LFLAGS_COMPAT_VERSION") + + project->first("COMPATIBILITY_VERSION")); + } + if(!project->isEmpty("QMAKE_LFLAGS_VERSION")) { + project->values("QMAKE_LFLAGS") += QString(project->first("QMAKE_LFLAGS_VERSION") + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT")); + } + if(!project->isActiveConfig("compile_libtool")) + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_SONAME"); + } + } + + if(!project->isEmpty("QMAKE_BUNDLE")) { + QString plist = fileFixify(project->first("QMAKE_INFO_PLIST")); + if(plist.isEmpty()) + plist = specdir() + QDir::separator() + "Info.plist." + project->first("TEMPLATE"); + if(exists(Option::fixPathToLocalOS(plist))) { + if(project->isEmpty("QMAKE_INFO_PLIST")) + project->values("QMAKE_INFO_PLIST").append(plist); + project->values("QMAKE_INFO_PLIST_OUT").append(project->first("DESTDIR") + + project->first("QMAKE_BUNDLE") + + "/Contents/Info.plist"); + project->values("ALL_DEPS") += project->first("QMAKE_INFO_PLIST_OUT"); + if(!project->isEmpty("ICON") && project->first("TEMPLATE") == "app") + project->values("ALL_DEPS") += project->first("DESTDIR") + + project->first("QMAKE_BUNDLE") + + "/Contents/Resources/" + project->first("ICON").section('/', -1); + if(!project->isEmpty("QMAKE_BUNDLE_DATA")) { + QString bundle_dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/"; + QStringList &alldeps = project->values("ALL_DEPS"); + const QStringList &bundle_data = project->values("QMAKE_BUNDLE_DATA"); + for(int i = 0; i < bundle_data.count(); i++) { + const QStringList &files = project->values(bundle_data[i] + ".files"); + QString path = bundle_dir; + if(!project->isEmpty(bundle_data[i] + ".version")) { + alldeps += Option::fixPathToLocalOS(path + Option::dir_sep + + project->first(bundle_data[i] + ".path")); + path += project->first(bundle_data[i] + ".version") + "/" + + project->first("QMAKE_FRAMEWORK_VERSION") + "/"; + } + path += project->first(bundle_data[i] + ".path"); + path = Option::fixPathToLocalOS(path); + for(int file = 0; file < files.count(); file++) + alldeps += path + Option::dir_sep + fileInfo(files[file]).fileName(); + } + } + } + } +} + +QString +UnixMakefileGenerator::libtoolFileName(bool fixify) +{ + QString ret = var("TARGET"); + int slsh = ret.lastIndexOf(Option::dir_sep); + if(slsh != -1) + ret = ret.right(ret.length() - slsh - 1); + int dot = ret.indexOf('.'); + if(dot != -1) + ret = ret.left(dot); + ret += Option::libtool_ext; + if(!project->isEmpty("QMAKE_LIBTOOL_DESTDIR")) + ret.prepend(project->first("QMAKE_LIBTOOL_DESTDIR") + Option::dir_sep); + if(fixify) { + if(QDir::isRelativePath(ret) && !project->isEmpty("DESTDIR")) + ret.prepend(project->first("DESTDIR")); + ret = Option::fixPathToLocalOS(fileFixify(ret, qmake_getpwd(), Option::output_dir)); + } + return ret; +} + +void +UnixMakefileGenerator::writeLibtoolFile() +{ + QString fname = libtoolFileName(), lname = fname; + mkdir(fileInfo(fname).path()); + int slsh = lname.lastIndexOf(Option::dir_sep); + if(slsh != -1) + lname = lname.right(lname.length() - slsh - 1); + QFile ft(fname); + if(!ft.open(QIODevice::WriteOnly)) + return; + project->values("ALL_DEPS").append(fileFixify(fname)); + + QTextStream t(&ft); + t << "# " << lname << " - a libtool library file\n"; + t << "# Generated by qmake/libtool (" << qmake_version() << ") (Qt " + << QT_VERSION_STR << ") on: " << QDateTime::currentDateTime().toString(); + t << "\n"; + + t << "# The name that we can dlopen(3).\n" + << "dlname='" << var(project->isActiveConfig("plugin") ? "TARGET" : "TARGET_x") + << "'\n\n"; + + t << "# Names of this library.\n"; + t << "library_names='"; + if(project->isActiveConfig("plugin")) { + t << var("TARGET"); + } else { + if (project->isEmpty("QMAKE_HPUX_SHLIB")) + t << var("TARGET_x.y.z") << " "; + t << var("TARGET_x") << " " << var("TARGET_"); + } + t << "'\n\n"; + + t << "# The name of the static archive.\n" + << "old_library='" << lname.left(lname.length()-Option::libtool_ext.length()) << ".a'\n\n"; + + t << "# Libraries that this one depends upon.\n"; + QStringList libs; + if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS")) + libs = project->values("QMAKE_INTERNAL_PRL_LIBS"); + else + libs << "QMAKE_LIBS"; //obvious one + t << "dependency_libs='"; + for(QStringList::ConstIterator it = libs.begin(); it != libs.end(); ++it) + t << project->values((*it)).join(" ") << " "; + t << "'\n\n"; + + t << "# Version information for " << lname << "\n"; + int maj = project->first("VER_MAJ").toInt(); + int min = project->first("VER_MIN").toInt(); + int pat = project->first("VER_PAT").toInt(); + t << "current=" << (10*maj + min) << "\n" // best I can think of + << "age=0\n" + << "revision=" << pat << "\n\n"; + + t << "# Is this an already installed library.\n" + "installed=yes\n\n"; // ### + + t << "# Files to dlopen/dlpreopen.\n" + "dlopen=''\n" + "dlpreopen=''\n\n"; + + QString install_dir = project->first("QMAKE_LIBTOOL_LIBDIR"); + if(install_dir.isEmpty()) + install_dir = project->first("target.path"); + if(install_dir.isEmpty()) + install_dir = project->first("DESTDIR"); + t << "# Directory that this library needs to be installed in:\n" + "libdir='" << Option::fixPathToTargetOS(install_dir, false) << "'\n"; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/borland_bmake.cpp b/qmake/generators/win32/borland_bmake.cpp new file mode 100644 index 0000000000..585a0f4982 --- /dev/null +++ b/qmake/generators/win32/borland_bmake.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "borland_bmake.h" +#include "option.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +BorlandMakefileGenerator::BorlandMakefileGenerator() : Win32MakefileGenerator(), init_flag(false) +{ + +} + +bool +BorlandMakefileGenerator::writeMakefile(QTextStream &t) +{ + writeHeader(t); + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + QStringList &qut = project->values("QMAKE_EXTRA_TARGETS"); + for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it) + t << *it << " "; + t << "all first clean:" << "\n\t" + << "@echo \"Some of the required modules (" + << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t" + << "@echo \"Skipped.\"" << endl << endl; + return true; + } + + if(project->first("TEMPLATE") == "app" || + project->first("TEMPLATE") == "lib") { + writeBorlandParts(t); + return MakefileGenerator::writeMakefile(t); + } + else if(project->first("TEMPLATE") == "subdirs") { + writeSubDirs(t); + return true; + } + return false; +} + +void +BorlandMakefileGenerator::writeBorlandParts(QTextStream &t) +{ + t << "!if !$d(BCB)" << endl; + t << "BCB = $(MAKEDIR)\\.." << endl; + t << "!endif" << endl << endl; + + writeStandardParts(t); +} + +void +BorlandMakefileGenerator::init() +{ + if(init_flag) + return; + init_flag = true; + + /* 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("QMAKE_COPY_FILE")) + project->values("QMAKE_COPY_FILE").append("$(COPY)"); + if(project->isEmpty("QMAKE_COPY_DIR")) + project->values("QMAKE_COPY_DIR").append("xcopy /s /q /y /i"); + if(project->isEmpty("QMAKE_INSTALL_FILE")) + project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_PROGRAM")) + project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_DIR")) + project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)"); + if(project->values("MAKEFILE").isEmpty()) + project->values("MAKEFILE").append("Makefile"); + return; + } + + processVars(); + + project->values("QMAKE_LIBS") += project->values("LIBS"); + + MakefileGenerator::init(); + + if (project->isActiveConfig("dll") || !project->values("QMAKE_APP_FLAG").isEmpty()) { + // bcc does not generate a .tds file for static libs + QString tdsPostfix; + if (!project->values("VERSION").isEmpty()) + tdsPostfix = project->first("TARGET_VERSION_EXT"); + tdsPostfix += ".tds"; + project->values("QMAKE_CLEAN").append(project->first("DESTDIR") + project->first("TARGET") + tdsPostfix); + } +} + +void BorlandMakefileGenerator::writeBuildRulesPart(QTextStream &t) +{ + t << "first: all" << endl; + t << "all: " << fileFixify(Option::output.fileName()) << " " << varGlue("ALL_DEPS"," "," "," ") << " $(DESTDIR_TARGET)" << endl << endl; + t << "$(DESTDIR_TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) " << var("POST_TARGETDEPS"); + if(!project->isEmpty("QMAKE_PRE_LINK")) + t << "\n\t" <isActiveConfig("staticlib")) { + t << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET)" + << "\n\t" << "$(LIB) $(DESTDIR_TARGET) @&&|" << " \n+" + << project->values("OBJECTS").join(" \\\n+") << " \\\n+" + << project->values("OBJMOC").join(" \\\n+"); + } else { + t << "\n\t" << "$(LINK) @&&|" << "\n\t" + << "$(LFLAGS) $(OBJECTS) $(OBJMOC),$(DESTDIR_TARGET),,$(LIBS),$(DEF_FILE),$(RES_FILE)"; + } + t << endl << "|"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" <isEmpty("IMAGES")) + t << varGlue("QMAKE_IMAGE_COLLECTION", "\n\t-$(DEL_FILE) ", "\n\t-$(DEL_FILE) ", ""); + t << endl; + + t << "distclean: clean" + << "\n\t-$(DEL_FILE) $(DESTDIR_TARGET)" + << endl << endl; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/borland_bmake.h b/qmake/generators/win32/borland_bmake.h new file mode 100644 index 0000000000..7a2734f5e3 --- /dev/null +++ b/qmake/generators/win32/borland_bmake.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BORLAND_BMAKE_H +#define BORLAND_BMAKE_H + +#include "winmakefile.h" + +QT_BEGIN_NAMESPACE + +class BorlandMakefileGenerator : public Win32MakefileGenerator +{ + bool init_flag; + void writeBorlandParts(QTextStream &); + void writeBuildRulesPart(QTextStream &t); + void writeCleanParts(QTextStream &t); + bool writeMakefile(QTextStream &); + void init(); + +public: + BorlandMakefileGenerator(); + ~BorlandMakefileGenerator(); +}; + +inline BorlandMakefileGenerator::~BorlandMakefileGenerator() +{ } + +QT_END_NAMESPACE + +#endif // BORLAND_BMAKE_H diff --git a/qmake/generators/win32/mingw_make.cpp b/qmake/generators/win32/mingw_make.cpp new file mode 100644 index 0000000000..462920e06c --- /dev/null +++ b/qmake/generators/win32/mingw_make.cpp @@ -0,0 +1,516 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mingw_make.h" +#include "option.h" +#include "meta.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +MingwMakefileGenerator::MingwMakefileGenerator() : Win32MakefileGenerator(), init_flag(false) +{ + if (Option::shellPath.isEmpty()) + quote = "\""; + else + quote = "'"; +} + +bool MingwMakefileGenerator::isWindowsShell() const +{ +#ifdef Q_OS_WIN + return Option::shellPath.isEmpty(); +#else + return Win32MakefileGenerator::isWindowsShell(); +#endif +} + +QString MingwMakefileGenerator::escapeDependencyPath(const QString &path) const +{ + QString ret = path; + ret.remove('\"'); + ret.replace('\\', "/"); + ret.replace(' ', "\\ "); + return ret; +} + +QString MingwMakefileGenerator::getLibTarget() +{ + return QString("lib" + project->first("TARGET") + project->first("TARGET_VERSION_EXT") + ".a"); +} + +bool MingwMakefileGenerator::findLibraries() +{ + return findLibraries("QMAKE_LIBS") && findLibraries("QMAKE_LIBS_PRIVATE"); +} + +bool MingwMakefileGenerator::findLibraries(const QString &where) +{ + QStringList &l = project->values(where); + + QList dirs; + { + QStringList &libpaths = project->values("QMAKE_LIBDIR"); + for(QStringList::Iterator libpathit = libpaths.begin(); + libpathit != libpaths.end(); ++libpathit) + dirs.append(QMakeLocalFileName((*libpathit))); + } + + QStringList::Iterator it = l.begin(); + while (it != l.end()) { + if ((*it).startsWith("-l")) { + QString steam = (*it).mid(2), out; + QString suffix; + if (!project->isEmpty("QMAKE_" + steam.toUpper() + "_SUFFIX")) + suffix = project->first("QMAKE_" + steam.toUpper() + "_SUFFIX"); + for (QList::Iterator dir_it = dirs.begin(); dir_it != dirs.end(); ++dir_it) { + QString extension; + int ver = findHighestVersion((*dir_it).local(), steam, "dll.a|a"); + if (ver != -1) + extension += QString::number(ver); + extension += suffix; + if(QMakeMetaInfo::libExists((*dir_it).local() + Option::dir_sep + steam) || + exists((*dir_it).local() + Option::dir_sep + steam + extension + ".a") || + exists((*dir_it).local() + Option::dir_sep + steam + extension + ".dll.a")) { + out = (*it) + extension; + break; + } + } + if (!out.isEmpty()) // We assume if it never finds it that its correct + (*it) = out; + } else if((*it).startsWith("-L")) { + dirs.append(QMakeLocalFileName((*it).mid(2))); + } + + ++it; + } + return true; +} + +bool MingwMakefileGenerator::writeMakefile(QTextStream &t) +{ + writeHeader(t); + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + t << "all clean:" << "\n\t" + << "@echo \"Some of the required modules (" + << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t" + << "@echo \"Skipped.\"" << endl << endl; + writeMakeQmake(t); + return true; + } + + if(project->first("TEMPLATE") == "app" || + project->first("TEMPLATE") == "lib") { + if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib") + writePkgConfigFile(); + + if(Option::mkfile::do_stub_makefile) { + t << "QMAKE = " << var("QMAKE_QMAKE") << endl; + QStringList &qut = project->values("QMAKE_EXTRA_TARGETS"); + for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it) + t << *it << " "; + t << "first all clean install distclean uninstall: qmake" << endl + << "qmake_all:" << endl; + writeMakeQmake(t); + if(project->isEmpty("QMAKE_NOFORCE")) + t << "FORCE:" << endl << endl; + return true; + } + writeMingwParts(t); + return MakefileGenerator::writeMakefile(t); + } + else if(project->first("TEMPLATE") == "subdirs") { + writeSubDirs(t); + return true; + } + return false; + } + +void createLdObjectScriptFile(const QString &fileName, const QStringList &objList) +{ + QString filePath = Option::output_dir + QDir::separator() + fileName; + QFile file(filePath); + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream t(&file); + t << "INPUT(" << endl; + for (QStringList::ConstIterator it = objList.constBegin(); it != objList.constEnd(); ++it) { + if (QDir::isRelativePath(*it)) + t << "./" << *it << endl; + else + t << *it << endl; + } + t << ");" << endl; + t.flush(); + file.close(); + } +} + +void createArObjectScriptFile(const QString &fileName, const QString &target, const QStringList &objList) +{ + QString filePath = Option::output_dir + QDir::separator() + fileName; + QFile file(filePath); + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream t(&file); + t << "CREATE " << target << endl; + for (QStringList::ConstIterator it = objList.constBegin(); it != objList.constEnd(); ++it) { + t << "ADDMOD " << *it << endl; + } + t << "SAVE" << endl; + t.flush(); + file.close(); + } +} + +void createRvctObjectScriptFile(const QString &fileName, const QStringList &objList) +{ + QString filePath = Option::output_dir + QDir::separator() + fileName; + QFile file(filePath); + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream t(&file); + for (QStringList::ConstIterator it = objList.constBegin(); it != objList.constEnd(); ++it) { + if (QDir::isRelativePath(*it)) + t << "./" << *it << endl; + else + t << *it << endl; + } + t.flush(); + file.close(); + } +} + +void MingwMakefileGenerator::writeMingwParts(QTextStream &t) +{ + if (!project->isEmpty("QMAKE_SYMBIAN_SHLIB")) { + t << "vpath %.dso " << project->values("QMAKE_LIBDIR").join(";") << endl; + t << "vpath %.lib " << project->values("QMAKE_LIBDIR").join(";") << endl; + t << "\n\n"; + } + + writeStandardParts(t); + + if (!preCompHeaderOut.isEmpty()) { + QString header = project->first("PRECOMPILED_HEADER"); + QString cHeader = preCompHeaderOut + Option::dir_sep + "c"; + t << escapeDependencyPath(cHeader) << ": " << escapeDependencyPath(header) << " " + << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t") + << "\n\t" << mkdir_p_asstring(preCompHeaderOut) + << "\n\t" << "$(CC) -x c-header -c $(CFLAGS) $(INCPATH) -o " << cHeader << " " << header + << endl << endl; + QString cppHeader = preCompHeaderOut + Option::dir_sep + "c++"; + t << escapeDependencyPath(cppHeader) << ": " << escapeDependencyPath(header) << " " + << escapeDependencyPaths(findDependencies(header)).join(" \\\n\t\t") + << "\n\t" << mkdir_p_asstring(preCompHeaderOut) + << "\n\t" << "$(CXX) -x c++-header -c $(CXXFLAGS) $(INCPATH) -o " << cppHeader << " " << header + << endl << endl; + } +} + +void MingwMakefileGenerator::init() +{ + if(init_flag) + return; + init_flag = true; + + /* 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("QMAKE_COPY_FILE")) + project->values("QMAKE_COPY_FILE").append("$(COPY)"); + if(project->isEmpty("QMAKE_COPY_DIR")) + project->values("QMAKE_COPY_DIR").append("xcopy /s /q /y /i"); + if(project->isEmpty("QMAKE_INSTALL_FILE")) + project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_PROGRAM")) + project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_DIR")) + project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)"); + if(project->values("MAKEFILE").isEmpty()) + project->values("MAKEFILE").append("Makefile"); + return; + } + + project->values("TARGET_PRL").append(project->first("TARGET")); + + processVars(); + + if (!project->values("RES_FILE").isEmpty()) { + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("RES_FILE")); + } + + // LIBS defined in Profile comes first for gcc + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS")); + project->values("QMAKE_LIBS_PRIVATE") += escapeFilePaths(project->values("LIBS_PRIVATE")); + + QString targetfilename = project->values("TARGET").first(); + QStringList &configs = project->values("CONFIG"); + + if(project->isActiveConfig("qt_dll")) + if(configs.indexOf("qt") == -1) + configs.append("qt"); + + if(project->isActiveConfig("dll") && project->values("QMAKE_SYMBIAN_SHLIB").isEmpty()) { + 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 + "lib" + project->first("TARGET") + + project->first("TARGET_VERSION_EXT") + ".a"); + project->values("QMAKE_LFLAGS").append(QString("-Wl,--out-implib,") + project->first("MINGW_IMPORT_LIB")); + } + + if(!project->values("DEF_FILE").isEmpty() && project->values("QMAKE_SYMBIAN_SHLIB").isEmpty()) { + QString defFileName = fileFixify(project->values("DEF_FILE")).first(); + project->values("QMAKE_LFLAGS").append(QString("-Wl,") + escapeFilePath(defFileName)); + } + + MakefileGenerator::init(); + + // precomp + if (!project->first("PRECOMPILED_HEADER").isEmpty() + && project->isActiveConfig("precompile_header")) { + QString preCompHeader = var("PRECOMPILED_DIR") + + QFileInfo(project->first("PRECOMPILED_HEADER")).fileName(); + preCompHeaderOut = preCompHeader + ".gch"; + project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c"); + project->values("QMAKE_CLEAN").append(preCompHeaderOut + Option::dir_sep + "c++"); + + project->values("QMAKE_RUN_CC").clear(); + project->values("QMAKE_RUN_CC").append("$(CC) -c -include " + preCompHeader + + " $(CFLAGS) $(INCPATH) -o $obj $src"); + project->values("QMAKE_RUN_CC_IMP").clear(); + project->values("QMAKE_RUN_CC_IMP").append("$(CC) -c -include " + preCompHeader + + " $(CFLAGS) $(INCPATH) -o $@ $<"); + project->values("QMAKE_RUN_CXX").clear(); + project->values("QMAKE_RUN_CXX").append("$(CXX) -c -include " + preCompHeader + + " $(CXXFLAGS) $(INCPATH) -o $obj $src"); + project->values("QMAKE_RUN_CXX_IMP").clear(); + project->values("QMAKE_RUN_CXX_IMP").append("$(CXX) -c -include " + preCompHeader + + " $(CXXFLAGS) $(INCPATH) -o $@ $<"); + } + + if(project->isActiveConfig("dll")) { + project->values("QMAKE_CLEAN").append(project->first("MINGW_IMPORT_LIB")); + } +} + +void MingwMakefileGenerator::fixTargetExt() +{ + if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") { + project->values("QMAKE_LFLAGS").append("-static"); + } + Win32MakefileGenerator::fixTargetExt(); +} + +void MingwMakefileGenerator::writeIncPart(QTextStream &t) +{ + t << "INCPATH = "; + + QStringList &incs = project->values("INCLUDEPATH"); + for(QStringList::Iterator incit = incs.begin(); incit != incs.end(); ++incit) { + QString inc = (*incit); + inc.replace(QRegExp("\\\\$"), ""); + inc.replace(QRegExp("\""), ""); + t << "-I" << quote << inc << quote << " "; + } + t << "-I" << quote << specdir() << quote + << endl; +} + +void MingwMakefileGenerator::writeLibsPart(QTextStream &t) +{ + if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") { + t << "LIB = " << var("QMAKE_LIB") << endl; + } else { + t << "LINK = " << var("QMAKE_LINK") << endl; + t << "LFLAGS = " << var("QMAKE_LFLAGS") << endl; + t << "LIBS = "; + if(!project->values("QMAKE_LIBDIR").isEmpty()) + writeLibDirPart(t); + if (project->isActiveConfig("rvct_linker")) { + t << var("QMAKE_LIBS") << ' ' + << var("QMAKE_LIBS_PRIVATE") << endl; + } else { + t << var("QMAKE_LIBS").replace(QRegExp("(\\slib|^lib)")," -l") << ' ' + << var("QMAKE_LIBS_PRIVATE").replace(QRegExp("(\\slib|^lib)")," -l") << endl; + } + } +} + +void MingwMakefileGenerator::writeLibDirPart(QTextStream &t) +{ + QStringList libDirs = project->values("QMAKE_LIBDIR"); + QString libArg = QString::fromLatin1("-L"); + if (project->isActiveConfig("rvct_linker")) + libArg = QString::fromLatin1("--userlibpath "); + for (int i = 0; i < libDirs.size(); ++i) { + libDirs[i].remove("\""); + if (libDirs[i].endsWith("\\")) + libDirs[i].chop(1); + } + t << valGlue(libDirs, libArg+quote, quote+" "+libArg+quote, quote) << " "; +} + +void MingwMakefileGenerator::writeObjectsPart(QTextStream &t) +{ + if (project->values("OBJECTS").count() < var("QMAKE_LINK_OBJECT_MAX").toInt()) { + objectsLinkLine = "$(OBJECTS)"; + } else if (project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") { + QString ar_script_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET"); + if (!var("BUILD_NAME").isEmpty()) { + ar_script_file += "." + var("BUILD_NAME"); + } + // QMAKE_LIB is used for win32, including mingw, whereas QMAKE_AR is used on Unix. + if (project->isActiveConfig("rvct_linker")) { + createRvctObjectScriptFile(ar_script_file, project->values("OBJECTS")); + QString ar_cmd = project->values("QMAKE_LIB").join(" "); + if (ar_cmd.isEmpty()) + ar_cmd = "armar --create"; + objectsLinkLine = ar_cmd + " " + var("DEST_TARGET") + " --via " + ar_script_file; + } else { + // Strip off any options since the ar commands will be read from file. + QString ar_cmd = var("QMAKE_LIB").section(" ", 0, 0);; + if (ar_cmd.isEmpty()) + ar_cmd = "ar"; + createArObjectScriptFile(ar_script_file, var("DEST_TARGET"), project->values("OBJECTS")); + objectsLinkLine = ar_cmd + " -M < " + ar_script_file; + } + } else { + QString ld_script_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET"); + if (!var("BUILD_NAME").isEmpty()) { + ld_script_file += "." + var("BUILD_NAME"); + } + if (project->isActiveConfig("rvct_linker")) { + createRvctObjectScriptFile(ld_script_file, project->values("OBJECTS")); + objectsLinkLine = QString::fromLatin1("--via ") + ld_script_file; + } else { + createLdObjectScriptFile(ld_script_file, project->values("OBJECTS")); + objectsLinkLine = ld_script_file; + } + } + Win32MakefileGenerator::writeObjectsPart(t); +} + +void MingwMakefileGenerator::writeBuildRulesPart(QTextStream &t) +{ + t << "first: all" << endl; + t << "all: " << escapeDependencyPath(fileFixify(Option::output.fileName())) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS"))," "," "," ") << " $(DESTDIR_TARGET)" << endl << endl; + t << "$(DESTDIR_TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) " << var("POST_TARGETDEPS"); + if(!project->isEmpty("QMAKE_PRE_LINK")) + t << "\n\t" <isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") { + if (project->values("OBJECTS").count() < var("QMAKE_LINK_OBJECT_MAX").toInt()) { + t << "\n\t" << "$(LIB) $(DESTDIR_TARGET) " << objectsLinkLine << " " ; + } else { + t << "\n\t" << objectsLinkLine << " " ; + } + } else { + t << "\n\t" << "$(LINK) $(LFLAGS) -o $(DESTDIR_TARGET) " << objectsLinkLine << " " << " $(LIBS)"; + } + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" <first("RC_FILE")); + + QString incPathStr = fileInfo(rc_file).path(); + if (incPathStr != "." && QDir::isRelativePath(incPathStr)) + incPathStr.prepend("./"); + + if (!rc_file.isEmpty()) { + t << escapeDependencyPath(var("RES_FILE")) << ": " << rc_file << "\n\t" + << var("QMAKE_RC") << " -i " << rc_file << " -o " << var("RES_FILE") + << " --include-dir=" << incPathStr << " $(DEFINES)" << endl << endl; + } +} + +void MingwMakefileGenerator::processPrlVariable(const QString &var, const QStringList &l) +{ + if (var == "QMAKE_PRL_LIBS") { + QString where = "QMAKE_LIBS"; + if (!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS")) + where = project->first("QMAKE_INTERNAL_PRL_LIBS"); + QStringList &out = project->values(where); + for (QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) { + out.removeAll((*it)); + out.append((*it)); + } + } else { + Win32MakefileGenerator::processPrlVariable(var, l); + } +} + +QStringList &MingwMakefileGenerator::findDependencies(const QString &file) +{ + QStringList &aList = MakefileGenerator::findDependencies(file); + // Note: The QMAKE_IMAGE_COLLECTION file have all images + // as dependency, so don't add precompiled header then + if (file == project->first("QMAKE_IMAGE_COLLECTION") + || 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 diff --git a/qmake/generators/win32/mingw_make.h b/qmake/generators/win32/mingw_make.h new file mode 100644 index 0000000000..5bc9c7b3b4 --- /dev/null +++ b/qmake/generators/win32/mingw_make.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MINGW_MAKE_H +#define MINGW_MAKE_H + +#include "winmakefile.h" + +QT_BEGIN_NAMESPACE + +class MingwMakefileGenerator : public Win32MakefileGenerator +{ +public: + MingwMakefileGenerator(); + ~MingwMakefileGenerator(); +protected: + QString escapeDependencyPath(const QString &path) const; + QString getLibTarget(); + bool writeMakefile(QTextStream &); + void init(); +private: + bool isWindowsShell() const; + void writeMingwParts(QTextStream &); + void writeIncPart(QTextStream &t); + void writeLibsPart(QTextStream &t); + void writeLibDirPart(QTextStream &t); + void writeObjectsPart(QTextStream &t); + void writeBuildRulesPart(QTextStream &t); + void writeRcFilePart(QTextStream &t); + void processPrlVariable(const QString &var, const QStringList &l); + + QStringList &findDependencies(const QString &file); + + QString preCompHeaderOut; + + virtual bool findLibraries(); + bool findLibraries(const QString &where); + void fixTargetExt(); + + bool init_flag; + QString objectsLinkLine; + QString quote; +}; + +inline MingwMakefileGenerator::~MingwMakefileGenerator() +{ } + +QT_END_NAMESPACE + +#endif // MINGW_MAKE_H diff --git a/qmake/generators/win32/msbuild_objectmodel.cpp b/qmake/generators/win32/msbuild_objectmodel.cpp new file mode 100644 index 0000000000..d4d2400146 --- /dev/null +++ b/qmake/generators/win32/msbuild_objectmodel.cpp @@ -0,0 +1,1940 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "msvc_objectmodel.h" +#include "msbuild_objectmodel.h" +#include "msvc_vcproj.h" +#include "msvc_vcxproj.h" +#include +#include + +QT_BEGIN_NAMESPACE + +// XML Tags --------------------------------------------------------- +const char _CLCompile[] = "ClCompile"; +const char _ItemGroup[] = "ItemGroup"; +const char _Link[] = "Link"; +const char _Midl[] = "Midl"; +const char _ResourceCompile[] = "ResourceCompile"; + +// XML Properties --------------------------------------------------- +const char _AddModuleNamesToAssembly[] = "AddModuleNamesToAssembly"; +const char _AdditionalDependencies[] = "AdditionalDependencies"; +const char _AdditionalIncludeDirectories[] = "AdditionalIncludeDirectories"; +const char _AdditionalLibraryDirectories[] = "AdditionalLibraryDirectories"; +const char _AdditionalManifestDependencies[] = "AdditionalManifestDependencies"; +const char _AdditionalOptions[] = "AdditionalOptions"; +const char _AdditionalUsingDirectories[] = "AdditionalUsingDirectories"; +const char _AllowIsolation[] = "AllowIsolation"; +const char _AlwaysAppend[] = "AlwaysAppend"; +const char _ApplicationConfigurationMode[] = "ApplicationConfigurationMode"; +const char _AssemblerListingLocation[] = "AssemblerListingLocation"; +const char _AssemblerOutput[] = "AssemblerOutput"; +const char _AssemblyDebug[] = "AssemblyDebug"; +const char _AssemblyLinkResource[] = "AssemblyLinkResource"; +const char _ATLMinimizesCRunTimeLibraryUsage[] = "ATLMinimizesCRunTimeLibraryUsage"; +const char _BaseAddress[] = "BaseAddress"; +const char _BasicRuntimeChecks[] = "BasicRuntimeChecks"; +const char _BrowseInformation[] = "BrowseInformation"; +const char _BrowseInformationFile[] = "BrowseInformationFile"; +const char _BufferSecurityCheck[] = "BufferSecurityCheck"; +const char _BuildBrowserInformation[] = "BuildBrowserInformation"; +const char _CallingConvention[] = "CallingConvention"; +const char _CharacterSet[] = "CharacterSet"; +const char _ClientStubFile[] = "ClientStubFile"; +const char _CLRImageType[] = "CLRImageType"; +const char _CLRSupportLastError[] = "CLRSupportLastError"; +const char _CLRThreadAttribute[] = "CLRThreadAttribute"; +const char _CLRUnmanagedCodeCheck[] = "CLRUnmanagedCodeCheck"; +const char _Command[] = "Command"; +const char _CompileAs[] = "CompileAs"; +const char _CompileAsManaged[] = "CompileAsManaged"; +const char _ConfigurationType[] = "ConfigurationType"; +const char _CPreprocessOptions[] = "CPreprocessOptions"; +const char _CreateHotpatchableImage[] = "CreateHotpatchableImage"; +const char _CreateHotPatchableImage[] = "CreateHotPatchableImage"; +const char _Culture[] = "Culture"; +const char _DataExecutionPrevention[] = "DataExecutionPrevention"; +const char _DebugInformationFormat[] = "DebugInformationFormat"; +const char _DefaultCharType[] = "DefaultCharType"; +const char _DelayLoadDLLs[] = "DelayLoadDLLs"; +const char _DelaySign[] = "DelaySign"; +const char _DeleteExtensionsOnClean[] = "DeleteExtensionsOnClean"; +const char _DisableLanguageExtensions[] = "DisableLanguageExtensions"; +const char _DisableSpecificWarnings[] = "DisableSpecificWarnings"; +const char _DisplayLibrary[] = "DisplayLibrary"; +const char _DLLDataFileName[] = "DLLDataFileName"; +const char _EmbedManagedResourceFile[] = "EmbedManagedResourceFile"; +const char _EnableCOMDATFolding[] = "EnableCOMDATFolding"; +const char _EnableUAC[] = "EnableUAC"; +const char _EnableErrorChecks[] = "EnableErrorChecks"; +const char _EnableEnhancedInstructionSet[] = "EnableEnhancedInstructionSet"; +const char _EnableFiberSafeOptimizations[] = "EnableFiberSafeOptimizations"; +const char _EnablePREfast[] = "EnablePREfast"; +const char _EntryPointSymbol[] = "EntryPointSymbol"; +const char _ErrorCheckAllocations[] = "ErrorCheckAllocations"; +const char _ErrorCheckBounds[] = "ErrorCheckBounds"; +const char _ErrorCheckEnumRange[] = "ErrorCheckEnumRange"; +const char _ErrorCheckRefPointers[] = "ErrorCheckRefPointers"; +const char _ErrorCheckStubData[] = "ErrorCheckStubData"; +const char _ErrorReporting[] = "ErrorReporting"; +const char _ExceptionHandling[] = "ExceptionHandling"; +const char _ExpandAttributedSource[] = "ExpandAttributedSource"; +const char _ExportNamedFunctions[] = "ExportNamedFunctions"; +const char _FavorSizeOrSpeed[] = "FavorSizeOrSpeed"; +const char _FloatingPointModel[] = "FloatingPointModel"; +const char _FloatingPointExceptions[] = "FloatingPointExceptions"; +const char _ForceConformanceInForLoopScope[] = "ForceConformanceInForLoopScope"; +const char _ForceSymbolReferences[] = "ForceSymbolReferences"; +const char _ForcedIncludeFiles[] = "ForcedIncludeFiles"; +const char _ForcedUsingFiles[] = "ForcedUsingFiles"; +const char _FunctionLevelLinking[] = "FunctionLevelLinking"; +const char _FunctionOrder[] = "FunctionOrder"; +const char _GenerateClientFiles[] = "GenerateClientFiles"; +const char _GenerateDebugInformation[] = "GenerateDebugInformation"; +const char _GenerateManifest[] = "GenerateManifest"; +const char _GenerateMapFile[] = "GenerateMapFile"; +const char _GenerateServerFiles[] = "GenerateServerFiles"; +const char _GenerateStublessProxies[] = "GenerateStublessProxies"; +const char _GenerateTypeLibrary[] = "GenerateTypeLibrary"; +const char _GenerateXMLDocumentationFiles[] = "GenerateXMLDocumentationFiles"; +const char _HeaderFileName[] = "HeaderFileName"; +const char _HeapCommitSize[] = "HeapCommitSize"; +const char _HeapReserveSize[] = "HeapReserveSize"; +const char _IgnoreAllDefaultLibraries[] = "IgnoreAllDefaultLibraries"; +const char _IgnoreEmbeddedIDL[] = "IgnoreEmbeddedIDL"; +const char _IgnoreImportLibrary[] = "IgnoreImportLibrary"; +const char _IgnoreSpecificDefaultLibraries[] = "IgnoreSpecificDefaultLibraries"; +const char _IgnoreStandardIncludePath[] = "IgnoreStandardIncludePath"; +const char _ImportLibrary[] = "ImportLibrary"; +const char _InlineFunctionExpansion[] = "InlineFunctionExpansion"; +const char _IntrinsicFunctions[] = "IntrinsicFunctions"; +const char _InterfaceIdentifierFileName[] = "InterfaceIdentifierFileName"; +const char _IntermediateDirectory[] = "IntermediateDirectory"; +const char _KeyContainer[] = "KeyContainer"; +const char _KeyFile[] = "KeyFile"; +const char _LargeAddressAware[] = "LargeAddressAware"; +const char _LinkDLL[] = "LinkDLL"; +const char _LinkErrorReporting[] = "LinkErrorReporting"; +const char _LinkIncremental[] = "LinkIncremental"; +const char _LinkStatus[] = "LinkStatus"; +const char _LinkTimeCodeGeneration[] = "LinkTimeCodeGeneration"; +const char _LocaleID[] = "LocaleID"; +const char _ManifestFile[] = "ManifestFile"; +const char _MapExports[] = "MapExports"; +const char _MapFileName[] = "MapFileName"; +const char _MergedIDLBaseFileName[] = "MergedIDLBaseFileName"; +const char _MergeSections[] = "MergeSections"; +const char _Message[] = "Message"; +const char _MidlCommandFile[] = "MidlCommandFile"; +const char _MinimalRebuild[] = "MinimalRebuild"; +const char _MkTypLibCompatible[] = "MkTypLibCompatible"; +const char _ModuleDefinitionFile[] = "ModuleDefinitionFile"; +const char _MultiProcessorCompilation[] = "MultiProcessorCompilation"; +const char _Name[] = "Name"; +const char _NoEntryPoint[] = "NoEntryPoint"; +const char _NullTerminateStrings[] = "NullTerminateStrings"; +const char _ObjectFiles[] = "ObjectFiles"; +const char _ObjectFileName[] = "ObjectFileName"; +const char _OmitDefaultLibName[] = "OmitDefaultLibName"; +const char _OmitFramePointers[] = "OmitFramePointers"; +const char _OpenMPSupport[] = "OpenMPSupport"; +const char _Optimization[] = "Optimization"; +const char _OptimizeReferences[] = "OptimizeReferences"; +const char _OutputDirectory[] = "OutputDirectory"; +const char _OutputFile[] = "OutputFile"; +const char _PrecompiledHeader[] = "PrecompiledHeader"; +const char _PrecompiledHeaderFile[] = "PrecompiledHeaderFile"; +const char _PrecompiledHeaderOutputFile[] = "PrecompiledHeaderOutputFile"; +const char _PreprocessorDefinitions[] = "PreprocessorDefinitions"; +const char _PreprocessKeepComments[] = "PreprocessKeepComments"; +const char _PreprocessOutputPath[] = "PreprocessOutputPath"; +const char _PreprocessSuppressLineNumbers[] = "PreprocessSuppressLineNumbers"; +const char _PreprocessToFile[] = "PreprocessToFile"; +const char _PreventDllBinding[] = "PreventDllBinding"; +const char _PrimaryOutput[] = "PrimaryOutput"; +const char _ProcessorNumber[] = "ProcessorNumber"; +const char _ProgramDatabase[] = "ProgramDatabase"; +const char _ProgramDataBaseFileName[] = "ProgramDataBaseFileName"; +const char _ProgramDatabaseFile[] = "ProgramDatabaseFile"; +const char _ProxyFileName[] = "ProxyFileName"; +const char _RandomizedBaseAddress[] = "RandomizedBaseAddress"; +const char _RedirectOutputAndErrors[] = "RedirectOutputAndErrors"; +const char _RegisterOutput[] = "RegisterOutput"; +const char _RemoveObjects[] = "RemoveObjects"; +const char _ResourceOutputFileName[] = "ResourceOutputFileName"; +const char _RuntimeLibrary[] = "RuntimeLibrary"; +const char _RuntimeTypeInfo[] = "RuntimeTypeInfo"; +const char _SectionAlignment[] = "SectionAlignment"; +const char _ServerStubFile[] = "ServerStubFile"; +const char _SetChecksum[] = "SetChecksum"; +const char _ShowIncludes[] = "ShowIncludes"; +const char _ShowProgress[] = "ShowProgress"; +const char _SmallerTypeCheck[] = "SmallerTypeCheck"; +const char _StackCommitSize[] = "StackCommitSize"; +const char _StackReserveSize[] = "StackReserveSize"; +const char _StringPooling[] = "StringPooling"; +const char _StripPrivateSymbols[] = "StripPrivateSymbols"; +const char _StructMemberAlignment[] = "StructMemberAlignment"; +const char _SubSystem[] = "SubSystem"; +const char _SupportNobindOfDelayLoadedDLL[] = "SupportNobindOfDelayLoadedDLL"; +const char _SupportUnloadOfDelayLoadedDLL[] = "SupportUnloadOfDelayLoadedDLL"; +const char _SuppressCompilerWarnings[] = "SuppressCompilerWarnings"; +const char _SuppressStartupBanner[] = "SuppressStartupBanner"; +const char _SwapRunFromCD[] = "SwapRunFromCD"; +const char _SwapRunFromNet[] = "SwapRunFromNet"; +const char _TargetEnvironment[] = "TargetEnvironment"; +const char _TargetMachine[] = "TargetMachine"; +const char _TerminalServerAware[] = "TerminalServerAware"; +const char _TrackerLogDirectory[] = "TrackerLogDirectory"; +const char _TreatLibWarningAsErrors[] = "TreatLibWarningAsErrors"; +const char _TreatLinkerWarningAsErrors[] = "TreatLinkerWarningAsErrors"; +const char _TreatSpecificWarningsAsErrors[] = "TreatSpecificWarningsAsErrors"; +const char _TreatWarningAsError[] = "TreatWarningAsError"; +const char _TreatWChar_tAsBuiltInType[] = "TreatWChar_tAsBuiltInType"; +const char _TurnOffAssemblyGeneration[] = "TurnOffAssemblyGeneration"; +const char _TypeLibFormat[] = "TypeLibFormat"; +const char _TypeLibraryFile[] = "TypeLibraryFile"; +const char _TypeLibraryName[] = "TypeLibraryName"; +const char _TypeLibraryResourceID[] = "TypeLibraryResourceID"; +const char _UACExecutionLevel[] = "UACExecutionLevel"; +const char _UACUIAccess[] = "UACUIAccess"; +const char _UndefineAllPreprocessorDefinitions[]= "UndefineAllPreprocessorDefinitions"; +const char _UndefinePreprocessorDefinitions[] = "UndefinePreprocessorDefinitions"; +const char _UseFullPaths[] = "UseFullPaths"; +const char _UseOfATL[] = "UseOfATL"; +const char _UseOfMfc[] = "UseOfMfc"; +const char _UseUnicodeForAssemblerListing[] = "UseUnicodeForAssemblerListing"; +const char _ValidateAllParameters[] = "ValidateAllParameters"; +const char _VCCustomBuildTool[] = "VCCustomBuildTool"; +const char _Verbose[] = "Verbose"; +const char _Version[] = "Version"; +const char _WarnAsError[] = "WarnAsError"; +const char _WarningLevel[] = "WarningLevel"; +const char _WholeProgramOptimization[] = "WholeProgramOptimization"; +const char _XMLDocumentationFileName[] = "XMLDocumentationFileName"; + + +// XmlOutput stream functions ------------------------------ +inline XmlOutput::xml_output attrTagT(const char *name, const triState v) +{ + if(v == unset) + return noxml(); + return tagValue(name, (v == _True ? "true" : "false")); +} + +inline XmlOutput::xml_output attrTagL(const char *name, qint64 v) +{ + return tagValue(name, QString::number(v)); +} + +/*ifNot version*/ +inline XmlOutput::xml_output attrTagL(const char *name, qint64 v, qint64 ifn) +{ + if (v == ifn) + return noxml(); + return tagValue(name, QString::number(v)); +} + +inline XmlOutput::xml_output attrTagS(const char *name, const QString &v) +{ + if(v.isEmpty()) + return noxml(); + return tagValue(name, v); +} + +inline XmlOutput::xml_output attrTagX(const char *name, const QStringList &v, const char *s = ",") +{ + if(v.isEmpty()) + return noxml(); + QStringList temp = v; + temp.append(QString("%(%1)").arg(name)); + return tagValue(name, temp.join(s)); +} + +inline XmlOutput::xml_output valueTagX(const QStringList &v, const char *s = " ") +{ + if(v.isEmpty()) + return noxml(); + return valueTag(v.join(s)); +} + +inline XmlOutput::xml_output valueTagDefX(const QStringList &v, const QString &tagName, const char *s = " ") +{ + if(v.isEmpty()) + return noxml(); + QStringList temp = v; + temp.append(QString("%(%1)").arg(tagName)); + return valueTag(temp.join(s)); +} + +inline XmlOutput::xml_output valueTagT( const triState v) +{ + if(v == unset) + return noxml(); + return valueTag(v == _True ? "true" : "false"); +} + +static QString vcxCommandSeparator() +{ + // MSBuild puts the contents of the custom commands into a batch file and calls it. + // As we want every sub-command to be error-checked (as is done by makefile-based + // backends), we insert the checks ourselves, using the undocumented jump target. + static QString cmdSep = + QLatin1String(" if errorlevel 1 goto VCEnd "); + return cmdSep; +} + +// Tree file generation --------------------------------------------- +void XTreeNode::generateXML(XmlOutput &xml, XmlOutput &xmlFilter, const QString &tagName, VCProject &tool, const QString &filter) { + + if (children.size()) { + // Filter + QString tempFilterName; + ChildrenMap::ConstIterator it, end = children.constEnd(); + if (!tagName.isEmpty()) { + tempFilterName.append(filter); + tempFilterName.append("\\"); + tempFilterName.append(tagName); + xmlFilter << tag(_ItemGroup); + xmlFilter << tag("Filter") + << attrTag("Include", tempFilterName) + << closetag(); + xmlFilter << closetag(); + } + // First round, do nested filters + for (it = children.constBegin(); it != end; ++it) + if ((*it)->children.size()) + { + if ( !tempFilterName.isEmpty() ) + (*it)->generateXML(xml, xmlFilter, it.key(), tool, tempFilterName); + else + (*it)->generateXML(xml, xmlFilter, it.key(), tool, filter); + } + // Second round, do leafs + for (it = children.constBegin(); it != end; ++it) + if (!(*it)->children.size()) + { + if ( !tempFilterName.isEmpty() ) + (*it)->generateXML(xml, xmlFilter, it.key(), tool, tempFilterName); + else + (*it)->generateXML(xml, xmlFilter, it.key(), tool, filter); + } + } else { + // Leaf + xml << tag(_ItemGroup); + xmlFilter << tag(_ItemGroup); + VCXProjectWriter::outputFileConfigs(tool, xml, xmlFilter, info, filter); + xmlFilter << closetag(); + xml << closetag(); + } +} + +// Flat file generation --------------------------------------------- +void XFlatNode::generateXML(XmlOutput &xml, XmlOutput &xmlFilter, const QString &/*tagName*/, VCProject &tool, const QString &filter) { + if (children.size()) { + ChildrenMapFlat::ConstIterator it = children.constBegin(); + ChildrenMapFlat::ConstIterator end = children.constEnd(); + xml << tag(_ItemGroup); + xmlFilter << tag(_ItemGroup); + for (; it != end; ++it) { + VCXProjectWriter::outputFileConfigs(tool, xml, xmlFilter, (*it), filter); + } + xml << closetag(); + xmlFilter << closetag(); + } +} + +void VCXProjectWriter::write(XmlOutput &xml, VCProjectSingleConfig &tool) +{ + xml.setIndentString(" "); + + xml << decl("1.0", "utf-8") + << tag("Project") + << attrTag("DefaultTargets","Build") + << attrTag("ToolsVersion", "4.0") + << attrTag("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003") + << tag("ItemGroup") + << attrTag("Label", "ProjectConfigurations"); + + xml << tag("ProjectConfiguration") + << attrTag("Include" , tool.Configuration.Name) + << tagValue("Configuration", tool.Configuration.ConfigurationName) + << tagValue("Platform", tool.PlatformName) + << closetag(); + + xml << closetag() + << tag("PropertyGroup") + << attrTag("Label", "Globals") + << tagValue("ProjectGuid", tool.ProjectGUID) + << tagValue("RootNamespace", tool.Name) + << tagValue("Keyword", tool.Keyword) + << closetag(); + + // config part. + xml << import("Project", "$(VCTargetsPath)\\Microsoft.Cpp.Default.props"); + + write(xml, tool.Configuration); + + xml << import("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props"); + + // Extension settings + xml << tag("ImportGroup") + << attrTag("Label", "ExtensionSettings") + << closetag(); + + // PropertySheets + xml << tag("ImportGroup") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.Configuration.Name)) + << attrTag("Label", "PropertySheets"); + + xml << tag("Import") + << attrTag("Project", "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props") + << attrTag("Condition", "exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')") + << closetag() + << closetag(); + + + // UserMacros + xml << tag("PropertyGroup") + << attrTag("Label", "UserMacros") + << closetag(); + + xml << tag("PropertyGroup"); + + if ( !tool.Configuration.OutputDirectory.isEmpty() ) { + xml<< tag("OutDir") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.Configuration.Name)) + << valueTag(tool.Configuration.OutputDirectory); + } + if ( !tool.Configuration.IntermediateDirectory.isEmpty() ) { + xml<< tag("IntDir") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.Configuration.Name)) + << valueTag(tool.Configuration.IntermediateDirectory); + } + if ( !tool.Configuration.PrimaryOutput.isEmpty() ) { + xml<< tag("TargetName") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.Configuration.Name)) + << valueTag(tool.Configuration.PrimaryOutput); + } + + if ( tool.Configuration.linker.IgnoreImportLibrary != unset) { + xml<< tag("IgnoreImportLibrary") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.Configuration.Name)) + << valueTagT(tool.Configuration.linker.IgnoreImportLibrary); + } + + if ( tool.Configuration.linker.LinkIncremental != linkIncrementalDefault) { + const triState ts = (tool.Configuration.linker.LinkIncremental == linkIncrementalYes ? _True : _False); + xml<< tag("LinkIncremental") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.Configuration.Name)) + << valueTagT(ts); + } + + if ( tool.Configuration.preBuild.ExcludedFromBuild != unset ) + { + xml<< tag("PreBuildEventUseInBuild") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.Configuration.Name)) + << valueTagT(!tool.Configuration.preBuild.ExcludedFromBuild); + } + + if ( tool.Configuration.preLink.ExcludedFromBuild != unset ) + { + xml<< tag("PreLinkEventUseInBuild") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.Configuration.Name)) + << valueTagT(!tool.Configuration.preLink.ExcludedFromBuild); + } + + if ( tool.Configuration.postBuild.ExcludedFromBuild != unset ) + { + xml<< tag("PostBuildEventUseInBuild") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.Configuration.Name)) + << valueTagT(!tool.Configuration.postBuild.ExcludedFromBuild); + } + xml << closetag(); + + xml << tag("ItemDefinitionGroup") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.Configuration.Name)); + + // ClCompile + write(xml, tool.Configuration.compiler); + + // Link + write(xml, tool.Configuration.linker); + + // Midl + write(xml, tool.Configuration.idl); + + // ResourceCompiler + write(xml, tool.Configuration.resource); + + // Post build event + if ( tool.Configuration.postBuild.ExcludedFromBuild != unset ) + write(xml, tool.Configuration.postBuild); + + // Pre build event + if ( tool.Configuration.preBuild.ExcludedFromBuild != unset ) + write(xml, tool.Configuration.preBuild); + + // Pre link event + if ( tool.Configuration.preLink.ExcludedFromBuild != unset ) + write(xml, tool.Configuration.preLink); + + xml << closetag(); + + QFile filterFile; + filterFile.setFileName(Option::output.fileName().append(".filters")); + filterFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate); + QTextStream ts(&filterFile); + XmlOutput xmlFilter(ts, XmlOutput::NoConversion); + + xmlFilter.setIndentString(" "); + + xmlFilter << decl("1.0", "utf-8") + << tag("Project") + << attrTag("ToolsVersion", "4.0") + << attrTag("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"); + + xmlFilter << tag("ItemGroup"); + + VCProject tempProj; + tempProj.SingleProjects += tool; + + addFilters(tempProj, xmlFilter, "Form Files"); + addFilters(tempProj, xmlFilter, "Generated Files"); + addFilters(tempProj, xmlFilter, "Header Files"); + addFilters(tempProj, xmlFilter, "LexYacc Files"); + addFilters(tempProj, xmlFilter, "Resource Files"); + addFilters(tempProj, xmlFilter, "Source Files"); + addFilters(tempProj, xmlFilter, "Translation Files"); + xmlFilter << closetag(); + + outputFilter(tempProj, xml, xmlFilter, "Source Files"); + outputFilter(tempProj, xml, xmlFilter, "Header Files"); + outputFilter(tempProj, xml, xmlFilter, "Generated Files"); + outputFilter(tempProj, xml, xmlFilter, "LexYacc Files"); + outputFilter(tempProj, xml, xmlFilter, "Translation Files"); + outputFilter(tempProj, xml, xmlFilter, "Form Files"); + outputFilter(tempProj, xml, xmlFilter, "Resource Files"); + + for (int x = 0; x < tempProj.ExtraCompilers.count(); ++x) { + outputFilter(tempProj, xml, xmlFilter, tempProj.ExtraCompilers.at(x)); + } + + xml << import("Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets"); + + xml << tag("ImportGroup") + << attrTag("Label", "ExtensionTargets") + << closetag(); +} + +void VCXProjectWriter::write(XmlOutput &xml, VCProject &tool) +{ + if (tool.SingleProjects.count() == 0) { + warn_msg(WarnLogic, "Generator: .NET: no single project in merge project, no output"); + return; + } + + xml.setIndentString(" "); + + xml << decl("1.0", "utf-8") + << tag("Project") + << attrTag("DefaultTargets","Build") + << attrTag("ToolsVersion", "4.0") + << attrTag("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003") + << tag("ItemGroup") + << attrTag("Label", "ProjectConfigurations"); + + for (int i = 0; i < tool.SingleProjects.count(); ++i) { + xml << tag("ProjectConfiguration") + << attrTag("Include" , tool.SingleProjects.at(i).Configuration.Name) + << tagValue("Configuration", tool.SingleProjects.at(i).Configuration.ConfigurationName) + << tagValue("Platform", tool.SingleProjects.at(i).PlatformName) + << closetag(); + } + + xml << closetag() + << tag("PropertyGroup") + << attrTag("Label", "Globals") + << tagValue("ProjectGuid", tool.ProjectGUID) + << tagValue("RootNamespace", tool.Name) + << tagValue("Keyword", tool.Keyword) + << closetag(); + + // config part. + xml << import("Project", "$(VCTargetsPath)\\Microsoft.Cpp.Default.props"); + for (int i = 0; i < tool.SingleProjects.count(); ++i) + write(xml, tool.SingleProjects.at(i).Configuration); + xml << import("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props"); + + // Extension settings + xml << tag("ImportGroup") + << attrTag("Label", "ExtensionSettings") + << closetag(); + + // PropertySheets + for (int i = 0; i < tool.SingleProjects.count(); ++i) { + xml << tag("ImportGroup") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.SingleProjects.at(i).Configuration.Name)) + << attrTag("Label", "PropertySheets"); + + xml << tag("Import") + << attrTag("Project", "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props") + << attrTag("Condition", "exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')") + << closetag() + << closetag(); + } + + // UserMacros + xml << tag("PropertyGroup") + << attrTag("Label", "UserMacros") + << closetag(); + + xml << tag("PropertyGroup"); + for (int i = 0; i < tool.SingleProjects.count(); ++i) { + + if ( !tool.SingleProjects.at(i).Configuration.OutputDirectory.isEmpty() ) { + xml << tag("OutDir") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.SingleProjects.at(i).Configuration.Name)) + << valueTag(tool.SingleProjects.at(i).Configuration.OutputDirectory); + } + if ( !tool.SingleProjects.at(i).Configuration.IntermediateDirectory.isEmpty() ) { + xml << tag("IntDir") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.SingleProjects.at(i).Configuration.Name)) + << valueTag(tool.SingleProjects.at(i).Configuration.IntermediateDirectory); + } + if ( !tool.SingleProjects.at(i).Configuration.PrimaryOutput.isEmpty() ) { + xml << tag("TargetName") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.SingleProjects.at(i).Configuration.Name)) + << valueTag(tool.SingleProjects.at(i).Configuration.PrimaryOutput); + } + + if ( tool.SingleProjects.at(i).Configuration.linker.IgnoreImportLibrary != unset) { + xml << tag("IgnoreImportLibrary") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.SingleProjects.at(i).Configuration.Name)) + << valueTagT(tool.SingleProjects.at(i).Configuration.linker.IgnoreImportLibrary); + } + + if ( tool.SingleProjects.at(i).Configuration.linker.LinkIncremental != unset) { + const triState ts = (tool.SingleProjects.at(i).Configuration.linker.LinkIncremental == linkIncrementalYes ? _True : _False); + xml << tag("LinkIncremental") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.SingleProjects.at(i).Configuration.Name)) + << valueTagT(ts); + } + + if ( tool.SingleProjects.at(i).Configuration.preBuild.ExcludedFromBuild != unset ) + { + xml << tag("PreBuildEventUseInBuild") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.SingleProjects.at(i).Configuration.Name)) + << valueTagT(!tool.SingleProjects.at(i).Configuration.preBuild.ExcludedFromBuild); + } + + if ( tool.SingleProjects.at(i).Configuration.preLink.ExcludedFromBuild != unset ) + { + xml << tag("PreLinkEventUseInBuild") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.SingleProjects.at(i).Configuration.Name)) + << valueTagT(!tool.SingleProjects.at(i).Configuration.preLink.ExcludedFromBuild); + } + + if ( tool.SingleProjects.at(i).Configuration.postBuild.ExcludedFromBuild != unset ) + { + xml << tag("PostBuildEventUseInBuild") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.SingleProjects.at(i).Configuration.Name)) + << valueTagT(!tool.SingleProjects.at(i).Configuration.postBuild.ExcludedFromBuild); + } + } + xml << closetag(); + + for (int i = 0; i < tool.SingleProjects.count(); ++i) { + xml << tag("ItemDefinitionGroup") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.SingleProjects.at(i).Configuration.Name)); + + // ClCompile + write(xml, tool.SingleProjects.at(i).Configuration.compiler); + + // Link + write(xml, tool.SingleProjects.at(i).Configuration.linker); + + // Midl + write(xml, tool.SingleProjects.at(i).Configuration.idl); + + // ResourceCompiler + write(xml, tool.SingleProjects.at(i).Configuration.resource); + + // Post build event + if ( tool.SingleProjects.at(i).Configuration.postBuild.ExcludedFromBuild != unset ) + write(xml, tool.SingleProjects.at(i).Configuration.postBuild); + + // Pre build event + if ( tool.SingleProjects.at(i).Configuration.preBuild.ExcludedFromBuild != unset ) + write(xml, tool.SingleProjects.at(i).Configuration.preBuild); + + // Pre link event + if ( tool.SingleProjects.at(i).Configuration.preLink.ExcludedFromBuild != unset ) + write(xml, tool.SingleProjects.at(i).Configuration.preLink); + + xml << closetag(); + } + + // The file filters are added in a separate file for MSBUILD. + QFile filterFile; + filterFile.setFileName(Option::output.fileName().append(".filters")); + filterFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate); + QTextStream ts(&filterFile); + XmlOutput xmlFilter(ts, XmlOutput::NoConversion); + + xmlFilter.setIndentString(" "); + + xmlFilter << decl("1.0", "utf-8") + << tag("Project") + << attrTag("ToolsVersion", "4.0") + << attrTag("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"); + + xmlFilter << tag("ItemGroup"); + + addFilters(tool, xmlFilter, "Form Files"); + addFilters(tool, xmlFilter, "Generated Files"); + addFilters(tool, xmlFilter, "Header Files"); + addFilters(tool, xmlFilter, "LexYacc Files"); + addFilters(tool, xmlFilter, "Resource Files"); + addFilters(tool, xmlFilter, "Source Files"); + addFilters(tool, xmlFilter, "Translation Files"); + xmlFilter << closetag(); + + outputFilter(tool, xml, xmlFilter, "Source Files"); + outputFilter(tool, xml, xmlFilter, "Header Files"); + outputFilter(tool, xml, xmlFilter, "Generated Files"); + outputFilter(tool, xml, xmlFilter, "LexYacc Files"); + outputFilter(tool, xml, xmlFilter, "Translation Files"); + outputFilter(tool, xml, xmlFilter, "Form Files"); + outputFilter(tool, xml, xmlFilter, "Resource Files"); + for (int x = 0; x < tool.ExtraCompilers.count(); ++x) { + outputFilter(tool, xml, xmlFilter, tool.ExtraCompilers.at(x)); + } + outputFilter(tool, xml, xmlFilter, "Root Files"); + + xml << import("Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets"); + + xml << tag("ImportGroup") + << attrTag("Label", "ExtensionTargets") + << closetag(); +} + +static inline QString toString(asmListingOption option) +{ + switch (option) { + case asmListingAsmMachine: + return "AssemblyAndMachineCode"; + case asmListingAsmMachineSrc: + return "All"; + case asmListingAsmSrc: + return "AssemblyAndSourceCode"; + case asmListingAssemblyOnly: + return "AssemblyCode"; + } + return QString(); +} + +static inline QString toString(basicRuntimeCheckOption option) +{ + switch (option) { + case runtimeBasicCheckNone: + return ""; + case runtimeCheckStackFrame: + return "StackFrameRuntimeCheck"; + case runtimeCheckUninitVariables: + return "UninitializedLocalUsageCheck"; + case runtimeBasicCheckAll: + return "EnableFastChecks"; + } + return QString(); +} + +static inline QString toString(callingConventionOption option) +{ + switch (option) { + case callConventionCDecl: + return "Cdecl"; + case callConventionFastCall: + return "FastCall"; + case callConventionStdCall: + return "StdCall"; + } + return QString(); +} + +static inline QString toString(CompileAsOptions option) +{ + switch (option) { + case compileAsC: + return "CompileAsC"; + case compileAsCPlusPlus: + return "CompileAsCpp"; + } + return QString(); +} + +static inline QString toString(compileAsManagedOptions option) +{ + switch (option) { + case managedAssembly: + return "true"; + case managedAssemblyPure: + return "Safe"; + case managedAssemblyOldSyntax: + return "OldSyntax"; + } + return QString(); +} + +static inline QString toString(debugOption option) +{ + switch (option) { + case debugOldStyleInfo: + return "OldStyle"; + case debugEditAndContinue: + return "EditAndContinue"; + case debugEnabled: + return "ProgramDatabase"; + } + return QString(); +} + +static inline QString toString(enhancedInstructionSetOption option) +{ + switch (option) { + case archSSE: + return "StreamingSIMDExtensions"; + case archSSE2: + return "StreamingSIMDExtensions2"; + } + return QString(); +} + +static inline QString toString(exceptionHandling option) +{ + switch (option) { + case ehNone: + return "false"; + case ehNoSEH: + return "Sync"; + case ehSEH: + return "Async"; + } + return QString(); +} + +static inline QString toString(favorSizeOrSpeedOption option) +{ + switch (option) { + case favorSize: + return "Size"; + case favorSpeed: + return "Speed"; + } + return QString(); +} + +static inline QString toString(floatingPointModel option) +{ + switch (option) { + case floatingPointFast: + return "Fast"; + case floatingPointPrecise: + return "Precise"; + case floatingPointStrict: + return "Strict"; + } + return QString(); +} + +static inline QString toString(inlineExpansionOption option) +{ + switch (option) { + case expandDisable: + return "Disabled"; + case expandOnlyInline: + return "OnlyExplicitInline"; + case expandAnySuitable: + return "AnySuitable"; + } + return QString(); +} + +static inline QString toString(optimizeOption option) +{ + switch (option) { + case optimizeMinSpace: + return "MinSpace"; + case optimizeMaxSpeed: + return "MaxSpeed"; + } + return QString(); +} + +static inline QString toString(pchOption option) +{ + switch (option) { + case pchNone: + return "NotUsing"; + case pchCreateUsingSpecific: + return "Create"; + case pchUseUsingSpecific: + return "Use"; + } + return QString(); +} + +static inline QString toString(runtimeLibraryOption option) +{ + switch (option) { + case rtMultiThreaded: + return "MultiThreaded"; + case rtMultiThreadedDLL: + return "MultiThreadedDLL"; + case rtMultiThreadedDebug: + return "MultiThreadedDebug"; + case rtMultiThreadedDebugDLL: + return "MultiThreadedDebugDLL"; + } + return QString(); +} + +static inline QString toString(structMemberAlignOption option) +{ + switch (option) { + case alignSingleByte: + return "1Byte"; + case alignTwoBytes: + return "2Bytes"; + case alignFourBytes: + return "4Bytes"; + case alignEightBytes: + return "8Bytes"; + case alignSixteenBytes: + return "16Bytes"; + } + return QString(); +} + +static inline QString toString(warningLevelOption option) +{ + switch (option) { + case warningLevel_0: + return "TurnOffAllWarnings"; + case warningLevel_1: + return "Level1"; + case warningLevel_2: + return "Level2"; + case warningLevel_3: + return "Level3"; + case warningLevel_4: + return "Level4"; + } + return QString(); +} + +static inline QString toString(optLinkTimeCodeGenType option) +{ + switch (option) { + case optLTCGEnabled: + return "UseLinkTimeCodeGeneration"; + case optLTCGInstrument: + return "PGInstrument"; + case optLTCGOptimize: + return "PGOptimization"; + case optLTCGUpdate: + return "PGUpdate"; + } + return QString(); +} + +static inline QString toString(subSystemOption option) +{ + switch (option) { + case subSystemConsole: + return "Console"; + case subSystemWindows: + return "Windows"; + } + return QString(); +} + +static inline QString toString(machineTypeOption option) +{ + switch (option) { + case machineX86: + return "MachineX86"; + case machineX64: + return "MachineX64"; + } + return QString(); +} + +static inline QString toString(midlCharOption option) +{ + switch (option) { + case midlCharUnsigned: + return "Unsigned"; + case midlCharSigned: + return "Signed"; + case midlCharAscii7: + return "Ascii"; + } + return QString(); +} + +static inline QString toString(midlErrorCheckOption option) +{ + switch (option) { + case midlDisableAll: + return "None"; + case midlEnableAll: + return "All"; + } + return QString(); +} + +static inline QString toString(midlStructMemberAlignOption option) +{ + switch (option) { + case midlAlignSingleByte: + return "1"; + case midlAlignTwoBytes: + return "2"; + case midlAlignFourBytes: + return "4"; + case midlAlignEightBytes: + return "8"; + case midlAlignSixteenBytes: + return "16"; + } + return QString(); +} + +static inline QString toString(midlTargetEnvironment option) +{ + switch (option) { + case midlTargetWin32: + return "Win32"; + case midlTargetWin64: + return "X64"; + } + return QString(); +} + +static inline QString toString(midlWarningLevelOption option) +{ + switch (option) { + case midlWarningLevel_0: + return "0"; + case midlWarningLevel_1: + return "1"; + case midlWarningLevel_2: + return "2"; + case midlWarningLevel_3: + return "3"; + case midlWarningLevel_4: + return "4"; + } + return QString(); +} + +static inline QString toString(enumResourceLangID option) +{ + if (option == 0) + return QString(); + else + return QString::number(qlonglong(option)); +} + +static inline QString toString(charSet option) +{ + switch (option) { + case charSetNotSet: + return "NotSet"; + case charSetUnicode: + return "Unicode"; + case charSetMBCS: + return "MultiByte"; + } + return QString(); +} + +static inline QString toString(ConfigurationTypes option) +{ + switch (option) { + case typeApplication: + return "Application"; + case typeDynamicLibrary: + return "DynamicLibrary"; + case typeStaticLibrary: + return "StaticLibrary"; + } + return QString(); +} + +static inline QString toString(useOfATL option) +{ + switch (option) { + case useATLStatic: + return "Static"; + case useATLDynamic: + return "Dynamic"; + } + return QString(); +} + +static inline QString toString(useOfMfc option) +{ + switch (option) { + case useMfcStatic: + return "Static"; + case useMfcDynamic: + return "Dynamic"; + } + return QString(); +} + +static inline triState toTriState(browseInfoOption option) +{ + switch (option) + { + case brInfoNone: + return _False; + case brAllInfo: + case brNoLocalSymbols: + return _True; + } + return unset; +} + +static inline triState toTriState(preprocessOption option) +{ + switch (option) + { + case preprocessNo: + return _False; + case preprocessNoLineNumbers: + case preprocessYes: + return _True; + } + return unset; +} + +static inline triState toTriState(optFoldingType option) +{ + switch (option) + { + case optNoFolding: + return _False; + case optFolding: + return _True; + } + return unset; +} + +static inline triState toTriState(addressAwarenessType option) +{ + switch (option) + { + case addrAwareDefault: + return unset; + case addrAwareNoLarge: + return _False; + case addrAwareLarge: + return _True; + } + return unset; +} + +static inline triState toTriState(linkIncrementalType option) +{ + switch (option) + { + case linkIncrementalDefault: + return unset; + case linkIncrementalNo: + return _False; + case linkIncrementalYes: + return _True; + } + return unset; +} + +static inline triState toTriState(linkProgressOption option) +{ + switch (option) + { + case linkProgressNotSet: + return unset; + case linkProgressAll: + case linkProgressLibs: + return _True; + } + return unset; +} + +static inline triState toTriState(optRefType option) +{ + switch (option) + { + case optReferencesDefault: + return unset; + case optNoReferences: + return _False; + case optReferences: + return _True; + } + return unset; +} + +static inline triState toTriState(termSvrAwarenessType option) +{ + switch (option) + { + case termSvrAwareDefault: + return unset; + case termSvrAwareNo: + return _False; + case termSvrAwareYes: + return _True; + } + return unset; +} + +void VCXProjectWriter::write(XmlOutput &xml, const VCCLCompilerTool &tool) +{ + xml + << tag(_CLCompile) + << attrTagX(_AdditionalIncludeDirectories, tool.AdditionalIncludeDirectories, ";") + << attrTagX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrTagX(_AdditionalUsingDirectories, tool.AdditionalUsingDirectories, ";") +//unused << attrTagS(_AlwaysAppend, tool.AlwaysAppend) + << attrTagS(_AssemblerListingLocation, tool.AssemblerListingLocation) + << attrTagS(_AssemblerOutput, toString(tool.AssemblerOutput)) + << attrTagS(_BasicRuntimeChecks, toString(tool.BasicRuntimeChecks)) + << attrTagT(_BrowseInformation, toTriState(tool.BrowseInformation)) + << attrTagS(_BrowseInformationFile, tool.BrowseInformationFile) + << attrTagT(_BufferSecurityCheck, tool.BufferSecurityCheck) + << attrTagS(_CallingConvention, toString(tool.CallingConvention)) + << attrTagS(_CompileAs, toString(tool.CompileAs)) + << attrTagS(_CompileAsManaged, toString(tool.CompileAsManaged)) + << attrTagT(_CreateHotpatchableImage, tool.CreateHotpatchableImage) + << attrTagS(_DebugInformationFormat, toString(tool.DebugInformationFormat)) + << attrTagT(_DisableLanguageExtensions, tool.DisableLanguageExtensions) + << attrTagX(_DisableSpecificWarnings, tool.DisableSpecificWarnings, ";") + << attrTagS(_EnableEnhancedInstructionSet, toString(tool.EnableEnhancedInstructionSet)) + << attrTagT(_EnableFiberSafeOptimizations, tool.EnableFiberSafeOptimizations) + << attrTagT(_EnablePREfast, tool.EnablePREfast) + << attrTagS(_ErrorReporting, tool.ErrorReporting) + << attrTagS(_ExceptionHandling, toString(tool.ExceptionHandling)) + << attrTagT(_ExpandAttributedSource, tool.ExpandAttributedSource) + << attrTagS(_FavorSizeOrSpeed, toString(tool.FavorSizeOrSpeed)) + << attrTagT(_FloatingPointExceptions, tool.FloatingPointExceptions) + << attrTagS(_FloatingPointModel, toString(tool.FloatingPointModel)) + << attrTagT(_ForceConformanceInForLoopScope, tool.ForceConformanceInForLoopScope) + << attrTagX(_ForcedIncludeFiles, tool.ForcedIncludeFiles, ";") + << attrTagX(_ForcedUsingFiles, tool.ForcedUsingFiles, ";") + << attrTagT(_FunctionLevelLinking, tool.EnableFunctionLevelLinking) + << attrTagT(_GenerateXMLDocumentationFiles, tool.GenerateXMLDocumentationFiles) + << attrTagT(_IgnoreStandardIncludePath, tool.IgnoreStandardIncludePath) + << attrTagS(_InlineFunctionExpansion, toString(tool.InlineFunctionExpansion)) + << attrTagT(_IntrinsicFunctions, tool.EnableIntrinsicFunctions) + << attrTagT(_MinimalRebuild, tool.MinimalRebuild) + << attrTagT(_MultiProcessorCompilation, tool.MultiProcessorCompilation) + << attrTagS(_ObjectFileName, tool.ObjectFile) +//unused << attrTagX(_ObjectFiles, tool.ObjectFiles, ";") + << attrTagT(_OmitDefaultLibName, tool.OmitDefaultLibName) + << attrTagT(_OmitFramePointers, tool.OmitFramePointers) + << attrTagT(_OpenMPSupport, tool.OpenMP) + << attrTagS(_Optimization, toString(tool.Optimization)) + << attrTagS(_PrecompiledHeader, toString(tool.UsePrecompiledHeader)) + << attrTagS(_PrecompiledHeaderFile, tool.PrecompiledHeaderThrough) + << attrTagS(_PrecompiledHeaderOutputFile, tool.PrecompiledHeaderFile) + << attrTagT(_PreprocessKeepComments, tool.KeepComments) + << attrTagX(_PreprocessorDefinitions, tool.PreprocessorDefinitions, ";") + << attrTagS(_PreprocessOutputPath, tool.PreprocessOutputPath) + << attrTagT(_PreprocessSuppressLineNumbers, tool.PreprocessSuppressLineNumbers) + << attrTagT(_PreprocessToFile, toTriState(tool.GeneratePreprocessedFile)) + << attrTagS(_ProgramDataBaseFileName, tool.ProgramDataBaseFileName) + << attrTagS(_ProcessorNumber, tool.MultiProcessorCompilationProcessorCount) + << attrTagS(_RuntimeLibrary, toString(tool.RuntimeLibrary)) + << attrTagT(_RuntimeTypeInfo, tool.RuntimeTypeInfo) + << attrTagT(_ShowIncludes, tool.ShowIncludes) + << attrTagT(_SmallerTypeCheck, tool.SmallerTypeCheck) + << attrTagT(_StringPooling, tool.StringPooling) + << attrTagS(_StructMemberAlignment, toString(tool.StructMemberAlignment)) + << attrTagT(_SuppressStartupBanner, tool.SuppressStartupBanner) +//unused << attrTagS(_TreatSpecificWarningsAsErrors, tool.TreatSpecificWarningsAsErrors) + << attrTagT(_TreatWarningAsError, tool.WarnAsError) + << attrTagT(_TreatWChar_tAsBuiltInType, tool.TreatWChar_tAsBuiltInType) + << attrTagT(_UndefineAllPreprocessorDefinitions, tool.UndefineAllPreprocessorDefinitions) + << attrTagX(_UndefinePreprocessorDefinitions, tool.UndefinePreprocessorDefinitions, ";") + << attrTagT(_UseFullPaths, tool.DisplayFullPaths) + << attrTagT(_UseUnicodeForAssemblerListing, tool.UseUnicodeForAssemblerListing) + << attrTagS(_WarningLevel, toString(tool.WarningLevel)) + << attrTagT(_WholeProgramOptimization, tool.WholeProgramOptimization) + << attrTagS(_XMLDocumentationFileName, tool.XMLDocumentationFileName) + << closetag(_CLCompile); +} + +void VCXProjectWriter::write(XmlOutput &xml, const VCLinkerTool &tool) +{ + xml + << tag(_Link) + << attrTagX(_AdditionalDependencies, tool.AdditionalDependencies, ";") + << attrTagX(_AdditionalLibraryDirectories, tool.AdditionalLibraryDirectories, ";") + << attrTagX(_AdditionalManifestDependencies, tool.AdditionalManifestDependencies, ";") + << attrTagX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrTagX(_AddModuleNamesToAssembly, tool.AddModuleNamesToAssembly, ";") + << attrTagT(_AllowIsolation, tool.AllowIsolation) + << attrTagT(_AssemblyDebug, tool.AssemblyDebug) + << attrTagX(_AssemblyLinkResource, tool.AssemblyLinkResource, ";") + << attrTagS(_BaseAddress, tool.BaseAddress) + << attrTagS(_CLRImageType, tool.CLRImageType) + << attrTagS(_CLRSupportLastError, tool.CLRSupportLastError) + << attrTagS(_CLRThreadAttribute, tool.CLRThreadAttribute) + << attrTagT(_CLRUnmanagedCodeCheck, tool.CLRUnmanagedCodeCheck) +//unused << attrTagS(_CreateHotPatchableImage, tool.CreateHotPatchableImage) + << attrTagT(_DataExecutionPrevention, tool.DataExecutionPrevention) + << attrTagX(_DelayLoadDLLs, tool.DelayLoadDLLs, ";") + << attrTagT(_DelaySign, tool.DelaySign) + << attrTagS(_EmbedManagedResourceFile, tool.LinkToManagedResourceFile) + << attrTagT(_EnableCOMDATFolding, toTriState(tool.EnableCOMDATFolding)) + << attrTagT(_EnableUAC, tool.EnableUAC) + << attrTagS(_EntryPointSymbol, tool.EntryPointSymbol) + << attrTagX(_ForceSymbolReferences, tool.ForceSymbolReferences, ";") + << attrTagS(_FunctionOrder, tool.FunctionOrder) + << attrTagT(_GenerateDebugInformation, tool.GenerateDebugInformation) + << attrTagT(_GenerateManifest, tool.GenerateManifest) + << attrTagT(_GenerateMapFile, tool.GenerateMapFile) + << attrTagL(_HeapCommitSize, tool.HeapCommitSize, /*ifNot*/ -1) + << attrTagL(_HeapReserveSize, tool.HeapReserveSize, /*ifNot*/ -1) + << attrTagT(_IgnoreAllDefaultLibraries, tool.IgnoreAllDefaultLibraries) + << attrTagT(_IgnoreEmbeddedIDL, tool.IgnoreEmbeddedIDL) + << attrTagT(_IgnoreImportLibrary, tool.IgnoreImportLibrary) + << attrTagX(_IgnoreSpecificDefaultLibraries, tool.IgnoreDefaultLibraryNames, ";") + << attrTagS(_ImportLibrary, tool.ImportLibrary) + << attrTagS(_KeyContainer, tool.KeyContainer) + << attrTagS(_KeyFile, tool.KeyFile) + << attrTagT(_LargeAddressAware, toTriState(tool.LargeAddressAware)) + << attrTagT(_LinkDLL, (tool.config->ConfigurationType == typeDynamicLibrary ? _True : unset)) + << attrTagS(_LinkErrorReporting, tool.LinkErrorReporting) + << attrTagT(_LinkIncremental, toTriState(tool.LinkIncremental)) + << attrTagT(_LinkStatus, toTriState(tool.ShowProgress)) + << attrTagS(_LinkTimeCodeGeneration, toString(tool.LinkTimeCodeGeneration)) + << attrTagS(_ManifestFile, tool.ManifestFile) + << attrTagT(_MapExports, tool.MapExports) + << attrTagS(_MapFileName, tool.MapFileName) + << attrTagS(_MergedIDLBaseFileName, tool.MergedIDLBaseFileName) + << attrTagS(_MergeSections, tool.MergeSections) + << attrTagS(_MidlCommandFile, tool.MidlCommandFile) + << attrTagS(_ModuleDefinitionFile, tool.ModuleDefinitionFile) + << attrTagT(_NoEntryPoint, tool.ResourceOnlyDLL) + << attrTagT(_OptimizeReferences, toTriState(tool.OptimizeReferences)) + << attrTagS(_OutputFile, tool.OutputFile) + << attrTagT(_PreventDllBinding, tool.PreventDllBinding) + << attrTagS(_ProgramDatabaseFile, tool.ProgramDatabaseFile) + << attrTagT(_RandomizedBaseAddress, tool.RandomizedBaseAddress) + << attrTagT(_RegisterOutput, tool.RegisterOutput) + << attrTagL(_SectionAlignment, tool.SectionAlignment, /*ifNot*/ -1) + << attrTagT(_SetChecksum, tool.SetChecksum) + << attrTagL(_StackCommitSize, tool.StackCommitSize, /*ifNot*/ -1) + << attrTagL(_StackReserveSize, tool.StackReserveSize, /*ifNot*/ -1) + << attrTagS(_StripPrivateSymbols, tool.StripPrivateSymbols) + << attrTagS(_SubSystem, toString(tool.SubSystem)) +// << attrTagT(_SupportNobindOfDelayLoadedDLL, tool.SupportNobindOfDelayLoadedDLL) + << attrTagT(_SupportUnloadOfDelayLoadedDLL, tool.SupportUnloadOfDelayLoadedDLL) + << attrTagT(_SuppressStartupBanner, tool.SuppressStartupBanner) + << attrTagT(_SwapRunFromCD, tool.SwapRunFromCD) + << attrTagT(_SwapRunFromNet, tool.SwapRunFromNet) + << attrTagS(_TargetMachine, toString(tool.TargetMachine)) + << attrTagT(_TerminalServerAware, toTriState(tool.TerminalServerAware)) + << attrTagT(_TreatLinkerWarningAsErrors, tool.TreatWarningsAsErrors) + << attrTagT(_TurnOffAssemblyGeneration, tool.TurnOffAssemblyGeneration) + << attrTagS(_TypeLibraryFile, tool.TypeLibraryFile) + << attrTagL(_TypeLibraryResourceID, tool.TypeLibraryResourceID, /*ifNot*/ 0) + << attrTagS(_UACExecutionLevel, tool.UACExecutionLevel) + << attrTagT(_UACUIAccess, tool.UACUIAccess) + << attrTagS(_Version, tool.Version) + << closetag(_Link); +} + +void VCXProjectWriter::write(XmlOutput &xml, const VCMIDLTool &tool) +{ + xml + << tag(_Midl) + << attrTagX(_AdditionalIncludeDirectories, tool.AdditionalIncludeDirectories, ";") + << attrTagX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrTagT(_ApplicationConfigurationMode, tool.ApplicationConfigurationMode) + << attrTagS(_ClientStubFile, tool.ClientStubFile) + << attrTagX(_CPreprocessOptions, tool.CPreprocessOptions, " ") + << attrTagS(_DefaultCharType, toString(tool.DefaultCharType)) + << attrTagS(_DLLDataFileName, tool.DLLDataFileName) + << attrTagS(_EnableErrorChecks, toString(tool.EnableErrorChecks)) + << attrTagT(_ErrorCheckAllocations, tool.ErrorCheckAllocations) + << attrTagT(_ErrorCheckBounds, tool.ErrorCheckBounds) + << attrTagT(_ErrorCheckEnumRange, tool.ErrorCheckEnumRange) + << attrTagT(_ErrorCheckRefPointers, tool.ErrorCheckRefPointers) + << attrTagT(_ErrorCheckStubData, tool.ErrorCheckStubData) + << attrTagS(_GenerateClientFiles, tool.GenerateClientFiles) + << attrTagS(_GenerateServerFiles, tool.GenerateServerFiles) + << attrTagT(_GenerateStublessProxies, tool.GenerateStublessProxies) + << attrTagT(_GenerateTypeLibrary, tool.GenerateTypeLibrary) + << attrTagS(_HeaderFileName, tool.HeaderFileName) + << attrTagT(_IgnoreStandardIncludePath, tool.IgnoreStandardIncludePath) + << attrTagS(_InterfaceIdentifierFileName, tool.InterfaceIdentifierFileName) + << attrTagL(_LocaleID, tool.LocaleID, /*ifNot*/ -1) + << attrTagT(_MkTypLibCompatible, tool.MkTypLibCompatible) + << attrTagS(_OutputDirectory, tool.OutputDirectory) + << attrTagX(_PreprocessorDefinitions, tool.PreprocessorDefinitions, ";") + << attrTagS(_ProxyFileName, tool.ProxyFileName) + << attrTagS(_RedirectOutputAndErrors, tool.RedirectOutputAndErrors) + << attrTagS(_ServerStubFile, tool.ServerStubFile) + << attrTagS(_StructMemberAlignment, toString(tool.StructMemberAlignment)) + << attrTagT(_SuppressCompilerWarnings, tool.SuppressCompilerWarnings) + << attrTagT(_SuppressStartupBanner, tool.SuppressStartupBanner) + << attrTagS(_TargetEnvironment, toString(tool.TargetEnvironment)) + << attrTagS(_TypeLibFormat, tool.TypeLibFormat) + << attrTagS(_TypeLibraryName, tool.TypeLibraryName) + << attrTagX(_UndefinePreprocessorDefinitions, tool.UndefinePreprocessorDefinitions, ";") + << attrTagT(_ValidateAllParameters, tool.ValidateAllParameters) + << attrTagT(_WarnAsError, tool.WarnAsError) + << attrTagS(_WarningLevel, toString(tool.WarningLevel)) + << closetag(_Midl); +} + +void VCXProjectWriter::write(XmlOutput &xml, const VCCustomBuildTool &tool) +{ + const QString &configName = tool.config->Name; + + if ( !tool.AdditionalDependencies.isEmpty() ) + { + xml << tag("AdditionalInputs") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(configName)) + << valueTagDefX(tool.AdditionalDependencies, "AdditionalInputs", ";"); + } + + if( !tool.CommandLine.isEmpty() ) + { + xml << tag("Command") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(configName)) + << valueTag(tool.CommandLine.join(vcxCommandSeparator())); + } + + if ( !tool.Description.isEmpty() ) + { + xml << tag("Message") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(configName)) + << valueTag(tool.Description); + } + + if ( !tool.Outputs.isEmpty() ) + { + xml << tag("Outputs") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(configName)) + << valueTagDefX(tool.Outputs, "Outputs", ";"); + } +} + +void VCXProjectWriter::write(XmlOutput &xml, const VCLibrarianTool &tool) +{ + xml + << tag(_Link) + << attrTagX(_AdditionalDependencies, tool.AdditionalDependencies, ";") + << attrTagX(_AdditionalLibraryDirectories, tool.AdditionalLibraryDirectories, ";") + << attrTagX(_AdditionalOptions, tool.AdditionalOptions, " ") +//unused << attrTagS(_DisplayLibrary, tool.DisplayLibrary) +//unused << attrTagS(_ErrorReporting, tool.ErrorReporting) + << attrTagX(_ExportNamedFunctions, tool.ExportNamedFunctions, ";") + << attrTagX(_ForceSymbolReferences, tool.ForceSymbolReferences, ";") + << attrTagT(_IgnoreAllDefaultLibraries, tool.IgnoreAllDefaultLibraries) + << attrTagX(_IgnoreSpecificDefaultLibraries, tool.IgnoreDefaultLibraryNames, ";") +//unused << attrTagT(_LinkTimeCodeGeneration, tool.LinkTimeCodeGeneration) + << attrTagS(_ModuleDefinitionFile, tool.ModuleDefinitionFile) +//unused << attrTagS(_Name, tool.Name) + << attrTagS(_OutputFile, tool.OutputFile) +//unused << attrTagX(_RemoveObjects, tool.RemoveObjects, ";") +//unused << attrTagS(_SubSystem, tool.SubSystem) + << attrTagT(_SuppressStartupBanner, tool.SuppressStartupBanner) +//unused << attrTagS(_TargetMachine, tool.TargetMachine) +//unused << attrTagT(_TreatLibWarningAsErrors, tool.TreatLibWarningAsErrors) +//unused << attrTagT(_Verbose, tool.Verbose) + << closetag(_Link); +} + +void VCXProjectWriter::write(XmlOutput &xml, const VCResourceCompilerTool &tool) +{ + xml + << tag(_ResourceCompile) + << attrTagX(_AdditionalIncludeDirectories, tool.AdditionalIncludeDirectories, ";") + << attrTagX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrTagS(_Culture, toString(tool.Culture)) + << attrTagT(_IgnoreStandardIncludePath, tool.IgnoreStandardIncludePath) +//unused << attrTagT(_NullTerminateStrings, tool.NullTerminateStrings) + << attrTagX(_PreprocessorDefinitions, tool.PreprocessorDefinitions, ";") + << attrTagS(_ResourceOutputFileName, tool.ResourceOutputFileName) + << attrTagT(_ShowProgress, toTriState(tool.ShowProgress)) + << attrTagT(_SuppressStartupBanner, tool.SuppressStartupBanner) +//unused << attrTagS(_TrackerLogDirectory, tool.TrackerLogDirectory) +//unused << attrTagS(_UndefinePreprocessorDefinitions, tool.UndefinePreprocessorDefinitions) + << closetag(_ResourceCompile); +} + +void VCXProjectWriter::write(XmlOutput &xml, const VCEventTool &tool) +{ + xml + << tag(tool.EventName) + << attrTagS(_Command, tool.CommandLine.join(vcxCommandSeparator())) + << attrTagS(_Message, tool.Description) + << closetag(tool.EventName); +} + +void VCXProjectWriter::write(XmlOutput &xml, const VCDeploymentTool &tool) +{ + Q_UNUSED(xml); + Q_UNUSED(tool); + // SmartDevice deployment not supported in VS 2010 +} + +void VCXProjectWriter::write(XmlOutput &xml, const VCConfiguration &tool) +{ + xml << tag("PropertyGroup") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(tool.Name)) + << attrTag("Label", "Configuration") + << attrTagS(_OutputDirectory, tool.OutputDirectory) + << attrTagT(_ATLMinimizesCRunTimeLibraryUsage, tool.ATLMinimizesCRunTimeLibraryUsage) + << attrTagT(_BuildBrowserInformation, tool.BuildBrowserInformation) + << attrTagS(_CharacterSet, toString(tool.CharacterSet)) + << attrTagS(_ConfigurationType, toString(tool.ConfigurationType)) + << attrTagS(_DeleteExtensionsOnClean, tool.DeleteExtensionsOnClean) + << attrTagS(_ImportLibrary, tool.ImportLibrary) + << attrTagS(_IntermediateDirectory, tool.IntermediateDirectory) + << attrTagS(_PrimaryOutput, tool.PrimaryOutput) + << attrTagS(_ProgramDatabase, tool.ProgramDatabase) + << attrTagT(_RegisterOutput, tool.RegisterOutput) + << attrTagS(_UseOfATL, toString(tool.UseOfATL)) + << attrTagS(_UseOfMfc, toString(tool.UseOfMfc)) + << attrTagT(_WholeProgramOptimization, tool.WholeProgramOptimization) + << closetag(); +} + +void VCXProjectWriter::write(XmlOutput &xml, VCFilter &tool) +{ + Q_UNUSED(xml); + Q_UNUSED(tool); + // unused in this generator +} + +void VCXProjectWriter::addFilters(VCProject &project, XmlOutput &xmlFilter, const QString &filtername) +{ + bool added = false; + + for (int i = 0; i < project.SingleProjects.count(); ++i) { + VCFilter filter; + const VCProjectSingleConfig &singleCfg = project.SingleProjects.at(i); + if (filtername == "Root Files") { + filter = singleCfg.RootFiles; + } else if (filtername == "Source Files") { + filter = singleCfg.SourceFiles; + } else if (filtername == "Header Files") { + filter = singleCfg.HeaderFiles; + } else if (filtername == "Generated Files") { + filter = singleCfg.GeneratedFiles; + } else if (filtername == "LexYacc Files") { + filter = singleCfg.LexYaccFiles; + } else if (filtername == "Translation Files") { + filter = singleCfg.TranslationFiles; + } else if (filtername == "Form Files") { + filter = singleCfg.FormFiles; + } else if (filtername == "Resource Files") { + filter = singleCfg.ResourceFiles; + } else { + // ExtraCompilers + filter = project.SingleProjects[i].filterForExtraCompiler(filtername); + } + + if(!filter.Files.isEmpty() && !added) { + xmlFilter << tag("Filter") + << attrTag("Include", filtername) + << attrTagS("UniqueIdentifier", filter.Guid) + << attrTagS("Extensions", filter.Filter) + << attrTagT("ParseFiles", filter.ParseFiles) + << closetag(); + } + } +} + +// outputs a given filter for all existing configurations of a project +void VCXProjectWriter::outputFilter(VCProject &project, XmlOutput &xml, XmlOutput &xmlFilter, const QString &filtername) +{ + XNode *root; + if (project.SingleProjects.at(0).flat_files) + root = new XFlatNode; + else + root = new XTreeNode; + + QString name, extfilter; + triState parse; + + for (int i = 0; i < project.SingleProjects.count(); ++i) { + VCFilter filter; + const VCProjectSingleConfig &singleCfg = project.SingleProjects.at(i); + if (filtername == "Root Files") { + filter = singleCfg.RootFiles; + } else if (filtername == "Source Files") { + filter = singleCfg.SourceFiles; + } else if (filtername == "Header Files") { + filter = singleCfg.HeaderFiles; + } else if (filtername == "Generated Files") { + filter = singleCfg.GeneratedFiles; + } else if (filtername == "LexYacc Files") { + filter = singleCfg.LexYaccFiles; + } else if (filtername == "Translation Files") { + filter = singleCfg.TranslationFiles; + } else if (filtername == "Form Files") { + filter = singleCfg.FormFiles; + } else if (filtername == "Resource Files") { + filter = singleCfg.ResourceFiles; + } else { + // ExtraCompilers + filter = project.SingleProjects[i].filterForExtraCompiler(filtername); + } + + // Merge all files in this filter to root tree + for (int x = 0; x < filter.Files.count(); ++x) + root->addElement(filter.Files.at(x)); + + // Save filter setting from first filter. Next filters + // may differ but we cannot handle that. (ex. extfilter) + if (name.isEmpty()) { + name = filter.Name; + extfilter = filter.Filter; + parse = filter.ParseFiles; + } + } + + if (!root->hasElements()) + return; + + root->generateXML(xml, xmlFilter, "", project, filtername); // output root tree +} + +// Output all configurations (by filtername) for a file (by info) +// A filters config output is in VCFilter.outputFileConfig() +void VCXProjectWriter::outputFileConfigs(VCProject &project, XmlOutput &xml, XmlOutput &xmlFilter, const VCFilterFile &info, const QString &filtername) +{ + // We need to check if the file has any custom build step. + // If there is one then it has to be included with "CustomBuild Include" + bool fileAdded = false; + + for (int i = 0; i < project.SingleProjects.count(); ++i) { + VCFilter filter; + const VCProjectSingleConfig &singleCfg = project.SingleProjects.at(i); + if (filtername.startsWith("Root Files")) { + filter = singleCfg.RootFiles; + } else if (filtername.startsWith("Source Files")) { + filter = singleCfg.SourceFiles; + } else if (filtername.startsWith("Header Files")) { + filter = singleCfg.HeaderFiles; + } else if (filtername.startsWith("Generated Files")) { + filter = singleCfg.GeneratedFiles; + } else if (filtername.startsWith("LexYacc Files")) { + filter = singleCfg.LexYaccFiles; + } else if (filtername.startsWith("Translation Files")) { + filter = singleCfg.TranslationFiles; + } else if (filtername.startsWith("Form Files")) { + filter = singleCfg.FormFiles; + } else if (filtername.startsWith("Resource Files")) { + filter = singleCfg.ResourceFiles; + } else { + // ExtraCompilers + filter = project.SingleProjects[i].filterForExtraCompiler(filtername); + } + + if (filter.Config) // only if the filter is not empty + if (outputFileConfig(filter, xml, xmlFilter, info.file, filtername, fileAdded)) // only add it once. + fileAdded = true; + } + + if ( !fileAdded ) + { + if (filtername.startsWith("Source Files")) { + + xmlFilter << tag("ClCompile") + << attrTag("Include",Option::fixPathToLocalOS(info.file)) + << attrTagS("Filter", filtername); + + xml << tag("ClCompile") + << attrTag("Include",Option::fixPathToLocalOS(info.file)); + + } else if(filtername.startsWith("Header Files")) { + + xmlFilter << tag("ClInclude") + << attrTag("Include",Option::fixPathToLocalOS(info.file)) + << attrTagS("Filter", filtername); + + xml << tag("ClInclude") + << attrTag("Include",Option::fixPathToLocalOS(info.file)); + } else if(filtername.startsWith("Generated Files") || filtername.startsWith("Form Files")) { + + if (info.file.endsWith(".h")) { + + xmlFilter << tag("ClInclude") + << attrTag("Include",Option::fixPathToLocalOS(info.file)) + << attrTagS("Filter", filtername); + + xml << tag("ClInclude") + << attrTag("Include",Option::fixPathToLocalOS(info.file)); + } else if(info.file.endsWith(".cpp")) { + + xmlFilter << tag("ClCompile") + << attrTag("Include",Option::fixPathToLocalOS(info.file)) + << attrTagS("Filter", filtername); + + xml << tag("ClCompile") + << attrTag("Include",Option::fixPathToLocalOS(info.file)); + } else if(info.file.endsWith(".res")) { + + xmlFilter << tag("CustomBuild") + << attrTag("Include",Option::fixPathToLocalOS(info.file)) + << attrTagS("Filter", filtername); + + xml << tag("CustomBuild") + << attrTag("Include",Option::fixPathToLocalOS(info.file)); + } else { + + xmlFilter << tag("CustomBuild") + << attrTag("Include",Option::fixPathToLocalOS(info.file)) + << attrTagS("Filter", filtername); + + xml << tag("CustomBuild") + << attrTag("Include",Option::fixPathToLocalOS(info.file)); + } + + } else if(filtername.startsWith("Root Files")) { + + if (info.file.endsWith(".rc")) { + + xmlFilter << tag("ResourceCompile") + << attrTag("Include",Option::fixPathToLocalOS(info.file)); + + xml << tag("ResourceCompile") + << attrTag("Include",Option::fixPathToLocalOS(info.file)); + } + } else { + + xmlFilter << tag("None") + << attrTag("Include",Option::fixPathToLocalOS(info.file)) + << attrTagS("Filter", filtername); + + xml << tag("None") + << attrTag("Include",Option::fixPathToLocalOS(info.file)); + } + } + + xml << closetag(); + xmlFilter << closetag(); +} + +bool VCXProjectWriter::outputFileConfig(VCFilter &filter, XmlOutput &xml, XmlOutput &xmlFilter, const QString &filename, const QString &filtername, bool fileAllreadyAdded) +{ + bool fileAdded = false; + + // Clearing each filter tool + filter.useCustomBuildTool = false; + filter.useCompilerTool = false; + filter.CustomBuildTool = VCCustomBuildTool(); + filter.CompilerTool = VCCLCompilerTool(); + + // Unset some default options + filter.CustomBuildTool.config = filter.Config; + filter.CompilerTool.BufferSecurityCheck = unset; + filter.CompilerTool.DebugInformationFormat = debugUnknown; + filter.CompilerTool.ExceptionHandling = ehDefault; + filter.CompilerTool.ProgramDataBaseFileName.clear(); + filter.CompilerTool.RuntimeLibrary = rtUnknown; + filter.CompilerTool.config = filter.Config; + + bool inBuild = false; + VCFilterFile info; + for (int i = 0; i < filter.Files.count(); ++i) { + if (filter.Files.at(i).file == filename) { + info = filter.Files.at(i); + inBuild = true; + } + } + inBuild &= !info.excludeFromBuild; + + if (inBuild) { + filter.addExtraCompiler(info); + if (filter.Project->usePCH) + filter.modifyPCHstage(info.file); + } else { + // Excluded files uses an empty compiler stage + if(info.excludeFromBuild) + filter.useCompilerTool = true; + } + + // Actual XML output ---------------------------------- + if (filter.useCustomBuildTool || filter.useCompilerTool || !inBuild) { + + if (filter.useCustomBuildTool) + { + if ( !fileAllreadyAdded ) { + + fileAdded = true; + + xmlFilter << tag("CustomBuild") + << attrTag("Include",Option::fixPathToLocalOS(filename)) + << attrTagS("Filter", filtername); + + xml << tag("CustomBuild") + << attrTag("Include",Option::fixPathToLocalOS(filename)); + + if ( filtername.startsWith("Form Files") || filtername.startsWith("Generated Files") || filtername.startsWith("Resource Files") ) + xml << attrTagS("FileType", "Document"); + } + + filter.Project->projectWriter->write(xml, filter.CustomBuildTool); + } + + if ( !fileAdded && !fileAllreadyAdded ) + { + fileAdded = true; + + if (filtername.startsWith("Source Files")) { + + xmlFilter << tag("ClCompile") + << attrTag("Include",Option::fixPathToLocalOS(filename)) + << attrTagS("Filter", filtername); + + xml << tag("ClCompile") + << attrTag("Include",Option::fixPathToLocalOS(filename)); + + } else if(filtername.startsWith("Header Files")) { + + xmlFilter << tag("ClInclude") + << attrTag("Include",Option::fixPathToLocalOS(filename)) + << attrTagS("Filter", filtername); + + xml << tag("ClInclude") + << attrTag("Include",Option::fixPathToLocalOS(filename)); + } else if(filtername.startsWith("Generated Files") || filtername.startsWith("Form Files")) { + + if (filename.endsWith(".h")) { + + xmlFilter << tag("ClInclude") + << attrTag("Include",Option::fixPathToLocalOS(filename)) + << attrTagS("Filter", filtername); + + xml << tag("ClInclude") + << attrTag("Include",Option::fixPathToLocalOS(filename)); + } else if(filename.endsWith(".cpp")) { + + xmlFilter << tag("ClCompile") + << attrTag("Include",Option::fixPathToLocalOS(filename)) + << attrTagS("Filter", filtername); + + xml << tag("ClCompile") + << attrTag("Include",Option::fixPathToLocalOS(filename)); + } else if(filename.endsWith(".res")) { + + xmlFilter << tag("CustomBuild") + << attrTag("Include",Option::fixPathToLocalOS(filename)) + << attrTagS("Filter", filtername); + + xml << tag("CustomBuild") + << attrTag("Include",Option::fixPathToLocalOS(filename)); + } else { + + xmlFilter << tag("CustomBuild") + << attrTag("Include",Option::fixPathToLocalOS(filename)) + << attrTagS("Filter", filtername); + + xml << tag("CustomBuild") + << attrTag("Include",Option::fixPathToLocalOS(filename)); + } + } else if(filtername.startsWith("Root Files")) { + + if (filename.endsWith(".rc")) { + + xmlFilter << tag("ResourceCompile") + << attrTag("Include",Option::fixPathToLocalOS(filename)); + + xml << tag("ResourceCompile") + << attrTag("Include",Option::fixPathToLocalOS(filename)); + } + } + } + + if(!inBuild) { + + xml << tag("ExcludedFromBuild") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(filter.Config->Name)) + << valueTag("true"); + } + + if (filter.useCompilerTool) { + + if ( !filter.CompilerTool.ForcedIncludeFiles.isEmpty() ) { + xml << tag("ForcedIncludeFiles") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(filter.Config->Name)) + << valueTagX(filter.CompilerTool.ForcedIncludeFiles); + } + + if ( !filter.CompilerTool.PrecompiledHeaderThrough.isEmpty() ) { + + xml << tag("PrecompiledHeaderFile") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(filter.Config->Name)) + << valueTag(filter.CompilerTool.PrecompiledHeaderThrough) + << tag("PrecompiledHeader") + << attrTag("Condition", QString("'$(Configuration)|$(Platform)'=='%1'").arg(filter.Config->Name)) + << valueTag(toString(filter.CompilerTool.UsePrecompiledHeader)); + } + } + } + + return fileAdded; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/msbuild_objectmodel.h b/qmake/generators/win32/msbuild_objectmodel.h new file mode 100644 index 0000000000..c80a2708fa --- /dev/null +++ b/qmake/generators/win32/msbuild_objectmodel.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MSBUILD_OBJECTMODEL_H +#define MSBUILD_OBJECTMODEL_H + +#include "project.h" +#include "xmloutput.h" +#include "msvc_objectmodel.h" +#include +#include +#include +#include +#include +#include + +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 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){ + 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() { + 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); + bool hasElements() { + return children.size() != 0; + } +}; + +class XFlatNode : public XNode +{ + typedef QMap 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){ + 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() { + children.clear(); + } + + void generateXML(XmlOutput &xml, XmlOutput &xmlFilter, const QString &tagName, VCProject &proj, const QString &filter); + bool hasElements() { + return children.size() != 0; + } +}; + +class VCXProjectWriter : public VCProjectWriter +{ +public: + void write(XmlOutput &, VCProjectSingleConfig &); + void write(XmlOutput &, VCProject &); + + void write(XmlOutput &, const VCCLCompilerTool &); + void write(XmlOutput &, const VCLinkerTool &); + void write(XmlOutput &, const VCMIDLTool &); + void write(XmlOutput &, const VCCustomBuildTool &); + void write(XmlOutput &, const VCLibrarianTool &); + void write(XmlOutput &, const VCResourceCompilerTool &); + void write(XmlOutput &, const VCEventTool &); + void write(XmlOutput &, const VCDeploymentTool &); + void write(XmlOutput &, const VCConfiguration &); + void write(XmlOutput &, VCFilter &); + +private: + 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(VCFilter &filter, XmlOutput &xml, XmlOutput &xmlFilter, const QString &filename, const QString &filtername, bool fileAllreadyAdded); + + friend class XTreeNode; + friend class XFlatNode; +}; + +QT_END_NAMESPACE + +#endif // MSVC_OBJECTMODEL_H diff --git a/qmake/generators/win32/msvc_nmake.cpp b/qmake/generators/win32/msvc_nmake.cpp new file mode 100644 index 0000000000..c55806d002 --- /dev/null +++ b/qmake/generators/win32/msvc_nmake.cpp @@ -0,0 +1,361 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "msvc_nmake.h" +#include "option.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +NmakeMakefileGenerator::NmakeMakefileGenerator() : Win32MakefileGenerator(), init_flag(false) +{ + +} + +bool +NmakeMakefileGenerator::writeMakefile(QTextStream &t) +{ + writeHeader(t); + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + QStringList &qut = project->values("QMAKE_EXTRA_TARGETS"); + for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it) + t << *it << " "; + t << "all first clean:" << "\n\t" + << "@echo \"Some of the required modules (" + << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t" + << "@echo \"Skipped.\"" << endl << endl; + writeMakeQmake(t); + return true; + } + + if(project->first("TEMPLATE") == "app" || + project->first("TEMPLATE") == "lib") { +#if 0 + if(Option::mkfile::do_stub_makefile) + return MakefileGenerator::writeStubMakefile(t); +#endif + writeNmakeParts(t); + return MakefileGenerator::writeMakefile(t); + } + else if(project->first("TEMPLATE") == "subdirs") { + writeSubDirs(t); + return true; + } + return false; +} + +QString NmakeMakefileGenerator::getPdbTarget() +{ + return QString(project->first("TARGET") + project->first("TARGET_VERSION_EXT") + ".pdb"); +} + +QString NmakeMakefileGenerator::defaultInstall(const QString &t) +{ + if((t != "target" && t != "dlltarget") || + (t == "dlltarget" && (project->first("TEMPLATE") != "lib" || !project->isActiveConfig("shared"))) || + project->first("TEMPLATE") == "subdirs") + return QString(); + + QString ret = Win32MakefileGenerator::defaultInstall(t); + + const QString root = "$(INSTALL_ROOT)"; + QStringList &uninst = project->values(t + ".uninstall"); + QString targetdir = Option::fixPathToTargetOS(project->first(t + ".path"), false); + targetdir = fileFixify(targetdir, FileFixifyAbsolute); + if(targetdir.right(1) != Option::dir_sep) + targetdir += Option::dir_sep; + + if(t == "target" && project->first("TEMPLATE") == "lib") { + if(project->isActiveConfig("shared") && project->isActiveConfig("debug")) { + QString pdb_target = getPdbTarget(); + pdb_target.remove('"'); + 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)") + " \"" + src_targ + "\" \"" + dst_targ + "\""; + if(!uninst.isEmpty()) + uninst.append("\n\t"); + uninst.append("-$(DEL_FILE) \"" + dst_targ + "\""); + } + } + + return ret; +} + +QStringList &NmakeMakefileGenerator::findDependencies(const QString &file) +{ + QStringList &aList = MakefileGenerator::findDependencies(file); + // Note: The QMAKE_IMAGE_COLLECTION file have all images + // as dependency, so don't add precompiled header then + if (file == project->first("QMAKE_IMAGE_COLLECTION")) + return aList; + 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; + } + } + return aList; +} + +void NmakeMakefileGenerator::writeNmakeParts(QTextStream &t) +{ + writeStandardParts(t); + + // precompiled header + if(usePCH) { + QString precompRule = QString("-c -Yc -Fp%1 -Fo%2").arg(precompPch).arg(precompObj); + t << precompObj << ": " << precompH << " " << findDependencies(precompH).join(" \\\n\t\t") + << "\n\t" << "$(CXX) " + precompRule +" $(CXXFLAGS) $(INCPATH) -TP " << precompH << endl << endl; + } +} + +QString NmakeMakefileGenerator::var(const QString &value) +{ + if (usePCH) { + if ((value == "QMAKE_RUN_CXX_IMP_BATCH" + || value == "QMAKE_RUN_CXX_IMP" + || value == "QMAKE_RUN_CXX")) { + QFileInfo precompHInfo(fileInfo(precompH)); + QString precompRule = QString("-c -FI%1 -Yu%2 -Fp%3") + .arg(precompHInfo.fileName()) + .arg(precompHInfo.fileName()) + .arg(precompPch); + QString p = MakefileGenerator::var(value); + p.replace("-c", precompRule); + // Cannot use -Gm with -FI & -Yu, as this gives an + // internal compiler error, on the newer compilers + // ### work-around for a VS 2003 bug. Move to some prf file or remove completely. + p.remove("-Gm"); + return p; + } else if (value == "QMAKE_CXXFLAGS") { + // Remove internal compiler error option + // ### work-around for a VS 2003 bug. Move to some prf file or remove completely. + return MakefileGenerator::var(value).remove("-Gm"); + } + } + + // Normal val + return MakefileGenerator::var(value); +} + +void NmakeMakefileGenerator::init() +{ + if(init_flag) + return; + init_flag = true; + + /* 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"); + if(project->isEmpty("QMAKE_COPY_FILE")) + project->values("QMAKE_COPY_FILE").append("$(COPY)"); + if(project->isEmpty("QMAKE_COPY_DIR")) + project->values("QMAKE_COPY_DIR").append("xcopy /s /q /y /i"); + if(project->isEmpty("QMAKE_INSTALL_FILE")) + project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_PROGRAM")) + project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_DIR")) + project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)"); + return; + } + + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS")); + project->values("QMAKE_LIBS_PRIVATE") += escapeFilePaths(project->values("LIBS_PRIVATE")); + processVars(); + + if (!project->values("RES_FILE").isEmpty()) { + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("RES_FILE")); + } + + if (!project->values("DEF_FILE").isEmpty()) { + QString defFileName = fileFixify(project->values("DEF_FILE")).first(); + project->values("QMAKE_LFLAGS").append(QString("/DEF:") + escapeFilePath(defFileName)); + } + + if(!project->values("VERSION").isEmpty()) { + QString version = project->values("VERSION")[0]; + int firstDot = version.indexOf("."); + QString major = version.left(firstDot); + QString minor = version.right(version.length() - firstDot - 1); + minor.replace(".", ""); + project->values("QMAKE_LFLAGS").append("/VERSION:" + major + "." + minor); + } + + // Base class init! + MakefileGenerator::init(); + + // Setup PCH variables + precompH = project->first("PRECOMPILED_HEADER"); + usePCH = !precompH.isEmpty() && project->isActiveConfig("precompile_header"); + 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) + project->values("OBJECTS") += precompObj; + // Add pch file to cleanup + project->values("QMAKE_CLEAN") += precompPch; + // Return to variable pool + project->values("PRECOMPILED_OBJECT") = QStringList(precompObj); + project->values("PRECOMPILED_PCH") = QStringList(precompPch); + } + + QString version = project->first("TARGET_VERSION_EXT"); + if(project->isActiveConfig("shared")) { + project->values("QMAKE_CLEAN").append(project->first("DESTDIR") + project->first("TARGET") + version + ".exp"); + } + if(project->isActiveConfig("debug")) { + project->values("QMAKE_DISTCLEAN").append(project->first("DESTDIR") + project->first("TARGET") + version + ".pdb"); + project->values("QMAKE_CLEAN").append(project->first("DESTDIR") + project->first("TARGET") + version + ".ilk"); + project->values("QMAKE_CLEAN").append("vc*.pdb"); + project->values("QMAKE_CLEAN").append("vc*.idb"); + } +} + +void NmakeMakefileGenerator::writeLibDirPart(QTextStream &t) +{ + QStringList libDirs = project->values("QMAKE_LIBDIR"); + for (int i = 0; i < libDirs.size(); ++i) + libDirs[i].remove("\""); + t << valGlue(libDirs,"/LIBPATH:\"","\" /LIBPATH:\"","\"") << " "; +} + +void NmakeMakefileGenerator::writeImplicitRulesPart(QTextStream &t) +{ + 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 << endl << endl; + + if(!project->isActiveConfig("no_batch")) { + // 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"); + + QHash source_directories; + source_directories.insert(".", (void*)1); + QString directories[] = { QString("UI_SOURCES_DIR"), QString("UI_DIR"), QString() }; + for(int y = 0; !directories[y].isNull(); y++) { + QString dirTemp = project->first(directories[y]); + if (dirTemp.endsWith("\\")) + dirTemp.truncate(dirTemp.length()-1); + if(!dirTemp.isEmpty()) + source_directories.insert(dirTemp, (void*)1); + } + QString srcs[] = { QString("SOURCES"), QString("GENERATED_SOURCES"), QString() }; + for(int x = 0; !srcs[x].isNull(); x++) { + QStringList &l = project->values(srcs[x]); + for(QStringList::Iterator sit = l.begin(); sit != l.end(); ++sit) { + QString sep = "\\"; + if((*sit).indexOf(sep) == -1) + sep = "/"; + QString dir = (*sit).section(sep, 0, -2); + if(!dir.isEmpty() && !source_directories[dir]) + source_directories.insert(dir, (void*)1); + } + } + + for(QHash::Iterator it(source_directories.begin()); it != source_directories.end(); ++it) { + if(it.key().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 << "{" << it.key() << "}" << (*cppit) << "{" << objDir << "}" << Option::obj_ext << "::\n\t" + << var("QMAKE_RUN_CXX_IMP_BATCH").replace(QRegExp("\\$@"), var("OBJECTS_DIR")) << endl << "\t$<" << endl << "<<" << endl << endl; + for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit) + t << "{" << it.key() << "}" << (*cit) << "{" << objDir << "}" << Option::obj_ext << "::\n\t" + << var("QMAKE_RUN_CC_IMP_BATCH").replace(QRegExp("\\$@"), var("OBJECTS_DIR")) << endl << "\t$<" << endl << "<<" << endl << endl; + } + } 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") << endl << 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") << endl << endl; + } + +} + +void NmakeMakefileGenerator::writeBuildRulesPart(QTextStream &t) +{ + t << "first: all" << endl; + t << "all: " << fileFixify(Option::output.fileName()) << " " << varGlue("ALL_DEPS"," "," "," ") << "$(DESTDIR_TARGET)" << endl << endl; + t << "$(DESTDIR_TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) " << var("POST_TARGETDEPS"); + + if(!project->isEmpty("QMAKE_PRE_LINK")) + t << "\n\t" <isActiveConfig("staticlib")) { + t << "\n\t" << "$(LIBAPP) $(LIBFLAGS) /OUT:$(DESTDIR_TARGET) @<<" << "\n\t " + << "$(OBJECTS)"; + } else { + t << "\n\t" << "$(LINK) $(LFLAGS) /OUT:$(DESTDIR_TARGET) @<< " << "\n\t " + << "$(OBJECTS) $(LIBS)"; + } + t << endl << "<<"; + QString signature = !project->isEmpty("SIGNATURE_FILE") ? var("SIGNATURE_FILE") : var("DEFAULT_SIGNATURE"); + bool useSignature = !signature.isEmpty() && !project->isActiveConfig("staticlib") && + !project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH"); + if(useSignature) { + t << "\n\tsigntool sign /F " << signature << " $(DESTDIR_TARGET)"; + } + if(!project->isEmpty("QMAKE_POST_LINK")) { + t << "\n\t" << var("QMAKE_POST_LINK"); + } + t << endl; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/msvc_nmake.h b/qmake/generators/win32/msvc_nmake.h new file mode 100644 index 0000000000..8954655672 --- /dev/null +++ b/qmake/generators/win32/msvc_nmake.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MSVC_NMAKE_H +#define MSVC_NMAKE_H + +#include "winmakefile.h" + +QT_BEGIN_NAMESPACE + +class NmakeMakefileGenerator : public Win32MakefileGenerator +{ + bool init_flag; + void writeNmakeParts(QTextStream &); + void writeLibDirPart(QTextStream &t); + bool writeMakefile(QTextStream &); + void writeImplicitRulesPart(QTextStream &t); + void writeBuildRulesPart(QTextStream &t); + void init(); + +protected: + virtual QString getPdbTarget(); + virtual QString defaultInstall(const QString &t); + virtual QStringList &findDependencies(const QString &file); + QString var(const QString &value); + QString precompH, precompObj, precompPch; + bool usePCH; + +public: + NmakeMakefileGenerator(); + ~NmakeMakefileGenerator(); + +}; + +inline NmakeMakefileGenerator::~NmakeMakefileGenerator() +{ } + +QT_END_NAMESPACE + +#endif // MSVC_NMAKE_H diff --git a/qmake/generators/win32/msvc_objectmodel.cpp b/qmake/generators/win32/msvc_objectmodel.cpp new file mode 100644 index 0000000000..88a5043519 --- /dev/null +++ b/qmake/generators/win32/msvc_objectmodel.cpp @@ -0,0 +1,2909 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "msvc_objectmodel.h" +#include "msvc_vcproj.h" +#include "msvc_vcxproj.h" +#include +#include + +QT_BEGIN_NAMESPACE + +// XML Tags --------------------------------------------------------- +const char _Configuration[] = "Configuration"; +const char _Configurations[] = "Configurations"; +const char q_File[] = "File"; +const char _FileConfiguration[] = "FileConfiguration"; +const char q_Files[] = "Files"; +const char _Filter[] = "Filter"; +const char _Globals[] = "Globals"; +const char _Platform[] = "Platform"; +const char _Platforms[] = "Platforms"; +const char _Tool[] = "Tool"; +const char _VisualStudioProject[] = "VisualStudioProject"; + +// XML Properties --------------------------------------------------- +const char _AddModuleNamesToAssembly[] = "AddModuleNamesToAssembly"; +const char _AdditionalDependencies[] = "AdditionalDependencies"; +const char _AdditionalFiles[] = "AdditionalFiles"; +const char _AdditionalIncludeDirectories[] = "AdditionalIncludeDirectories"; +const char _AdditionalLibraryDirectories[] = "AdditionalLibraryDirectories"; +const char _AdditionalOptions[] = "AdditionalOptions"; +const char _AdditionalUsingDirectories[] = "AdditionalUsingDirectories"; +const char _AssemblerListingLocation[] = "AssemblerListingLocation"; +const char _AssemblerOutput[] = "AssemblerOutput"; +const char _ATLMinimizesCRunTimeLibraryUsage[] = "ATLMinimizesCRunTimeLibraryUsage"; +const char _BaseAddress[] = "BaseAddress"; +const char _BasicRuntimeChecks[] = "BasicRuntimeChecks"; +const char _BrowseInformation[] = "BrowseInformation"; +const char _BrowseInformationFile[] = "BrowseInformationFile"; +const char _BufferSecurityCheck[] = "BufferSecurityCheck"; +const char _BuildBrowserInformation[] = "BuildBrowserInformation"; +const char _CPreprocessOptions[] = "CPreprocessOptions"; +const char _CallingConvention[] = "CallingConvention"; +const char _CharacterSet[] = "CharacterSet"; +const char _CommandLine[] = "CommandLine"; +const char _CompileAs[] = "CompileAs"; +const char _CompileAsManaged[] = "CompileAsManaged"; +const char _CompileOnly[] = "CompileOnly"; +const char _ConfigurationType[] = "ConfigurationType"; +const char _Culture[] = "Culture"; +const char _DLLDataFileName[] = "DLLDataFileName"; +const char _DebugInformationFormat[] = "DebugInformationFormat"; +const char _DefaultCharIsUnsigned[] = "DefaultCharIsUnsigned"; +const char _DefaultCharType[] = "DefaultCharType"; +const char _DelayLoadDLLs[] = "DelayLoadDLLs"; +const char _DeleteExtensionsOnClean[] = "DeleteExtensionsOnClean"; +const char _Description[] = "Description"; +const char _Detect64BitPortabilityProblems[] = "Detect64BitPortabilityProblems"; +const char _DisableLanguageExtensions[] = "DisableLanguageExtensions"; +const char _DisableSpecificWarnings[] = "DisableSpecificWarnings"; +const char _EnableCOMDATFolding[] = "EnableCOMDATFolding"; +const char _EnableErrorChecks[] = "EnableErrorChecks"; +const char _EnableEnhancedInstructionSet[] = "EnableEnhancedInstructionSet"; +const char _EnableFiberSafeOptimizations[] = "EnableFiberSafeOptimizations"; +const char _EnableFunctionLevelLinking[] = "EnableFunctionLevelLinking"; +const char _EnableIntrinsicFunctions[] = "EnableIntrinsicFunctions"; +const char _EntryPointSymbol[] = "EntryPointSymbol"; +const char _ErrorCheckAllocations[] = "ErrorCheckAllocations"; +const char _ErrorCheckBounds[] = "ErrorCheckBounds"; +const char _ErrorCheckEnumRange[] = "ErrorCheckEnumRange"; +const char _ErrorCheckRefPointers[] = "ErrorCheckRefPointers"; +const char _ErrorCheckStubData[] = "ErrorCheckStubData"; +const char _ExceptionHandling[] = "ExceptionHandling"; +const char _ExcludedFromBuild[] = "ExcludedFromBuild"; +const char _ExpandAttributedSource[] = "ExpandAttributedSource"; +const char _ExportNamedFunctions[] = "ExportNamedFunctions"; +const char _FavorSizeOrSpeed[] = "FavorSizeOrSpeed"; +const char _FloatingPointModel[] = "FloatingPointModel"; +const char _FloatingPointExceptions[] = "FloatingPointExceptions"; +const char _ForceConformanceInForLoopScope[] = "ForceConformanceInForLoopScope"; +const char _ForceSymbolReferences[] = "ForceSymbolReferences"; +const char _ForcedIncludeFiles[] = "ForcedIncludeFiles"; +const char _ForcedUsingFiles[] = "ForcedUsingFiles"; +const char _FullIncludePath[] = "FullIncludePath"; +const char _FunctionOrder[] = "FunctionOrder"; +const char _GenerateDebugInformation[] = "GenerateDebugInformation"; +const char _GenerateMapFile[] = "GenerateMapFile"; +const char _GeneratePreprocessedFile[] = "GeneratePreprocessedFile"; +const char _GenerateStublessProxies[] = "GenerateStublessProxies"; +const char _GenerateTypeLibrary[] = "GenerateTypeLibrary"; +const char _GlobalOptimizations[] = "GlobalOptimizations"; +const char _HeaderFileName[] = "HeaderFileName"; +const char _HeapCommitSize[] = "HeapCommitSize"; +const char _HeapReserveSize[] = "HeapReserveSize"; +const char _IgnoreAllDefaultLibraries[] = "IgnoreAllDefaultLibraries"; +const char _IgnoreDefaultLibraryNames[] = "IgnoreDefaultLibraryNames"; +const char _IgnoreEmbeddedIDL[] = "IgnoreEmbeddedIDL"; +const char _IgnoreImportLibrary[] = "IgnoreImportLibrary"; +const char _IgnoreStandardIncludePath[] = "IgnoreStandardIncludePath"; +const char _ImportLibrary[] = "ImportLibrary"; +const char _ImproveFloatingPointConsistency[] = "ImproveFloatingPointConsistency"; +const char _InlineFunctionExpansion[] = "InlineFunctionExpansion"; +const char _InterfaceIdentifierFileName[] = "InterfaceIdentifierFileName"; +const char _IntermediateDirectory[] = "IntermediateDirectory"; +const char _KeepComments[] = "KeepComments"; +const char _LargeAddressAware[] = "LargeAddressAware"; +const char _LinkDLL[] = "LinkDLL"; +const char _LinkIncremental[] = "LinkIncremental"; +const char _LinkTimeCodeGeneration[] = "LinkTimeCodeGeneration"; +const char _LinkToManagedResourceFile[] = "LinkToManagedResourceFile"; +const char _MapExports[] = "MapExports"; +const char _MapFileName[] = "MapFileName"; +const char _MapLines[] = "MapLines "; +const char _MergeSections[] = "MergeSections"; +const char _MergedIDLBaseFileName[] = "MergedIDLBaseFileName"; +const char _MidlCommandFile[] = "MidlCommandFile"; +const char _MinimalRebuild[] = "MinimalRebuild"; +const char _MkTypLibCompatible[] = "MkTypLibCompatible"; +const char _ModuleDefinitionFile[] = "ModuleDefinitionFile"; +const char _Name[] = "Name"; +const char _ObjectFile[] = "ObjectFile"; +const char _OmitFramePointers[] = "OmitFramePointers"; +const char _OpenMP[] = "OpenMP"; +const char _Optimization[] = "Optimization "; +const char _OptimizeForProcessor[] = "OptimizeForProcessor"; +const char _OptimizeForWindows98[] = "OptimizeForWindows98"; +const char _OptimizeForWindowsApplication[] = "OptimizeForWindowsApplication"; +const char _OptimizeReferences[] = "OptimizeReferences"; +const char _OutputDirectory[] = "OutputDirectory"; +const char _OutputFile[] = "OutputFile"; +const char _Outputs[] = "Outputs"; +const char _ParseFiles[] = "ParseFiles"; +const char _PrecompiledHeaderFile[] = "PrecompiledHeaderFile"; +const char _PrecompiledHeaderThrough[] = "PrecompiledHeaderThrough"; +const char _PreprocessorDefinitions[] = "PreprocessorDefinitions"; +const char _PrimaryOutput[] = "PrimaryOutput"; +const char _ProjectGUID[] = "ProjectGUID"; +const char _Keyword[] = "Keyword"; +const char _ProjectType[] = "ProjectType"; +const char _ProgramDatabase[] = "ProgramDatabase"; +const char _ProgramDataBaseFileName[] = "ProgramDataBaseFileName"; +const char _ProgramDatabaseFile[] = "ProgramDatabaseFile"; +const char _ProxyFileName[] = "ProxyFileName"; +const char _RedirectOutputAndErrors[] = "RedirectOutputAndErrors"; +const char _RegisterOutput[] = "RegisterOutput"; +const char _RelativePath[] = "RelativePath"; +const char _RemoteDirectory[] = "RemoteDirectory"; +const char _ResourceOnlyDLL[] = "ResourceOnlyDLL"; +const char _ResourceOutputFileName[] = "ResourceOutputFileName"; +const char _RuntimeLibrary[] = "RuntimeLibrary"; +const char _RuntimeTypeInfo[] = "RuntimeTypeInfo"; +const char _SccProjectName[] = "SccProjectName"; +const char _SccLocalPath[] = "SccLocalPath"; +const char _SetChecksum[] = "SetChecksum"; +const char _ShowIncludes[] = "ShowIncludes"; +const char _ShowProgress[] = "ShowProgress"; +const char _SmallerTypeCheck[] = "SmallerTypeCheck"; +const char _StackCommitSize[] = "StackCommitSize"; +const char _StackReserveSize[] = "StackReserveSize"; +const char _StringPooling[] = "StringPooling"; +const char _StripPrivateSymbols[] = "StripPrivateSymbols"; +const char _StructMemberAlignment[] = "StructMemberAlignment"; +const char _SubSystem[] = "SubSystem"; +const char _SupportUnloadOfDelayLoadedDLL[] = "SupportUnloadOfDelayLoadedDLL"; +const char _SuppressStartupBanner[] = "SuppressStartupBanner"; +const char _SwapRunFromCD[] = "SwapRunFromCD"; +const char _SwapRunFromNet[] = "SwapRunFromNet"; +const char _TargetEnvironment[] = "TargetEnvironment"; +const char _TargetMachine[] = "TargetMachine"; +const char _TerminalServerAware[] = "TerminalServerAware"; +const char _Path[] = "Path"; +const char _TreatWChar_tAsBuiltInType[] = "TreatWChar_tAsBuiltInType"; +const char _TurnOffAssemblyGeneration[] = "TurnOffAssemblyGeneration"; +const char _TypeLibraryFile[] = "TypeLibraryFile"; +const char _TypeLibraryName[] = "TypeLibraryName"; +const char _TypeLibraryResourceID[] = "TypeLibraryResourceID"; +const char _UndefineAllPreprocessorDefinitions[]= "UndefineAllPreprocessorDefinitions"; +const char _UndefinePreprocessorDefinitions[] = "UndefinePreprocessorDefinitions"; +const char _UniqueIdentifier[] = "UniqueIdentifier"; +const char _UseOfATL[] = "UseOfATL"; +const char _UseOfMfc[] = "UseOfMfc"; +const char _UsePrecompiledHeader[] = "UsePrecompiledHeader"; +const char _ValidateParameters[] = "ValidateParameters"; +const char _VCCLCompilerTool[] = "VCCLCompilerTool"; +const char _VCLibrarianTool[] = "VCLibrarianTool"; +const char _VCLinkerTool[] = "VCLinkerTool"; +const char _VCCustomBuildTool[] = "VCCustomBuildTool"; +const char _VCResourceCompilerTool[] = "VCResourceCompilerTool"; +const char _VCMIDLTool[] = "VCMIDLTool"; +const char _Version[] = "Version"; +const char _WarnAsError[] = "WarnAsError"; +const char _WarningLevel[] = "WarningLevel"; +const char _WholeProgramOptimization[] = "WholeProgramOptimization"; +const char _CompileForArchitecture[] = "CompileForArchitecture"; +const char _InterworkCalls[] = "InterworkCalls"; + +// XmlOutput stream functions ------------------------------ +inline XmlOutput::xml_output attrT(const char *name, const triState v) +{ + if(v == unset) + return noxml(); + return attr(name, (v == _True ? "true" : "false")); +} + +inline XmlOutput::xml_output attrE(const char *name, int v) +{ + return attr(name, QString::number(v)); +} + +/*ifNot version*/ +inline XmlOutput::xml_output attrE(const char *name, int v, int ifn) +{ + if (v == ifn) + return noxml(); + return attr(name, QString::number(v)); +} + +inline XmlOutput::xml_output attrL(const char *name, qint64 v) +{ + return attr(name, QString::number(v)); +} + +/*ifNot version*/ +inline XmlOutput::xml_output attrL(const char *name, qint64 v, qint64 ifn) +{ + if (v == ifn) + return noxml(); + return attr(name, QString::number(v)); +} + +inline XmlOutput::xml_output attrS(const char *name, const QString &v) +{ + if(v.isEmpty()) + return noxml(); + return attr(name, v); +} + +inline XmlOutput::xml_output attrX(const char *name, const QStringList &v, const char *s = ",") +{ + if(v.isEmpty()) + return noxml(); + return attr(name, v.join(s)); +} + +triState operator!(const triState &rhs) +{ + if (rhs == unset) + return rhs; + triState lhs = (rhs == _True ? _False : _True); + return lhs; +} + +// VCToolBase ------------------------------------------------- +QStringList VCToolBase::fixCommandLine(const QString &input) +{ + // The splitting regexp is a bit bizarre for backwards compat reasons (why else ...). + return input.split(QRegExp(QLatin1String("\n\t|\r\\\\h|\r\n"))); +} + +static QString vcCommandSeparator() +{ + // MSVC transforms the build tree into a single batch file, simply pasting the contents + // of the custom commands into it, and putting an "if errorlevel goto" statement behind it. + // As we want every sub-command to be error-checked (as is done by makefile-based + // backends), we insert the checks ourselves, using the undocumented jump target. + static QString cmdSep = + QLatin1String(" if errorlevel 1 goto VCReportError "); + return cmdSep; +} + +// VCCLCompilerTool ------------------------------------------------- +VCCLCompilerTool::VCCLCompilerTool() + : AssemblerOutput(asmListingNone), + BasicRuntimeChecks(runtimeBasicCheckNone), + BrowseInformation(brInfoNone), + BufferSecurityCheck(_False), + CallingConvention(callConventionDefault), + CompileAs(compileAsDefault), + CompileAsManaged(managedDefault), + CompileOnly(unset), + DebugInformationFormat(debugDisabled), + DefaultCharIsUnsigned(unset), + Detect64BitPortabilityProblems(unset), + DisableLanguageExtensions(unset), + EnableEnhancedInstructionSet(archNotSet), + EnableFiberSafeOptimizations(unset), + EnableFunctionLevelLinking(unset), + EnableIntrinsicFunctions(unset), + ExceptionHandling(ehDefault), + ExpandAttributedSource(unset), + FavorSizeOrSpeed(favorNone), + FloatingPointModel(floatingPointNotSet), + FloatingPointExceptions(unset), + ForceConformanceInForLoopScope(unset), + GeneratePreprocessedFile(preprocessNo), + PreprocessSuppressLineNumbers(unset), + GlobalOptimizations(unset), + IgnoreStandardIncludePath(unset), + ImproveFloatingPointConsistency(unset), + InlineFunctionExpansion(expandDefault), + KeepComments(unset), + MinimalRebuild(unset), + OmitDefaultLibName(unset), + OmitFramePointers(unset), + OpenMP(unset), + Optimization(optimizeCustom), + OptimizeForProcessor(procOptimizeBlended), + OptimizeForWindowsApplication(unset), + ProgramDataBaseFileName(""), + RuntimeLibrary(rtMultiThreaded), + RuntimeTypeInfo(unset), + ShowIncludes(unset), + SmallerTypeCheck(unset), + StringPooling(unset), + StructMemberAlignment(alignNotSet), + SuppressStartupBanner(unset), + TreatWChar_tAsBuiltInType(unset), + TurnOffAssemblyGeneration(unset), + UndefineAllPreprocessorDefinitions(unset), + UsePrecompiledHeader(pchUnset), + UseUnicodeForAssemblerListing(unset), + WarnAsError(unset), + WarningLevel(warningLevel_0), + WholeProgramOptimization(unset), + CompileForArchitecture(archUnknown), + InterworkCalls(unset), + EnablePREfast(unset), + DisplayFullPaths(unset), + MultiProcessorCompilation(unset), + GenerateXMLDocumentationFiles(unset), + CreateHotpatchableImage(unset) +{ +} + +/* + * Some values for the attribute UsePrecompiledHeader have changed from VS 2003 to VS 2005, + * see the following chart, so we need a function that transforms those values if we are + * using NET2005: + * + * Meaning 2003 2005 + * ----------------------------------------- + * Don't use PCH 0 0 + * Create PCH (/Yc) 1 1 + * Automatically generate (/YX) 2 (seems that it was removed) + * Use specific PCH (/Yu) 3 2 + * + */ +inline XmlOutput::xml_output xformUsePrecompiledHeaderForNET2005(pchOption whatPch, DotNET compilerVersion) +{ + if (compilerVersion >= NET2005) { + if (whatPch == pchGenerateAuto) whatPch = (pchOption)0; + if (whatPch == pchUseUsingSpecific) whatPch = (pchOption)2; + } + return attrE(_UsePrecompiledHeader, whatPch, /*ifNot*/ pchUnset); +} + +inline XmlOutput::xml_output xformExceptionHandlingNET2005(exceptionHandling eh, DotNET compilerVersion) +{ + if (eh == ehDefault) + return noxml(); + + if (compilerVersion >= NET2005) + return attrE(_ExceptionHandling, eh); + + return attrS(_ExceptionHandling, (eh == ehNoSEH ? "true" : "false")); +} + +bool VCCLCompilerTool::parseOption(const char* option) +{ + // skip index 0 ('/' or '-') + char first = option[1]; + char second = option[2]; + char third = option[3]; + char fourth = option[4]; + bool found = true; + + switch (first) { + case '?': + case 'h': + if(second == 'o' && third == 't' && fourth == 'p') { + CreateHotpatchableImage = _True; + break; + } + qWarning("Generator: Option '/?', '/help': MSVC.NET projects do not support outputting help info"); + found = false; + break; + case '@': + qWarning("Generator: Option '/@': MSVC.NET projects do not support the use of a response file"); + found = false; + break; + case 'l': + qWarning("Generator: Option '/link': qmake generator does not support passing link options through the compiler tool"); + found = false; + break; + case 'A': + if(second != 'I') { + found = false; break; + } + AdditionalUsingDirectories += option+3; + break; + case 'C': + KeepComments = _True; + break; + case 'D': + PreprocessorDefinitions += option+2; + break; + case 'E': + if(second == 'H') { + QByteArray opt(option + 2); + if (opt.contains('a') && !opt.contains('s') && !opt.contains('c')) + ExceptionHandling = ehSEH; + else if (!opt.contains('a') && opt.contains("s-") && opt.contains("c-")) + ExceptionHandling = ehNone; + else if (!opt.contains('a') && opt.contains('s') && opt.contains('c')) + ExceptionHandling = ehNoSEH; + else { + // ExceptionHandling must be false, or it will override + // with an /EHsc option + ExceptionHandling = ehNone; + AdditionalOptions += option; + } + if (config->CompilerVersion < NET2005 + && ExceptionHandling == ehSEH) { + ExceptionHandling = ehNone; + AdditionalOptions += option; + } + break; + } else if (second == 'P') { + PreprocessSuppressLineNumbers = _True; + } + GeneratePreprocessedFile = preprocessYes; + break; + case 'F': + if(second <= '9' && second >= '0') { + AdditionalOptions += option; + break; + } else { + switch (second) { + case 'A': + if(third == 'c') { + AssemblerOutput = asmListingAsmMachine; + if(fourth == 's') + AssemblerOutput = asmListingAsmMachineSrc; + } else if(third == 's') { + AssemblerOutput = asmListingAsmSrc; + } else if (third == 'u') { + UseUnicodeForAssemblerListing = _True; + } else { + AssemblerOutput = asmListingAssemblyOnly; + } + break; + case 'C': + DisplayFullPaths = _True; + break; + case 'a': + AssemblerListingLocation = option+3; + break; + case 'I': + ForcedIncludeFiles += option+3; + break; + case 'i': + PreprocessOutputPath += option+3; + break; + case 'R': + BrowseInformation = brAllInfo; + BrowseInformationFile = option+3; + break; + case 'r': + BrowseInformation = brNoLocalSymbols; + BrowseInformationFile = option+3; + break; + case 'U': + ForcedUsingFiles += option+3; + break; + case 'd': + ProgramDataBaseFileName = option+3; + break; + case 'e': + OutputFile = option+3; + break; + case 'm': + AdditionalOptions += option; + break; + case 'o': + ObjectFile = option+3; + break; + case 'p': + PrecompiledHeaderFile = option+3; + break; + case 'x': + ExpandAttributedSource = _True; + break; + default: + found = false; break; + } + } + break; + case 'G': + switch (second) { + case '3': + case '4': + qWarning("Option '/G3' and '/G4' were phased out in Visual C++ 5.0"); + found = false; break; + case '5': + OptimizeForProcessor = procOptimizePentium; + break; + case '6': + case 'B': + OptimizeForProcessor = procOptimizePentiumProAndAbove; + break; + case '7': + OptimizeForProcessor = procOptimizePentium4AndAbove; + break; + case 'A': + OptimizeForWindowsApplication = _True; + break; + case 'F': + StringPooling = _True; + break; + case 'H': + AdditionalOptions += option; + break; + case 'L': + WholeProgramOptimization = _True; + if(third == '-') + WholeProgramOptimization = _False; + break; + case 'R': + RuntimeTypeInfo = _True; + if(third == '-') + RuntimeTypeInfo = _False; + break; + case 'S': + BufferSecurityCheck = _True; + if(third == '-') + BufferSecurityCheck = _False; + break; + case 'T': + EnableFiberSafeOptimizations = _True; + break; + case 'X': + // Same as the /EHsc option, which is Exception Handling without SEH + ExceptionHandling = ehNoSEH; + if (third == '-') + ExceptionHandling = ehNone; + break; + case 'Z': + case 'e': + case 'h': + AdditionalOptions += option; + break; + case 'd': + CallingConvention = callConventionCDecl; + break; + case 'f': + StringPooling = _True; + AdditionalOptions += option; + break; + case 'm': + MinimalRebuild = _True; + if(third == '-') + MinimalRebuild = _False; + break; + case 'r': + CallingConvention = callConventionFastCall; + break; + case 's': + // Warning: following [num] is not used, + // were should we put it? + BufferSecurityCheck = _True; + break; + case 'y': + EnableFunctionLevelLinking = _True; + break; + case 'z': + CallingConvention = callConventionStdCall; + break; + default: + found = false; break; + } + break; + case 'H': + AdditionalOptions += option; + break; + case 'I': + AdditionalIncludeDirectories += option+2; + break; + case 'J': + DefaultCharIsUnsigned = _True; + break; + case 'L': + if(second == 'D') { + AdditionalOptions += option; + break; + } + found = false; break; + case 'M': + if(second == 'D') { + RuntimeLibrary = rtMultiThreadedDLL; + if(third == 'd') + RuntimeLibrary = rtMultiThreadedDebugDLL; + break; + } else if(second == 'L') { + RuntimeLibrary = rtSingleThreaded; + if(third == 'd') + RuntimeLibrary = rtSingleThreadedDebug; + break; + } else if(second == 'T') { + RuntimeLibrary = rtMultiThreaded; + if(third == 'd') + RuntimeLibrary = rtMultiThreadedDebug; + break; + } else if (second == 'P') { + if (config->CompilerVersion >= NET2005) { + AdditionalOptions += option; + } else if (config->CompilerVersion >= NET2010) { + MultiProcessorCompilation = _True; + MultiProcessorCompilationProcessorCount = option+3; + } else { + warn_msg(WarnLogic, "/MP option is not supported in Visual C++ < 2005, ignoring."); + } + break; + } + found = false; break; + case 'O': + switch (second) { + case '1': + Optimization = optimizeMinSpace; + break; + case '2': + Optimization = optimizeMaxSpeed; + break; + case 'a': + AdditionalOptions += option; + break; + case 'b': + if(third == '0') + InlineFunctionExpansion = expandDisable; + else if(third == '1') + InlineFunctionExpansion = expandOnlyInline; + else if(third == '2') + InlineFunctionExpansion = expandAnySuitable; + else + found = false; + break; + case 'd': + Optimization = optimizeDisabled; + break; + case 'g': + GlobalOptimizations = _True; + break; + case 'i': + EnableIntrinsicFunctions = _True; + break; + case 'p': + ImproveFloatingPointConsistency = _True; + if(third == '-') + ImproveFloatingPointConsistency = _False; + break; + case 's': + FavorSizeOrSpeed = favorSize; + break; + case 't': + FavorSizeOrSpeed = favorSpeed; + break; + case 'w': + AdditionalOptions += option; + break; + case 'x': + Optimization = optimizeFull; + break; + case 'y': + OmitFramePointers = _True; + if(third == '-') + OmitFramePointers = _False; + break; + default: + found = false; break; + } + break; + case 'P': + GeneratePreprocessedFile = preprocessYes; + break; + case 'Q': + if(second == 'I') { + AdditionalOptions += option; + break; + } else if (second == 'R') { + QString opt = option + 3; + if (opt == "interwork-return") { + InterworkCalls = _True; + break; + } else if (opt == "arch4") { + CompileForArchitecture = archArmv4; + break; + } else if (opt == "arch5") { + CompileForArchitecture = archArmv5; + break; + } else if (opt == "arch4T") { + CompileForArchitecture = archArmv4T; + break; + } else if (opt == "arch5T") { + CompileForArchitecture = archArmv5T; + break; + } + } else if (second == 'M') { + QString opt = option + 3; + if (opt == "mips1") { + CompileForArchitecture = archMips1; + break; + } + else if (opt == "mips2") { + CompileForArchitecture = archMips2; + break; + } + else if (opt == "mips3") { + CompileForArchitecture = archMips3; + break; + } + else if (opt == "mips4") { + CompileForArchitecture = archMips4; + break; + } + else if (opt == "mips5") { + CompileForArchitecture = archMips5; + break; + } + else if (opt == "mips16") { + CompileForArchitecture = archMips16; + break; + } + else if (opt == "mips32") { + CompileForArchitecture = archMips32; + break; + } + else if (opt == "mips64") { + CompileForArchitecture = archMips64; + break; + } + } + found = false; break; + case 'R': + if(second == 'T' && third == 'C') { + if(fourth == '1') + BasicRuntimeChecks = runtimeBasicCheckAll; + else if(fourth == 'c') + SmallerTypeCheck = _True; + else if(fourth == 's') + BasicRuntimeChecks = runtimeCheckStackFrame; + else if(fourth == 'u') + BasicRuntimeChecks = runtimeCheckUninitVariables; + else + found = false; break; + } + break; + case 'T': + if(second == 'C') { + CompileAs = compileAsC; + } else if(second == 'P') { + CompileAs = compileAsCPlusPlus; + } else { + qWarning("Generator: Options '/Tp' and '/Tc' are not supported by qmake"); + found = false; break; + } + break; + case 'U': + UndefinePreprocessorDefinitions += option+2; + break; + case 'V': + AdditionalOptions += option; + break; + case 'W': + switch (second) { + case 'a': + case '4': + WarningLevel = warningLevel_4; + break; + case '3': + WarningLevel = warningLevel_3; + break; + case '2': + WarningLevel = warningLevel_2; + break; + case '1': + WarningLevel = warningLevel_1; + break; + case '0': + WarningLevel = warningLevel_0; + break; + case 'L': + AdditionalOptions += option; + break; + case 'X': + WarnAsError = _True; + break; + case 'p': + if(third == '6' && fourth == '4') { + if (config->CompilerVersion >= NET2010) { + // Deprecated for VS2010 but can be used under Additional Options. + AdditionalOptions += option; + } else { + Detect64BitPortabilityProblems = _True; + } + break; + } + // Fallthrough + default: + found = false; break; + } + break; + case 'X': + IgnoreStandardIncludePath = _True; + break; + case 'Y': + switch (second) { + case '\0': + case '-': + AdditionalOptions += option; + break; + case 'X': + UsePrecompiledHeader = pchGenerateAuto; + PrecompiledHeaderThrough = option+3; + break; + case 'c': + UsePrecompiledHeader = pchCreateUsingSpecific; + PrecompiledHeaderThrough = option+3; + break; + case 'd': + case 'l': + AdditionalOptions += option; + break; + case 'u': + UsePrecompiledHeader = pchUseUsingSpecific; + PrecompiledHeaderThrough = option+3; + break; + default: + found = false; break; + } + break; + case 'Z': + switch (second) { + case '7': + DebugInformationFormat = debugOldStyleInfo; + break; + case 'I': + DebugInformationFormat = debugEditAndContinue; + break; + case 'd': + DebugInformationFormat = debugLineInfoOnly; + break; + case 'i': + DebugInformationFormat = debugEnabled; + break; + case 'l': + OmitDefaultLibName = _True; + break; + case 'a': + DisableLanguageExtensions = _True; + break; + case 'e': + DisableLanguageExtensions = _False; + break; + case 'c': + if(third == ':') { + const char *c = option + 4; + // Go to the end of the option + while ( *c != '\0' && *c != ' ' && *c != '-') + ++c; + if(fourth == 'f') + ForceConformanceInForLoopScope = ((*c) == '-' ? _False : _True); + else if(fourth == 'w') + TreatWChar_tAsBuiltInType = ((*c) == '-' ? _False : _True); + else + found = false; + } else { + found = false; break; + } + break; + case 'g': + case 'm': + case 's': + AdditionalOptions += option; + break; + case 'p': + switch (third) + { + case '\0': + case '1': + StructMemberAlignment = alignSingleByte; + if(fourth == '6') + StructMemberAlignment = alignSixteenBytes; + break; + case '2': + StructMemberAlignment = alignTwoBytes; + break; + case '4': + StructMemberAlignment = alignFourBytes; + break; + case '8': + StructMemberAlignment = alignEightBytes; + break; + default: + found = false; break; + } + break; + default: + found = false; break; + } + break; + case 'a': + if (second == 'r' && third == 'c' && fourth == 'h') { + if (option[5] == ':') { + const char *o = option; + if (o[6] == 'S' && o[7] == 'S' && o[8] == 'E') { + EnableEnhancedInstructionSet = o[9] == '2' ? archSSE2 : archSSE; + break; + } + } + } else if (second == 'n' && third == 'a' && fourth == 'l') { + EnablePREfast = _True; + break; + } + found = false; + break; + case 'b': // see http://msdn2.microsoft.com/en-us/library/ms173499.aspx + if (second == 'i' && third == 'g' && fourth == 'o') { + const char *o = option; + if (o[5] == 'b' && o[6] == 'j') { + AdditionalOptions += option; + break; + } + } + found = false; + break; + case 'c': + if(second == '\0') { + CompileOnly = _True; + } else if(second == 'l') { + if (config->CompilerVersion < NET2005) { + if(*(option+5) == 'n') { + CompileAsManaged = managedAssemblyPure; + TurnOffAssemblyGeneration = _True; + } else if(*(option+5) == 'p') { + CompileAsManaged = managedAssemblyPure; + warn_msg(WarnLogic, "/clr:pure option only for .NET >= 2005, using /clr"); + } else if(*(option+5) == 's') { + CompileAsManaged = managedAssemblyPure; + warn_msg(WarnLogic, "/clr:safe option only for .NET >= 2005, using /clr"); + } else if(*(option+5) == 'o') { + CompileAsManaged = managedAssemblyPure; + warn_msg(WarnLogic, "/clr:oldSyntax option only for .NET >= 2005, using /clr"); + } else if(*(option+5) == 'i') { + CompileAsManaged = managedAssemblyPure; + warn_msg(WarnLogic, "initialAppDomain enum value unknown, using /crl"); + } else { + CompileAsManaged = managedAssemblyPure; + } + } else { + if(*(option+5) == 'n') { + CompileAsManaged = managedAssembly; + TurnOffAssemblyGeneration = _True; + } else if(*(option+5) == 'p') { + CompileAsManaged = managedAssemblyPure; + } else if(*(option+5) == 's') { + CompileAsManaged = managedAssemblySafe; + } else if(*(option+5) == 'o') { + CompileAsManaged = managedAssemblyOldSyntax; + } else if(*(option+5) == 'i') { + CompileAsManaged = managedAssembly; + warn_msg(WarnLogic, "initialAppDomain enum value unknown, using /crl default"); + } else { + CompileAsManaged = managedAssembly; + } + } + } else { + found = false; break; + } + break; + case 'd': + if (second == 'r') { + CompileAsManaged = managedAssembly; + break; + } else if (second != 'o' && third == 'c') { + GenerateXMLDocumentationFiles = _True; + XMLDocumentationFileName += option+4; + break; + } + found = false; + break; + case 'e': + if (second == 'r' && third == 'r' && fourth == 'o') { + if (option[12] == ':') { + if ( option[13] == 'n') { + ErrorReporting = "None"; + } else if (option[13] == 'p') { + ErrorReporting = "Prompt"; + } else if (option[13] == 'q') { + ErrorReporting = "Queue"; + } else if (option[13] == 's') { + ErrorReporting = "Send"; + } else { + found = false; + } + break; + } + } + found = false; + break; + case 'f': + if(second == 'p' && third == ':') { + // Go to the end of the option + const char *c = option + 4; + while (*c != '\0' && *c != ' ' && *c != '-') + ++c; + switch (fourth) { + case 'e': + FloatingPointExceptions = ((*c) == '-' ? _False : _True); + break; + case 'f': + FloatingPointModel = floatingPointFast; + break; + case 'p': + FloatingPointModel = floatingPointPrecise; + break; + case 's': + FloatingPointModel = floatingPointStrict; + break; + default: + found = false; + break; + } + } + break; + case 'n': + if(second == 'o' && third == 'B' && fourth == 'o') { + AdditionalOptions += "/noBool"; + break; + } + if(second == 'o' && third == 'l' && fourth == 'o') { + SuppressStartupBanner = _True; + break; + } + found = false; break; + case 'o': + if (second == 'p' && third == 'e' && fourth == 'n') { + OpenMP = _True; + break; + } + found = false; break; + case 's': + if(second == 'h' && third == 'o' && fourth == 'w') { + ShowIncludes = _True; + break; + } + found = false; break; + case 'u': + UndefineAllPreprocessorDefinitions = _True; + break; + case 'v': + if(second == 'd' || second == 'm') { + AdditionalOptions += option; + break; + } + found = false; break; + case 'w': + switch (second) { + case '\0': + WarningLevel = warningLevel_0; + break; + case 'd': + DisableSpecificWarnings += option+3; + break; + default: + AdditionalOptions += option; + } + break; + default: + AdditionalOptions += option; + break; + } + if(!found) { + warn_msg(WarnLogic, "Could not parse Compiler option: %s, added as AdditionalOption", option); + AdditionalOptions += option; + } + return true; +} + +// VCLinkerTool ----------------------------------------------------- +VCLinkerTool::VCLinkerTool() + : DataExecutionPrevention(unset), + EnableCOMDATFolding(optFoldingDefault), + GenerateDebugInformation(unset), + GenerateMapFile(unset), + HeapCommitSize(-1), + HeapReserveSize(-1), + IgnoreAllDefaultLibraries(unset), + IgnoreEmbeddedIDL(unset), + IgnoreImportLibrary(_True), + LargeAddressAware(addrAwareDefault), + LinkDLL(unset), + LinkIncremental(linkIncrementalDefault), + LinkTimeCodeGeneration(optLTCGDefault), + MapExports(unset), + MapLines(unset), + OptimizeForWindows98(optWin98Default), + OptimizeReferences(optReferencesDefault), + RandomizedBaseAddress(unset), + RegisterOutput(unset), + ResourceOnlyDLL(unset), + SetChecksum(unset), + ShowProgress(linkProgressNotSet), + StackCommitSize(-1), + StackReserveSize(-1), + SubSystem(subSystemNotSet), + SupportUnloadOfDelayLoadedDLL(unset), + SuppressStartupBanner(unset), + SwapRunFromCD(unset), + SwapRunFromNet(unset), + TargetMachine(machineNotSet), + TerminalServerAware(termSvrAwareDefault), + TreatWarningsAsErrors(unset), + TurnOffAssemblyGeneration(unset), + TypeLibraryResourceID(0), + GenerateManifest(unset), + EnableUAC(unset), + UACUIAccess(unset), + SectionAlignment(-1), + PreventDllBinding(unset), + AllowIsolation(unset), + AssemblyDebug(unset), + CLRUnmanagedCodeCheck(unset), + DelaySign(unset) +{ +} + +// Hashing routine to do fast option lookups ---- +// Slightly rewritten to stop on ':' ',' and '\0' +// Original routine in qtranslator.cpp ---------- +static uint elfHash(const char* name) +{ + const uchar *k; + uint h = 0; + uint g; + + if(name) { + k = (const uchar *) name; + while((*k) && + (*k)!= ':' && + (*k)!=',' && + (*k)!=' ') { + h = (h << 4) + *k++; + if((g = (h & 0xf0000000)) != 0) + h ^= g >> 24; + h &= ~g; + } + } + if(!h) + h = 1; + return h; +} + +//#define USE_DISPLAY_HASH +#ifdef USE_DISPLAY_HASH +static void displayHash(const char* str) +{ + printf("case 0x%07x: // %s\n break;\n", elfHash(str), str); +} +#endif + +bool VCLinkerTool::parseOption(const char* option) +{ +#ifdef USE_DISPLAY_HASH + // Main options + displayHash("/ALIGN"); displayHash("/ALLOWBIND"); displayHash("/ASSEMBLYMODULE"); + displayHash("/ASSEMBLYRESOURCE"); displayHash("/BASE"); displayHash("/DEBUG"); + displayHash("/DEF"); displayHash("/DEFAULTLIB"); displayHash("/DELAY"); + displayHash("/DELAYLOAD"); displayHash("/DLL"); displayHash("/DRIVER"); + displayHash("/ENTRY"); displayHash("/EXETYPE"); displayHash("/EXPORT"); + displayHash("/FIXED"); displayHash("/FORCE"); displayHash("/HEAP"); + displayHash("/IDLOUT"); displayHash("/IGNORE"); displayHash("/IGNOREIDL"); displayHash("/IMPLIB"); + displayHash("/INCLUDE"); displayHash("/INCREMENTAL"); displayHash("/LARGEADDRESSAWARE"); + displayHash("/LIBPATH"); displayHash("/LTCG"); displayHash("/MACHINE"); + displayHash("/MAP"); displayHash("/MAPINFO"); displayHash("/MERGE"); + displayHash("/MIDL"); displayHash("/NOASSEMBLY"); displayHash("/NODEFAULTLIB"); + displayHash("/NOENTRY"); displayHash("/NOLOGO"); displayHash("/OPT"); + displayHash("/ORDER"); displayHash("/OUT"); displayHash("/PDB"); + displayHash("/PDBSTRIPPED"); displayHash("/RELEASE"); displayHash("/SECTION"); + displayHash("/STACK"); displayHash("/STUB"); displayHash("/SUBSYSTEM"); + displayHash("/SWAPRUN"); displayHash("/TLBID"); displayHash("/TLBOUT"); + displayHash("/TSAWARE"); displayHash("/VERBOSE"); displayHash("/VERSION"); + displayHash("/VXD"); displayHash("/WS "); displayHash("/libpath"); + +#endif +#ifdef USE_DISPLAY_HASH + // Sub options + displayHash("UNLOAD"); displayHash("NOBIND"); displayHash("no"); displayHash("NOSTATUS"); displayHash("STATUS"); + displayHash("AM33"); displayHash("ARM"); displayHash("CEE"); displayHash("EBC"); displayHash("IA64"); displayHash("X86"); displayHash("X64"); displayHash("M32R"); + displayHash("MIPS"); displayHash("MIPS16"); displayHash("MIPSFPU"); displayHash("MIPSFPU16"); displayHash("MIPSR41XX"); displayHash("PPC"); + displayHash("SH3"); displayHash("SH3DSP"); displayHash("SH4"); displayHash("SH5"); displayHash("THUMB"); displayHash("TRICORE"); displayHash("EXPORTS"); + displayHash("LINES"); displayHash("REF"); displayHash("NOREF"); displayHash("ICF"); displayHash("WIN98"); displayHash("NOWIN98"); + displayHash("CONSOLE"); displayHash("EFI_APPLICATION"); displayHash("EFI_BOOT_SERVICE_DRIVER"); displayHash("EFI_ROM"); displayHash("EFI_RUNTIME_DRIVER"); displayHash("NATIVE"); + displayHash("POSIX"); displayHash("WINDOWS"); displayHash("WINDOWSCE"); displayHash("NET"); displayHash("CD"); displayHash("NO"); +#endif + bool found = true; + const uint optionHash = elfHash(option); + if (config->CompilerVersion < NET2010) { + switch (optionHash) { + case 0x3360dbe: // /ALIGN[:number] + case 0x1485c34: // /ALLOWBIND[:NO] + case 0x33aec94: // /FIXED[:NO] + case 0x7988f7e: // /SECTION:name,[E][R][W][S][D][K][L][P][X][,ALIGN=#] + case 0x0348992: // /STUB:filename + AdditionalOptions += option; + return true; + } + } + + switch (optionHash) { + case 0x6b21972: // /DEFAULTLIB:library + case 0x396ea92: // /DRIVER[:UPONLY | :WDM] + case 0xaca9d75: // /EXETYPE[:DYNAMIC | :DEV386] + case 0x3ad5444: // /EXPORT:entryname[,@ordinal[,NONAME]][,DATA] + case 0x33b4675: // /FORCE:[MULTIPLE|UNRESOLVED] + case 0x3dc3455: // /IGNORE:number,number,number,number ### NOTE: This one is undocumented, but it is even used by Microsoft. + // In recent versions of the Microsoft linker they have disabled this undocumented feature. + case 0x0034bc4: // /VXD + AdditionalOptions += option; + break; + case 0x3360dbe: // /ALIGN[:number] + SectionAlignment = QString(option+7).toLongLong(); + break; + case 0x1485c34: // /ALLOWBIND[:NO] + if(*(option+10) == ':' && (*(option+11) == 'n' || *(option+11) == 'N')) + PreventDllBinding = _False; + else + PreventDllBinding = _True; + break; + case 0x312011e: // /ALLOWISOLATION[:NO] + if(*(option+15) == ':' && (*(option+16) == 'n' || *(option+16) == 'N')) + AllowIsolation = _False; + else + AllowIsolation = _True; + break; + case 0x679c075: // /ASSEMBLYMODULE:filename + AddModuleNamesToAssembly += option+15; + break; + case 0x75f35f7: // /ASSEMBLYDEBUG[:DISABLE] + if(*(option+14) == ':' && (*(option+15) == 'D')) + AssemblyDebug = _False; + else + AssemblyDebug = _True; + break; + case 0x43294a5: // /ASSEMBLYLINKRESOURCE:filename + AssemblyLinkResource += option+22; + break; + case 0x062d065: // /ASSEMBLYRESOURCE:filename + LinkToManagedResourceFile = option+18; + break; + case 0x0336675: // /BASE:{address | @filename,key} + // Do we need to do a manual lookup when '@filename,key'? + // Seems BaseAddress only can contain the location... + // We don't use it in Qt, so keep it simple for now + BaseAddress = option+6; + break; + case 0x63bf065: // /CLRIMAGETYPE:{IJW|PURE|SAFE} + if(*(option+14) == 'I') + CLRImageType = "ForceIJWImage"; + else if(*(option+14) == 'P') + CLRImageType = "ForcePureILImage"; + else if(*(option+14) == 'S') + CLRImageType = "ForceSafeILImage"; + break; + case 0x5f2a6a2: // /CLRSUPPORTLASTERROR{:NO | SYSTEMDLL} + if(*(option+20) == ':') { + if(*(option+21) == 'N') { + CLRSupportLastError = "Disabled"; + } else if(*(option+21) == 'S') { + CLRSupportLastError = "SystemDlls"; + } + } else { + CLRSupportLastError = "Enabled"; + } + break; + case 0xc7984f5: // /CLRTHREADATTRIBUTE:{STA|MTA|NONE} + if(*(option+20) == 'N') + CLRThreadAttribute = "DefaultThreadingAttribute"; + else if(*(option+20) == 'M') + CLRThreadAttribute = "MTAThreadingAttribute"; + else if(*(option+20) == 'S') + CLRThreadAttribute = "STAThreadingAttribute"; + break; + case 0xa8c637b: // /CLRUNMANAGEDCODECHECK[:NO] + if(*(option+23) == 'N') + CLRUnmanagedCodeCheck = _False; + else + CLRUnmanagedCodeCheck = _True; + break; + case 0x62d9e94: // /MANIFEST[:NO] + if ((*(option+9) == ':' && (*(option+10) == 'N' || *(option+10) == 'n'))) + GenerateManifest = _False; + else + GenerateManifest = _True; + break; + case 0x8b64559: // /MANIFESTDEPENDENCY:manifest_dependency + AdditionalManifestDependencies += option+20; + break; + case 0xe9e8195: // /MANIFESTFILE:filename + ManifestFile = option+14; + break; + case 0x9e9fb83: // /MANIFESTUAC http://msdn.microsoft.com/en-us/library/bb384691%28VS.100%29.aspx + if ((*(option+12) == ':' && (*(option+13) == 'N' || *(option+13) == 'n'))) + EnableUAC = _False; + else if((*(option+12) == ':' && (*(option+13) == 'l' || *(option+14) == 'e'))) { // level + if(*(option+20) == 'a') + UACExecutionLevel = "AsInvoker"; + else if(*(option+20) == 'h') + UACExecutionLevel = "HighestAvailable"; + else if(*(option+20) == 'r') + UACExecutionLevel = "RequireAdministrator"; + } else if((*(option+12) == ':' && (*(option+13) == 'u' || *(option+14) == 'i'))) { // uiAccess + if(*(option+22) == 't') + UACUIAccess = _True; + else + UACUIAccess = _False; + } else if((*(option+12) == ':' && (*(option+13) == 'f' || *(option+14) == 'r'))) { // fragment + AdditionalOptions += option; + }else + EnableUAC = _True; + break; + case 0x3389797: // /DEBUG + GenerateDebugInformation = _True; + break; + case 0x0033896: // /DEF:filename + ModuleDefinitionFile = option+5; + break; + case 0x338a069: // /DELAY:{UNLOAD | NOBIND} + // MS documentation does not specify what to do with + // this option, so we'll put it in AdditionalOptions + AdditionalOptions += option; + break; + case 0x06f4bf4: // /DELAYLOAD:dllname + DelayLoadDLLs += option+11; + break; + case 0x06d451e: // /DELAYSIGN[:NO] + if(*(option+10) == ':' && (*(option+11) == 'n' || *(option+11) == 'N')) + DelaySign = _False; + else + DelaySign = _True; + break; + case 0x003390c: // /DLL + // This option is not used for vcproj files + break; + case 0x2ee8415: // /DYNAMICBASE[:NO] + if(*(option+12) == ':' && (*(option+13) == 'n' || *(option+13) == 'N')) + RandomizedBaseAddress = _False; + else + RandomizedBaseAddress = _True; + break; + case 0x33a3979: // /ENTRY:function + EntryPointSymbol = option+7; + break; + case 0x4504334: // /ERRORREPORT:[ NONE | PROMPT | QUEUE | SEND ] + if(*(option+12) == ':' ) { + if(*(option+13) == 'N') + LinkErrorReporting = "NoErrorReport"; + else if(*(option+13) == 'P') + LinkErrorReporting = "PromptImmediately"; + else if(*(option+13) == 'Q') + LinkErrorReporting = "QueueForNextLogin"; + else if(*(option+13) == 'S') + LinkErrorReporting = "SendErrorReport"; + } + break; + case 0x033c960: // /HEAP:reserve[,commit] + { + QStringList both = QString(option+6).split(","); + HeapReserveSize = both[0].toLongLong(); + if(both.count() == 2) + HeapCommitSize = both[1].toLongLong(); + } + break; + case 0x3d91494: // /IDLOUT:[path\]filename + MergedIDLBaseFileName = option+8; + break; + case 0x345a04c: // /IGNOREIDL + IgnoreEmbeddedIDL = _True; + break; + case 0x3e250e2: // /IMPLIB:filename + ImportLibrary = option+8; + break; + case 0xe281ab5: // /INCLUDE:symbol + ForceSymbolReferences += option+9; + break; + case 0xb28103c: // /INCREMENTAL[:no] + if(*(option+12) == ':' && + (*(option+13) == 'n' || *(option+13) == 'N')) + LinkIncremental = linkIncrementalNo; + else + LinkIncremental = linkIncrementalYes; + break; + case 0x07f1ab2: // /KEYCONTAINER:name + KeyContainer = option+14; + break; + case 0xfadaf35: // /KEYFILE:filename + KeyFile = option+9; + break; + case 0x26e4675: // /LARGEADDRESSAWARE[:no] + if(*(option+18) == ':' && + *(option+19) == 'n') + LargeAddressAware = addrAwareNoLarge; + else + LargeAddressAware = addrAwareLarge; + break; + case 0x2f96bc8: // /libpath:dir + case 0x0d745c8: // /LIBPATH:dir + AdditionalLibraryDirectories += option+9; + break; + case 0x0341877: // /LTCG[:NOSTATUS|:STATUS] + config->WholeProgramOptimization = _True; + if (config->CompilerVersion >= NET2005) { + LinkTimeCodeGeneration = optLTCGEnabled; + if(*(option+5) == ':') { + const char* str = option+6; + if (*str == 'S') + ShowProgress = linkProgressAll; +#ifndef Q_OS_WIN + else if (strncasecmp(str, "pginstrument", 12)) + LinkTimeCodeGeneration = optLTCGInstrument; + else if (strncasecmp(str, "pgoptimize", 10)) + LinkTimeCodeGeneration = optLTCGOptimize; + else if (strncasecmp(str, "pgupdate", 8 )) + LinkTimeCodeGeneration = optLTCGUpdate; +#else + else if (_stricmp(str, "pginstrument")) + LinkTimeCodeGeneration = optLTCGInstrument; + else if (_stricmp(str, "pgoptimize")) + LinkTimeCodeGeneration = optLTCGOptimize; + else if (_stricmp(str, "pgupdate")) + LinkTimeCodeGeneration = optLTCGUpdate; +#endif + } + } else { + AdditionalOptions.append(option); + } + break; + case 0x379ED25: + case 0x157cf65: // /MACHINE:{AM33|ARM|CEE|IA64|X86|M32R|MIPS|MIPS16|MIPSFPU|MIPSFPU16|MIPSR41XX|PPC|SH3|SH4|SH5|THUMB|TRICORE} + switch (elfHash(option+9)) { + // Very limited documentation on all options but X86, + case 0x0005bb6: // X86 + TargetMachine = machineX86; + break; + case 0x0005b94: // X64 + TargetMachine = machineX64; + break; + // so we put the others in AdditionalOptions... + case 0x0046063: // AM33 + case 0x000466d: // ARM + case 0x0004795: // CEE + case 0x0004963: // EBC + case 0x004d494: // IA64 + case 0x0050672: // M32R + case 0x0051e53: // MIPS + case 0x51e5646: // MIPS16 + case 0x1e57b05: // MIPSFPU + case 0x57b09a6: // MIPSFPU16 + case 0x5852738: // MIPSR41XX + case 0x0005543: // PPC + case 0x00057b3: // SH3 + case 0x57b7980: // SH3DSP + case 0x00057b4: // SH4 + case 0x00057b5: // SH5 + case 0x058da12: // THUMB + case 0x96d8435: // TRICORE + default: + AdditionalOptions += option; + break; + } + break; + case 0x0034160: // /MAP[:filename] + GenerateMapFile = _True; + if (option[4] == ':') + MapFileName = option+5; + break; + case 0x164e1ef: // /MAPINFO:{EXPORTS|LINES} + if(*(option+9) == 'E') + MapExports = _True; + else if(*(option+9) == 'L') + MapLines = _True; + break; + case 0x341a6b5: // /MERGE:from=to + MergeSections = option+7; + break; + case 0x0341d8c: // /MIDL:@file + MidlCommandFile = option+7; + break; + case 0x84e2679: // /NOASSEMBLY + TurnOffAssemblyGeneration = _True; + break; + case 0x2b21942: // /NODEFAULTLIB[:library] + if(*(option+13) == '\0') + IgnoreAllDefaultLibraries = _True; + else + IgnoreDefaultLibraryNames += option+14; + break; + case 0x33a3a39: // /NOENTRY + ResourceOnlyDLL = _True; + break; + case 0x434138f: // /NOLOGO + SuppressStartupBanner = _True; + break; + case 0xc841054: // /NXCOMPAT[:NO] + if ((*(option+9) == ':' && (*(option+10) == 'N' || *(option+10) == 'n'))) + DataExecutionPrevention = _False; + else + DataExecutionPrevention = _True; + break; + case 0x0034454: // /OPT:{REF | NOREF | ICF[=iterations] | NOICF | WIN98 | NOWIN98} + { + char third = *(option+7); + switch (third) { + case 'F': // REF + if(*(option+5) == 'R') { + OptimizeReferences = optReferences; + } else { // ICF[=iterations] + EnableCOMDATFolding = optFolding; + // [=iterations] case is not documented + } + break; + case 'R': // NOREF + OptimizeReferences = optNoReferences; + break; + case 'I': // NOICF + EnableCOMDATFolding = optNoFolding; + break; + case 'N': // WIN98 + OptimizeForWindows98 = optWin98Yes; + break; + case 'W': // NOWIN98 + OptimizeForWindows98 = optWin98No; + break; + default: + found = false; + } + } + break; + case 0x34468a2: // /ORDER:@filename + FunctionOrder = option+8; + break; + case 0x00344a4: // /OUT:filename + OutputFile = option+5; + break; + case 0x0034482: // /PDB:filename + ProgramDatabaseFile = option+5; + break; + case 0xa2ad314: // /PDBSTRIPPED:pdb_file_name + StripPrivateSymbols = option+13; + break; + case 0x6a09535: // /RELEASE + SetChecksum = _True; + break; + case 0x348857b: // /STACK:reserve[,commit] + { + QStringList both = QString(option+7).split(","); + StackReserveSize = both[0].toLongLong(); + if(both.count() == 2) + StackCommitSize = both[1].toLongLong(); + } + break; + case 0x75AA4D8: // /SAFESH:{NO} + { + AdditionalOptions += option; + break; + } + case 0x9B3C00D: + case 0x78dc00d: // /SUBSYSTEM:{CONSOLE|EFI_APPLICATION|EFI_BOOT_SERVICE_DRIVER|EFI_ROM|EFI_RUNTIME_DRIVER|NATIVE|POSIX|WINDOWS|WINDOWSCE}[,major[.minor]] + { + // Split up in subsystem, and version number + QStringList both = QString(option+11).split(","); + switch (elfHash(both[0].toLatin1())) { + case 0x8438445: // CONSOLE + SubSystem = subSystemConsole; + break; + case 0xbe29493: // WINDOWS + SubSystem = subSystemWindows; + break; + // The following are undocumented, so add them to AdditionalOptions + case 0x240949e: // EFI_APPLICATION + case 0xe617652: // EFI_BOOT_SERVICE_DRIVER + case 0x9af477d: // EFI_ROM + case 0xd34df42: // EFI_RUNTIME_DRIVER + case 0x5268ea5: // NATIVE + case 0x05547e8: // POSIX + case 0x2949c95: // WINDOWSCE + case 0x4B69795: // windowsce + AdditionalOptions += option; + break; + default: + found = false; + } + } + break; + case 0x8b654de: // /SWAPRUN:{NET | CD} + if(*(option+9) == 'N') + SwapRunFromNet = _True; + else if(*(option+9) == 'C') + SwapRunFromCD = _True; + else + found = false; + break; + case 0x34906d4: // /TLBID:id + TypeLibraryResourceID = QString(option+7).toLongLong(); + break; + case 0x4907494: // /TLBOUT:[path\]filename + TypeLibraryFile = option+8; + break; + case 0x976b525: // /TSAWARE[:NO] + if(*(option+8) == ':') + TerminalServerAware = termSvrAwareNo; + else + TerminalServerAware = termSvrAwareYes; + break; + case 0xaa67735: // /VERBOSE[:lib] + if(*(option+9) == ':') { + ShowProgress = linkProgressLibs; + AdditionalOptions += option; + } else { + ShowProgress = linkProgressAll; + } + break; + case 0xaa77f7e: // /VERSION:major[.minor] + Version = option+9; + break; + case 0x0034c50: // /WS[:NO] + if (config->CompilerVersion >= NET2010) { + if(*(option+3) == ':') + TreatWarningsAsErrors = _False; + else + TreatWarningsAsErrors = _True; + } else { + AdditionalOptions += option; + } + break; + default: + AdditionalOptions += option; + break; + } + if(!found) { + warn_msg(WarnLogic, "Could not parse Linker options: %s, added as AdditionalOption", option); + AdditionalOptions += option; + } + return found; +} + +// VCMIDLTool ------------------------------------------------------- +VCMIDLTool::VCMIDLTool() + : DefaultCharType(midlCharUnsigned), + EnableErrorChecks(midlDisableAll), + ErrorCheckAllocations(unset), + ErrorCheckBounds(unset), + ErrorCheckEnumRange(unset), + ErrorCheckRefPointers(unset), + ErrorCheckStubData(unset), + GenerateStublessProxies(unset), + GenerateTypeLibrary(unset), + IgnoreStandardIncludePath(unset), + MkTypLibCompatible(unset), + StructMemberAlignment(midlAlignNotSet), + SuppressStartupBanner(unset), + TargetEnvironment(midlTargetNotSet), + ValidateParameters(unset), + WarnAsError(unset), + WarningLevel(midlWarningLevel_0), + ApplicationConfigurationMode(unset), + ValidateAllParameters(unset), + SuppressCompilerWarnings(unset), + LocaleID(-1) +{ +} + +bool VCMIDLTool::parseOption(const char* option) +{ +#ifdef USE_DISPLAY_HASH + displayHash("/D name[=def]"); displayHash("/I directory-list"); displayHash("/Oi"); + displayHash("/Oic"); displayHash("/Oicf"); displayHash("/Oif"); displayHash("/Os"); + displayHash("/U name"); displayHash("/WX"); displayHash("/W{0|1|2|3|4}"); + displayHash("/Zp {N}"); displayHash("/Zs"); displayHash("/acf filename"); + displayHash("/align {N}"); displayHash("/app_config"); displayHash("/c_ext"); + displayHash("/char ascii7"); displayHash("/char signed"); displayHash("/char unsigned"); + displayHash("/client none"); displayHash("/client stub"); displayHash("/confirm"); + displayHash("/cpp_cmd cmd_line"); displayHash("/cpp_opt options"); + displayHash("/cstub filename"); displayHash("/dlldata filename"); displayHash("/env win32"); + displayHash("/env win64"); displayHash("/error all"); displayHash("/error allocation"); + displayHash("/error bounds_check"); displayHash("/error enum"); displayHash("/error none"); + displayHash("/error ref"); displayHash("/error stub_data"); displayHash("/h filename"); + displayHash("/header filename"); displayHash("/iid filename"); displayHash("/lcid"); + displayHash("/mktyplib203"); displayHash("/ms_ext"); displayHash("/ms_union"); + displayHash("/msc_ver "); displayHash("/newtlb"); displayHash("/no_cpp"); + displayHash("/no_def_idir"); displayHash("/no_default_epv"); displayHash("/no_format_opt"); + displayHash("/no_warn"); displayHash("/nocpp"); displayHash("/nologo"); displayHash("/notlb"); + displayHash("/o filename"); displayHash("/oldnames"); displayHash("/oldtlb"); + displayHash("/osf"); displayHash("/out directory"); displayHash("/pack {N}"); + displayHash("/prefix all"); displayHash("/prefix client"); displayHash("/prefix server"); + displayHash("/prefix switch"); displayHash("/protocol all"); displayHash("/protocol dce"); + displayHash("/protocol ndr64"); displayHash("/proxy filename"); displayHash("/robust"); + displayHash("/rpcss"); displayHash("/savePP"); displayHash("/server none"); + displayHash("/server stub"); displayHash("/sstub filename"); displayHash("/syntax_check"); + displayHash("/target {system}"); displayHash("/tlb filename"); displayHash("/use_epv"); + displayHash("/win32"); displayHash("/win64"); +#endif + bool found = true; + int offset = 0; + + const uint optionHash = elfHash(option); + + if (config->CompilerVersion < NET2010) { + switch (optionHash) { + case 0x5b1cb97: // /app_config + case 0x5a2fc64: // /client {none|stub} + case 0x35aabb2: // /cstub filename + case 0x64ceb12: // /newtlb + case 0x556dbee: // /no_warn + case 0x662bb12: // /oldtlb + case 0x69c9cf2: // /server {none|stub} + case 0x36aabb2: // /sstub filename + AdditionalOptions += option; + return true; + } + } + + switch(optionHash) { + case 0x0000334: // /D name[=def] + PreprocessorDefinitions += option+3; + break; + case 0x0000339: // /I directory-list + AdditionalIncludeDirectories += option+3; + break; + case 0x0345f96: // /Oicf + case 0x00345f6: // /Oif + GenerateStublessProxies = _True; + break; + case 0x0000345: // /U name + UndefinePreprocessorDefinitions += option+3; + break; + case 0x00034c8: // /WX + WarnAsError = _True; + break; + case 0x3582fde: // /align {N} + offset = 3; // Fallthrough + case 0x0003510: // /Zp {N} + switch (*(option+offset+4)) { + case '1': + StructMemberAlignment = (*(option+offset+5) == '\0') ? midlAlignSingleByte : midlAlignSixteenBytes; + break; + case '2': + StructMemberAlignment = midlAlignTwoBytes; + break; + case '4': + StructMemberAlignment = midlAlignFourBytes; + break; + case '8': + StructMemberAlignment = midlAlignEightBytes; + break; + default: + found = false; + } + break; + case 0x5b1cb97: // /app_config + ApplicationConfigurationMode = _True; + break; + case 0x0359e82: // /char {ascii7|signed|unsigned} + switch(*(option+6)) { + case 'a': + DefaultCharType = midlCharAscii7; + break; + case 's': + DefaultCharType = midlCharSigned; + break; + case 'u': + DefaultCharType = midlCharUnsigned; + break; + default: + found = false; + } + break; + case 0x5a2fc64: // /client {none|stub} + if(*(option+8) == 's') + GenerateClientFiles = "Stub"; + else + GenerateClientFiles = "None"; + break; + case 0xa766524: // /cpp_opt options + CPreprocessOptions += option+9; + break; + case 0x35aabb2: // /cstub filename + ClientStubFile = option+7; + break; + case 0xb32abf1: // /dlldata filename + DLLDataFileName = option + 9; + break; + case 0x0035c56: // /env {win32|win64} + TargetEnvironment = (*(option+8) == '6') ? midlTargetWin64 : midlTargetWin32; + break; + case 0x35c9962: // /error {all|allocation|bounds_check|enum|none|ref|stub_data} + EnableErrorChecks = midlEnableCustom; + switch (*(option+7)) { + case 'a': + if(*(option+10) == '\0') + EnableErrorChecks = midlEnableAll; + else + ErrorCheckAllocations = _True; + break; + case 'b': + ErrorCheckBounds = _True; + break; + case 'e': + ErrorCheckEnumRange = _True; + break; + case 'n': + EnableErrorChecks = midlDisableAll; + break; + case 'r': + ErrorCheckRefPointers = _True; + break; + case 's': + ErrorCheckStubData = _True; + break; + default: + found = false; + } + break; + case 0x5eb7af2: // /header filename + offset = 5; + case 0x0000358: // /h filename + HeaderFileName = option + offset + 3; + break; + case 0x0035ff4: // /iid filename + InterfaceIdentifierFileName = option+5; + break; + case 0x64b7933: // /mktyplib203 + MkTypLibCompatible = _True; + break; + case 0x64ceb12: // /newtlb + TypeLibFormat = "NewFormat"; + break; + case 0x8e0b0a2: // /no_def_idir + IgnoreStandardIncludePath = _True; + break; + case 0x65635ef: // /nologo + SuppressStartupBanner = _True; + break; + case 0x695e9f4: // /no_robust + ValidateAllParameters = _False; + break; + case 0x3656b22: // /notlb + GenerateTypeLibrary = _True; + break; + case 0x556dbee: // /no_warn + SuppressCompilerWarnings = _True; + break; + case 0x000035f: // /o filename + RedirectOutputAndErrors = option+3; + break; + case 0x662bb12: // /oldtlb + TypeLibFormat = "OldFormat"; + break; + case 0x00366c4: // /out directory + OutputDirectory = option+5; + break; + case 0x36796f9: // /proxy filename + ProxyFileName = option+7; + break; + case 0x6959c94: // /robust + ValidateParameters = _True; + break; + case 0x6a88df4: // /target {system} + if(*(option+11) == '6') + TargetEnvironment = midlTargetWin64; + else + TargetEnvironment = midlTargetWin32; + break; + case 0x69c9cf2: // /server {none|stub} + if(*(option+8) == 's') + GenerateServerFiles = "Stub"; + else + GenerateServerFiles = "None"; + break; + case 0x36aabb2: // /sstub filename + ServerStubFile = option+7; + break; + case 0x0036b22: // /tlb filename + TypeLibraryName = option+5; + break; + case 0x36e0162: // /win32 + TargetEnvironment = midlTargetWin32; + break; + case 0x36e0194: // /win64 + TargetEnvironment = midlTargetWin64; + break; + case 0x0003459: // /Oi + case 0x00345f3: // /Oic + case 0x0003463: // /Os + case 0x0003513: // /Zs + case 0x0035796: // /acf filename + case 0x3595cf4: // /c_ext + case 0xa64d3dd: // /confirm + case 0xa765b64: // /cpp_cmd cmd_line + case 0x03629f4: // /lcid + case 0x6495cc4: // /ms_ext + case 0x96c7a1e: // /ms_union + case 0x4996fa2: // /msc_ver + case 0x6555a40: // /no_cpp + case 0xf64d6a6: // /no_default_epv + case 0x6dd9384: // /no_format_opt + case 0x3655a70: // /nocpp + case 0x2b455a3: // /oldnames + case 0x0036696: // /osf + case 0x036679b: // /pack {N} + case 0x678bd38: // /prefix {all|client|server|switch} + case 0x96b702c: // /protocol {all|dce|ndr64} + case 0x3696aa3: // /rpcss + case 0x698ca60: // /savePP + case 0xce9b12b: // /syntax_check + case 0xc9b5f16: // /use_epv + AdditionalOptions += option; + break; + default: + // /W{0|1|2|3|4} case + if(*(option+1) == 'W') { + switch (*(option+2)) { + case '0': + WarningLevel = midlWarningLevel_0; + break; + case '1': + WarningLevel = midlWarningLevel_1; + break; + case '2': + WarningLevel = midlWarningLevel_2; + break; + case '3': + WarningLevel = midlWarningLevel_3; + break; + case '4': + WarningLevel = midlWarningLevel_4; + break; + default: + found = false; + } + } + break; + } + if(!found) + warn_msg(WarnLogic, "Could not parse MIDL option: %s", option); + return true; +} + +// VCLibrarianTool -------------------------------------------------- +VCLibrarianTool::VCLibrarianTool() + : IgnoreAllDefaultLibraries(unset), + SuppressStartupBanner(_True) +{ +} + +// VCCustomBuildTool ------------------------------------------------ +VCCustomBuildTool::VCCustomBuildTool() +{ + ToolName = "VCCustomBuildTool"; +} + +// VCResourceCompilerTool ------------------------------------------- +VCResourceCompilerTool::VCResourceCompilerTool() + : Culture(rcUseDefault), + IgnoreStandardIncludePath(unset), + ShowProgress(linkProgressNotSet), + SuppressStartupBanner(unset) +{ + PreprocessorDefinitions = QStringList("NDEBUG"); +} + +// VCDeploymentTool -------------------------------------------- +VCDeploymentTool::VCDeploymentTool() + : RegisterOutput(registerNo) +{ + DeploymentTag = "DeploymentTool"; + RemoteDirectory = ""; +} + +VCEventTool::VCEventTool(const QString &eventName) + : ExcludedFromBuild(unset) +{ + EventName = eventName; + ToolName = "VC"; + ToolName += eventName; + ToolName += "Tool"; +} + +// VCPostBuildEventTool --------------------------------------------- +VCPostBuildEventTool::VCPostBuildEventTool() + : VCEventTool("PostBuildEvent") +{ +} + +// VCPreBuildEventTool ---------------------------------------------- +VCPreBuildEventTool::VCPreBuildEventTool() + : VCEventTool("PreBuildEvent") +{ +} + +// VCPreLinkEventTool ----------------------------------------------- +VCPreLinkEventTool::VCPreLinkEventTool() + : VCEventTool("PreLinkEvent") +{ +} + +// VCConfiguration -------------------------------------------------- + +VCConfiguration::VCConfiguration() + : ATLMinimizesCRunTimeLibraryUsage(unset), + BuildBrowserInformation(unset), + CharacterSet(charSetNotSet), + ConfigurationType(typeApplication), + RegisterOutput(unset), + UseOfATL(useATLNotSet), + UseOfMfc(useMfcStdWin), + WholeProgramOptimization(unset) +{ + compiler.config = this; + linker.config = this; + idl.config = this; + custom.config = this; +} + +// VCFilter --------------------------------------------------------- +VCFilter::VCFilter() + : ParseFiles(unset), + Config(0) +{ + useCustomBuildTool = false; + useCompilerTool = false; +} + +void VCFilter::addFile(const QString& filename) +{ + Files += VCFilterFile(filename); +} + +void VCFilter::addFile(const VCFilterFile& fileInfo) +{ + Files += VCFilterFile(fileInfo); +} + +void VCFilter::addFiles(const QStringList& fileList) +{ + for (int i = 0; i < fileList.count(); ++i) + addFile(fileList.at(i)); +} + +void VCFilter::modifyPCHstage(QString str) +{ + bool autogenSourceFile = Project->autogenPrecompCPP; + bool pchThroughSourceFile = !Project->precompCPP.isEmpty(); + bool isCFile = false; + for (QStringList::Iterator it = Option::c_ext.begin(); it != Option::c_ext.end(); ++it) { + if (str.endsWith(*it)) { + isCFile = true; + break; + } + } + bool isHFile = Option::hasFileExtension(str, Option::h_ext) && (str == Project->precompH); + bool isCPPFile = pchThroughSourceFile && (str == Project->precompCPP); + + if(!isCFile && !isHFile && !isCPPFile) + return; + + if(isHFile && pchThroughSourceFile) { + if (autogenSourceFile) { + useCustomBuildTool = true; + QString toFile(Project->precompCPP); + CustomBuildTool.Description = "Generating precompiled header source file '" + toFile + "' ..."; + CustomBuildTool.Outputs += toFile; + + QStringList lines; + CustomBuildTool.CommandLine += + "echo /*-------------------------------------------------------------------- >" + toFile; + lines << "* Precompiled header source file used by Visual Studio.NET to generate"; + lines << "* the .pch file."; + lines << "*"; + lines << "* Due to issues with the dependencies checker within the IDE, it"; + lines << "* sometimes fails to recompile the PCH file, if we force the IDE to"; + lines << "* create the PCH file directly from the header file."; + lines << "*"; + lines << "* This file is auto-generated by qmake since no PRECOMPILED_SOURCE was"; + lines << "* specified, and is used as the common stdafx.cpp. The file is only"; + lines << QLatin1String("* generated when creating ") + + (Config->CompilerVersion < NET2010 ? ".vcproj" : ".vcxproj") + + " project files, and is not used for"; + lines << "* command line compilations by nmake."; + lines << "*"; + lines << "* WARNING: All changes made in this file will be lost."; + lines << "--------------------------------------------------------------------*/"; + lines << "#include \"" + Project->precompHFilename + "\""; + foreach(QString line, lines) + CustomBuildTool.CommandLine += "echo " + line + ">>" + toFile; + } + return; + } + + useCompilerTool = true; + // Setup PCH options + CompilerTool.UsePrecompiledHeader = (isCFile ? pchNone : pchCreateUsingSpecific); + CompilerTool.PrecompiledHeaderThrough = (isCPPFile ? Project->precompHFilename : QString("$(NOINHERIT)")); + CompilerTool.ForcedIncludeFiles = QStringList("$(NOINHERIT)"); +} + +bool VCFilter::addExtraCompiler(const VCFilterFile &info) +{ + const QStringList &extraCompilers = Project->extraCompilerSources.value(info.file); + if (extraCompilers.isEmpty()) + return false; + + QString inFile = info.file; + + // is the extracompiler rule on a file with a built in compiler? + const QStringList &objectMappedFile = Project->extraCompilerOutputs[inFile]; + bool hasBuiltIn = false; + if (!objectMappedFile.isEmpty()) { + hasBuiltIn = Project->hasBuiltinCompiler(objectMappedFile.at(0)); +// qDebug("*** Extra compiler file has object mapped file '%s' => '%s'", qPrintable(inFile), qPrintable(objectMappedFile.join(" "))); + } + + CustomBuildTool.AdditionalDependencies.clear(); + CustomBuildTool.CommandLine.clear(); + CustomBuildTool.Description.clear(); + CustomBuildTool.Outputs.clear(); + CustomBuildTool.ToolPath.clear(); + CustomBuildTool.ToolName = QLatin1String(_VCCustomBuildTool); + + for (int x = 0; x < extraCompilers.count(); ++x) { + const QString &extraCompilerName = extraCompilers.at(x); + + if (!Project->verifyExtraCompiler(extraCompilerName, inFile) && !hasBuiltIn) + continue; + + // All information about the extra compiler + QString tmp_out = Project->project->first(extraCompilerName + ".output"); + QString tmp_cmd = Project->project->variables()[extraCompilerName + ".commands"].join(" "); + QString tmp_cmd_name = Project->project->variables()[extraCompilerName + ".name"].join(" "); + QStringList tmp_dep = Project->project->variables()[extraCompilerName + ".depends"]; + QString tmp_dep_cmd = Project->project->variables()[extraCompilerName + ".depend_command"].join(" "); + QStringList vars = Project->project->variables()[extraCompilerName + ".variables"]; + QStringList configs = Project->project->variables()[extraCompilerName + ".CONFIG"]; + bool combined = configs.indexOf("combine") != -1; + + QString cmd, cmd_name, out; + QStringList deps, inputs; + // Variabel replacement of output name + out = Option::fixPathToTargetOS( + Project->replaceExtraCompilerVariables(tmp_out, inFile, QString()), + false); + + // If file has built-in compiler, we've swapped the input and output of + // the command, as we in Visual Studio cannot have a Custom Buildstep on + // a file which uses a built-in compiler. We would in this case only get + // the result from the extra compiler. If 'hasBuiltIn' is true, we know + // that we're actually on the _output_file_ of the result, and we + // therefore swap inFile and out below, since the extra-compiler still + // must see it as the original way. If the result also has a built-in + // compiler, too bad.. + if (hasBuiltIn) { + out = inFile; + inFile = objectMappedFile.at(0); + } + + // Dependency for the output + if(!tmp_dep.isEmpty()) + deps = tmp_dep; + if(!tmp_dep_cmd.isEmpty()) { + // Execute dependency command, and add every line as a dep + char buff[256]; + QString dep_cmd = Project->replaceExtraCompilerVariables(tmp_dep_cmd, + Option::fixPathToLocalOS(inFile, true, false), + out); + if(Project->canExecute(dep_cmd)) { + dep_cmd.prepend(QLatin1String("cd ") + + Project->escapeFilePath(Option::fixPathToLocalOS(Option::output_dir, false)) + + QLatin1String(" && ")); + if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) { + QString indeps; + while(!feof(proc)) { + int read_in = (int)fread(buff, 1, 255, proc); + if(!read_in) + break; + indeps += QByteArray(buff, read_in); + } + QT_PCLOSE(proc); + if(!indeps.isEmpty()) { + QStringList extradeps = indeps.split(QLatin1Char('\n')); + for (int i = 0; i < extradeps.count(); ++i) { + QString dd = extradeps.at(i).simplified(); + if (!dd.isEmpty()) + deps += Project->fileFixify(dd, QString(), Option::output_dir); + } + } + } + } + } + for (int i = 0; i < deps.count(); ++i) + deps[i] = Option::fixPathToTargetOS( + Project->replaceExtraCompilerVariables(deps.at(i), inFile, out), + false).trimmed(); + // Command for file + if (combined) { + // Add dependencies for each file + QStringList tmp_in = Project->project->variables()[extraCompilerName + ".input"]; + for (int a = 0; a < tmp_in.count(); ++a) { + const QStringList &files = Project->project->variables()[tmp_in.at(a)]; + for (int b = 0; b < files.count(); ++b) { + deps += Project->findDependencies(files.at(b)); + inputs += Option::fixPathToTargetOS(files.at(b), false); + } + } + deps += inputs; // input files themselves too.. + + // Replace variables for command w/all input files + // ### join gives path issues with directories containing spaces! + cmd = Project->replaceExtraCompilerVariables(tmp_cmd, + inputs.join(" "), + out); + } else { + deps += inFile; // input file itself too.. + cmd = Project->replaceExtraCompilerVariables(tmp_cmd, + inFile, + out); + } + // Name for command + if(!tmp_cmd_name.isEmpty()) { + cmd_name = Project->replaceExtraCompilerVariables(tmp_cmd_name, inFile, out); + } else { + int space = cmd.indexOf(' '); + if(space != -1) + cmd_name = cmd.left(space); + else + cmd_name = cmd; + if((cmd_name[0] == '\'' || cmd_name[0] == '"') && + cmd_name[0] == cmd_name[cmd_name.length()-1]) + cmd_name = cmd_name.mid(1,cmd_name.length()-2); + } + + // Fixify paths + for (int i = 0; i < deps.count(); ++i) + deps[i] = Option::fixPathToTargetOS(deps[i], false); + + + // Output in info.additionalFile ----------- + if (!CustomBuildTool.Description.isEmpty()) + CustomBuildTool.Description += ", "; + CustomBuildTool.Description += cmd_name; + CustomBuildTool.CommandLine += VCToolBase::fixCommandLine(cmd.trimmed()); + int space = cmd.indexOf(' '); + QFileInfo finf(cmd.left(space)); + if (CustomBuildTool.ToolPath.isEmpty()) + CustomBuildTool.ToolPath += Option::fixPathToTargetOS(finf.path()); + CustomBuildTool.Outputs += out; + + deps += CustomBuildTool.AdditionalDependencies; + deps += cmd.left(cmd.indexOf(' ')); + // Make sure that all deps are only once + QMap uniqDeps; + for (int c = 0; c < deps.count(); ++c) { + QString aDep = deps.at(c).trimmed(); + if (!aDep.isEmpty()) + uniqDeps[aDep] = false; + } + CustomBuildTool.AdditionalDependencies = uniqDeps.keys(); + } + + // Ensure that none of the output files are also dependencies. Or else, the custom buildstep + // will be rebuild every time, even if nothing has changed. + foreach(QString output, CustomBuildTool.Outputs) { + CustomBuildTool.AdditionalDependencies.removeAll(output); + } + + useCustomBuildTool = !CustomBuildTool.CommandLine.isEmpty(); + return useCustomBuildTool; +} + +// VCProjectSingleConfig -------------------------------------------- +VCFilter& VCProjectSingleConfig::filterForExtraCompiler(const QString &compilerName) +{ + for (int i = 0; i < ExtraCompilersFiles.count(); ++i) + if (ExtraCompilersFiles.at(i).Name == compilerName) + return ExtraCompilersFiles[i]; + + static VCFilter nullFilter; + return nullFilter; +} + +// Tree file generation --------------------------------------------- +void TreeNode::generateXML(XmlOutput &xml, const QString &tagName, VCProject &tool, const QString &filter) { + if (children.size()) { + // Filter + ChildrenMap::ConstIterator it, end = children.constEnd(); + if (!tagName.isEmpty()) { + xml << tag("Filter") + << attr("Name", tagName) + << attr("Filter", ""); + } + // First round, do nested filters + for (it = children.constBegin(); it != end; ++it) + if ((*it)->children.size()) + (*it)->generateXML(xml, it.key(), tool, filter); + // Second round, do leafs + for (it = children.constBegin(); it != end; ++it) + if (!(*it)->children.size()) + (*it)->generateXML(xml, it.key(), tool, filter); + + if (!tagName.isEmpty()) + xml << closetag("Filter"); + } else { + // Leaf + VCProjectWriter::outputFileConfigs(tool, xml, info, filter); + } +} + +// Flat file generation --------------------------------------------- +void FlatNode::generateXML(XmlOutput &xml, const QString &/*tagName*/, VCProject &tool, const QString &filter) { + if (children.size()) { + ChildrenMapFlat::ConstIterator it = children.constBegin(); + ChildrenMapFlat::ConstIterator end = children.constEnd(); + for (; it != end; ++it) { + VCProjectWriter::outputFileConfigs(tool, xml, (*it), filter); + } + } +} + +void VCProjectWriter::write(XmlOutput &xml, VCProjectSingleConfig &tool) +{ + xml << decl("1.0", "Windows-1252") + << tag(_VisualStudioProject) + << attrS(_ProjectType, "Visual C++") + << attrS(_Version, tool.Version) + << attrS(_Name, tool.Name) + << attrS(_ProjectGUID, tool.ProjectGUID) + << attrS(_Keyword, tool.Keyword) + << attrS(_SccProjectName, tool.SccProjectName) + << attrS(_SccLocalPath, tool.SccLocalPath) + << tag(_Platforms) + << tag(_Platform) + << attrS(_Name, tool.PlatformName) + << closetag(_Platforms) + << tag(_Configurations); + write(xml, tool.Configuration); + xml << closetag(_Configurations) + << tag(q_Files); + // Add this configuration into a multi-config project, since that's where we have the flat/tree + // XML output functionality + VCProject tempProj; + tempProj.SingleProjects += tool; + outputFilter(tempProj, xml, "Sources"); + outputFilter(tempProj, xml, "Headers"); + outputFilter(tempProj, xml, "GeneratedFiles"); + outputFilter(tempProj, xml, "LexYaccFiles"); + outputFilter(tempProj, xml, "TranslationFiles"); + outputFilter(tempProj, xml, "FormFiles"); + outputFilter(tempProj, xml, "ResourceFiles"); + for (int x = 0; x < tempProj.ExtraCompilers.count(); ++x) { + outputFilter(tempProj, xml, tempProj.ExtraCompilers.at(x)); + } + outputFilter(tempProj, xml, "RootFiles"); + xml << closetag(q_Files) + << tag(_Globals) + << data(); // No "/>" end tag +} + +void VCProjectWriter::write(XmlOutput &xml, VCProject &tool) +{ + if (tool.SingleProjects.count() == 0) { + warn_msg(WarnLogic, "Generator: .NET: no single project in merge project, no output"); + return; + } + + xml << decl("1.0", "Windows-1252") + << tag(_VisualStudioProject) + << attrS(_ProjectType, "Visual C++") + << attrS(_Version, tool.Version) + << attrS(_Name, tool.Name) + << attrS(_ProjectGUID, tool.ProjectGUID) + << attrS(_Keyword, tool.Keyword) + << attrS(_SccProjectName, tool.SccProjectName) + << attrS(_SccLocalPath, tool.SccLocalPath) + << tag(_Platforms) + << tag(_Platform) + << attrS(_Name, tool.PlatformName) + << closetag(_Platforms) + << tag(_Configurations); + // Output each configuration + for (int i = 0; i < tool.SingleProjects.count(); ++i) + write(xml, tool.SingleProjects.at(i).Configuration); + xml << closetag(_Configurations) + << tag(q_Files); + outputFilter(tool, xml, "Sources"); + outputFilter(tool, xml, "Headers"); + outputFilter(tool, xml, "GeneratedFiles"); + outputFilter(tool, xml, "LexYaccFiles"); + outputFilter(tool, xml, "TranslationFiles"); + outputFilter(tool, xml, "FormFiles"); + outputFilter(tool, xml, "ResourceFiles"); + for (int x = 0; x < tool.ExtraCompilers.count(); ++x) { + outputFilter(tool, xml, tool.ExtraCompilers.at(x)); + } + outputFilter(tool, xml, "RootFiles"); + xml << closetag(q_Files) + << tag(_Globals) + << data(); // No "/>" end tag +} + +void VCProjectWriter::write(XmlOutput &xml, const VCCLCompilerTool &tool) +{ + xml << tag(_Tool) + << attrS(_Name, _VCCLCompilerTool) + << attrX(_AdditionalIncludeDirectories, tool.AdditionalIncludeDirectories) + << attrX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrX(_AdditionalUsingDirectories, tool.AdditionalUsingDirectories) + << attrS(_AssemblerListingLocation, tool.AssemblerListingLocation) + << attrE(_AssemblerOutput, tool.AssemblerOutput, /*ifNot*/ asmListingNone) + << attrE(_BasicRuntimeChecks, tool.BasicRuntimeChecks, /*ifNot*/ runtimeBasicCheckNone) + << attrE(_BrowseInformation, tool.BrowseInformation, /*ifNot*/ brInfoNone) + << attrS(_BrowseInformationFile, tool.BrowseInformationFile) + << attrT(_BufferSecurityCheck, tool.BufferSecurityCheck) + << attrE(_CallingConvention, tool.CallingConvention, /*ifNot*/ callConventionDefault) + << attrE(_CompileAs, tool.CompileAs, compileAsDefault) + << attrE(_CompileAsManaged, tool.CompileAsManaged, /*ifNot*/ managedDefault) + << attrT(_CompileOnly, tool.CompileOnly) + << attrE(_DebugInformationFormat, tool.DebugInformationFormat, /*ifNot*/ debugUnknown) + << attrT(_DefaultCharIsUnsigned, tool.DefaultCharIsUnsigned) + << attrT(_Detect64BitPortabilityProblems, tool.Detect64BitPortabilityProblems) + << attrT(_DisableLanguageExtensions, tool.DisableLanguageExtensions) + << attrX(_DisableSpecificWarnings, tool.DisableSpecificWarnings) + << attrE(_EnableEnhancedInstructionSet, tool.EnableEnhancedInstructionSet, /*ifnot*/ archNotSet) + << attrT(_EnableFiberSafeOptimizations, tool.EnableFiberSafeOptimizations) + << attrT(_EnableFunctionLevelLinking, tool.EnableFunctionLevelLinking) + << attrT(_EnableIntrinsicFunctions, tool.EnableIntrinsicFunctions) + << xformExceptionHandlingNET2005(tool.ExceptionHandling, tool.config->CompilerVersion) + << attrT(_ExpandAttributedSource, tool.ExpandAttributedSource) + << attrE(_FavorSizeOrSpeed, tool.FavorSizeOrSpeed, /*ifNot*/ favorNone) + + << attrE(_FloatingPointModel, tool.FloatingPointModel, /*ifNot*/ floatingPointNotSet) + << attrT(_FloatingPointExceptions, tool.FloatingPointExceptions) + + << attrT(_ForceConformanceInForLoopScope, tool.ForceConformanceInForLoopScope) + << attrX(_ForcedIncludeFiles, tool.ForcedIncludeFiles) + << attrX(_ForcedUsingFiles, tool.ForcedUsingFiles) + << attrE(_GeneratePreprocessedFile, tool.GeneratePreprocessedFile, /*ifNot*/ preprocessUnknown) + << attrT(_GlobalOptimizations, tool.GlobalOptimizations) + << attrT(_IgnoreStandardIncludePath, tool.IgnoreStandardIncludePath) + << attrT(_ImproveFloatingPointConsistency, tool.ImproveFloatingPointConsistency) + << attrE(_InlineFunctionExpansion, tool.InlineFunctionExpansion, /*ifNot*/ expandDefault) + << attrT(_KeepComments, tool.KeepComments) + << attrT(_MinimalRebuild, tool.MinimalRebuild) + << attrS(_ObjectFile, tool.ObjectFile) + << attrT(_OmitFramePointers, tool.OmitFramePointers) + << attrT(_OpenMP, tool.OpenMP) + << attrE(_Optimization, tool.Optimization, /*ifNot*/ optimizeDefault) + << attrE(_OptimizeForProcessor, tool.OptimizeForProcessor, /*ifNot*/ procOptimizeBlended) + << attrT(_OptimizeForWindowsApplication, tool.OptimizeForWindowsApplication) + << attrS(_OutputFile, tool.OutputFile) + << attrS(_PrecompiledHeaderFile, tool.PrecompiledHeaderFile) + << attrS(_PrecompiledHeaderThrough, tool.PrecompiledHeaderThrough) + << attrX(_PreprocessorDefinitions, tool.PreprocessorDefinitions) + << (tool.ProgramDataBaseFileName.isNull() ? noxml() : attr(_ProgramDataBaseFileName, tool.ProgramDataBaseFileName)) + << attrE(_RuntimeLibrary, tool.RuntimeLibrary, /*ifNot*/ rtUnknown) + << attrT(_RuntimeTypeInfo, tool.RuntimeTypeInfo) + << attrT(_ShowIncludes, tool.ShowIncludes) + << attrT(_SmallerTypeCheck, tool.SmallerTypeCheck) + << attrT(_StringPooling, tool.StringPooling) + << attrE(_StructMemberAlignment, tool.StructMemberAlignment, /*ifNot*/ alignNotSet) + << attrT(_SuppressStartupBanner, tool.SuppressStartupBanner) + << attrT(_TreatWChar_tAsBuiltInType, tool.TreatWChar_tAsBuiltInType) + << attrT(_TurnOffAssemblyGeneration, tool.TurnOffAssemblyGeneration) + << attrT(_UndefineAllPreprocessorDefinitions, tool.UndefineAllPreprocessorDefinitions) + << attrX(_UndefinePreprocessorDefinitions, tool.UndefinePreprocessorDefinitions) + << xformUsePrecompiledHeaderForNET2005(tool.UsePrecompiledHeader, tool.config->CompilerVersion) + << attrT(_WarnAsError, tool.WarnAsError) + << attrE(_WarningLevel, tool.WarningLevel, /*ifNot*/ warningLevelUnknown) + << attrT(_WholeProgramOptimization, tool.WholeProgramOptimization) + << attrE(_CompileForArchitecture, tool.CompileForArchitecture, /*ifNot*/ archUnknown) + << attrT(_InterworkCalls, tool.InterworkCalls) + + << closetag(_Tool); +} + +void VCProjectWriter::write(XmlOutput &xml, const VCLinkerTool &tool) +{ + xml << tag(_Tool) + << attrS(_Name, _VCLinkerTool) + << attrX(_AdditionalDependencies, tool.AdditionalDependencies, " ") + << attrX(_AdditionalLibraryDirectories, tool.AdditionalLibraryDirectories) + << attrX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrX(_AddModuleNamesToAssembly, tool.AddModuleNamesToAssembly) + << attrS(_BaseAddress, tool.BaseAddress) + << attrX(_DelayLoadDLLs, tool.DelayLoadDLLs) + << attrE(_EnableCOMDATFolding, tool.EnableCOMDATFolding, /*ifNot*/ optFoldingDefault) + << attrS(_EntryPointSymbol, tool.EntryPointSymbol) + << attrX(_ForceSymbolReferences, tool.ForceSymbolReferences) + << attrS(_FunctionOrder, tool.FunctionOrder) + << attrT(_GenerateDebugInformation, tool.GenerateDebugInformation) + << attrT(_GenerateMapFile, tool.GenerateMapFile) + << attrL(_HeapCommitSize, tool.HeapCommitSize, /*ifNot*/ -1) + << attrL(_HeapReserveSize, tool.HeapReserveSize, /*ifNot*/ -1) + << attrT(_IgnoreAllDefaultLibraries, tool.IgnoreAllDefaultLibraries) + << attrX(_IgnoreDefaultLibraryNames, tool.IgnoreDefaultLibraryNames) + << attrT(_IgnoreEmbeddedIDL, tool.IgnoreEmbeddedIDL) + << attrT(_IgnoreImportLibrary, tool.IgnoreImportLibrary) + << attrS(_ImportLibrary, tool.ImportLibrary) + << attrE(_LargeAddressAware, tool.LargeAddressAware, /*ifNot*/ addrAwareDefault) + << attrT(_LinkDLL, tool.LinkDLL) + << attrE(_LinkIncremental, tool.LinkIncremental, /*ifNot*/ linkIncrementalDefault) + << attrE(_LinkTimeCodeGeneration, tool.LinkTimeCodeGeneration) + << attrS(_LinkToManagedResourceFile, tool.LinkToManagedResourceFile) + << attrT(_MapExports, tool.MapExports) + << attrS(_MapFileName, tool.MapFileName) + << attrT(_MapLines, tool.MapLines) + << attrS(_MergedIDLBaseFileName, tool.MergedIDLBaseFileName) + << attrS(_MergeSections, tool.MergeSections) + << attrS(_MidlCommandFile, tool.MidlCommandFile) + << attrS(_ModuleDefinitionFile, tool.ModuleDefinitionFile) + << attrE(_OptimizeForWindows98, tool.OptimizeForWindows98, /*ifNot*/ optWin98Default) + << attrE(_OptimizeReferences, tool.OptimizeReferences, /*ifNot*/ optReferencesDefault) + << attrS(_OutputFile, tool.OutputFile) + << attr(_ProgramDatabaseFile, tool.ProgramDatabaseFile) + << attrT(_RegisterOutput, tool.RegisterOutput) + << attrT(_ResourceOnlyDLL, tool.ResourceOnlyDLL) + << attrT(_SetChecksum, tool.SetChecksum) + << attrE(_ShowProgress, tool.ShowProgress, /*ifNot*/ linkProgressNotSet) + << attrL(_StackCommitSize, tool.StackCommitSize, /*ifNot*/ -1) + << attrL(_StackReserveSize, tool.StackReserveSize, /*ifNot*/ -1) + << attrS(_StripPrivateSymbols, tool.StripPrivateSymbols) + << attrE(_SubSystem, tool.SubSystem) + << attrT(_SupportUnloadOfDelayLoadedDLL, tool.SupportUnloadOfDelayLoadedDLL) + << attrT(_SuppressStartupBanner, tool.SuppressStartupBanner) + << attrT(_SwapRunFromCD, tool.SwapRunFromCD) + << attrT(_SwapRunFromNet, tool.SwapRunFromNet) + << attrE(_TargetMachine, tool.TargetMachine, /*ifNot*/ machineNotSet) + << attrE(_TerminalServerAware, tool.TerminalServerAware, /*ifNot*/ termSvrAwareDefault) + << attrT(_TurnOffAssemblyGeneration, tool.TurnOffAssemblyGeneration) + << attrS(_TypeLibraryFile, tool.TypeLibraryFile) + << attrL(_TypeLibraryResourceID, tool.TypeLibraryResourceID, /*ifNot*/ rcUseDefault) + << attrS(_Version, tool.Version) + << closetag(_Tool); +} + +void VCProjectWriter::write(XmlOutput &xml, const VCMIDLTool &tool) +{ + xml << tag(_Tool) + << attrS(_Name, _VCMIDLTool) + << attrX(_AdditionalIncludeDirectories, tool.AdditionalIncludeDirectories) + << attrX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrX(_CPreprocessOptions, tool.CPreprocessOptions) + << attrE(_DefaultCharType, tool.DefaultCharType) + << attrS(_DLLDataFileName, tool.DLLDataFileName) + << attrE(_EnableErrorChecks, tool.EnableErrorChecks) + << attrT(_ErrorCheckAllocations, tool.ErrorCheckAllocations) + << attrT(_ErrorCheckBounds, tool.ErrorCheckBounds) + << attrT(_ErrorCheckEnumRange, tool.ErrorCheckEnumRange) + << attrT(_ErrorCheckRefPointers, tool.ErrorCheckRefPointers) + << attrT(_ErrorCheckStubData, tool.ErrorCheckStubData) + << attrX(_FullIncludePath, tool.FullIncludePath) + << attrT(_GenerateStublessProxies, tool.GenerateStublessProxies) + << attrT(_GenerateTypeLibrary, tool.GenerateTypeLibrary) + << attrS(_HeaderFileName, tool.HeaderFileName) + << attrT(_IgnoreStandardIncludePath, tool.IgnoreStandardIncludePath) + << attrS(_InterfaceIdentifierFileName, tool.InterfaceIdentifierFileName) + << attrT(_MkTypLibCompatible, tool.MkTypLibCompatible) + << attrS(_OutputDirectory, tool.OutputDirectory) + << attrX(_PreprocessorDefinitions, tool.PreprocessorDefinitions) + << attrS(_ProxyFileName, tool.ProxyFileName) + << attrS(_RedirectOutputAndErrors, tool.RedirectOutputAndErrors) + << attrE(_StructMemberAlignment, tool.StructMemberAlignment, /*ifNot*/ midlAlignNotSet) + << attrT(_SuppressStartupBanner, tool.SuppressStartupBanner) + << attrE(_TargetEnvironment, tool.TargetEnvironment, /*ifNot*/ midlTargetNotSet) + << attrS(_TypeLibraryName, tool.TypeLibraryName) + << attrX(_UndefinePreprocessorDefinitions, tool.UndefinePreprocessorDefinitions) + << attrT(_ValidateParameters, tool.ValidateParameters) + << attrT(_WarnAsError, tool.WarnAsError) + << attrE(_WarningLevel, tool.WarningLevel) + << closetag(_Tool); +} + +void VCProjectWriter::write(XmlOutput &xml, const VCCustomBuildTool &tool) +{ + xml << tag(_Tool) + << attrS(_Name, tool.ToolName) + << attrX(_AdditionalDependencies, tool.AdditionalDependencies, ";") + << attrS(_CommandLine, tool.CommandLine.join(vcCommandSeparator())) + << attrS(_Description, tool.Description) + << attrX(_Outputs, tool.Outputs, ";") + << attrS(_Path, tool.ToolPath) + << closetag(_Tool); +} + +void VCProjectWriter::write(XmlOutput &xml, const VCLibrarianTool &tool) +{ + xml + << tag(_Tool) + << attrS(_Name, _VCLibrarianTool) + << attrX(_AdditionalDependencies, tool.AdditionalDependencies) + << attrX(_AdditionalLibraryDirectories, tool.AdditionalLibraryDirectories) + << attrX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrX(_ExportNamedFunctions, tool.ExportNamedFunctions) + << attrX(_ForceSymbolReferences, tool.ForceSymbolReferences) + << attrT(_IgnoreAllDefaultLibraries, tool.IgnoreAllDefaultLibraries) + << attrX(_IgnoreDefaultLibraryNames, tool.IgnoreDefaultLibraryNames) + << attrS(_ModuleDefinitionFile, tool.ModuleDefinitionFile) + << attrS(_OutputFile, tool.OutputFile) + << attrT(_SuppressStartupBanner, tool.SuppressStartupBanner) + << closetag(_Tool); +} + +void VCProjectWriter::write(XmlOutput &xml, const VCResourceCompilerTool &tool) +{ + xml + << tag(_Tool) + << attrS(_Name, _VCResourceCompilerTool) + << attrS(_Path, tool.ToolPath) + << attrX(_AdditionalIncludeDirectories, tool.AdditionalIncludeDirectories) + << attrX(_AdditionalOptions, tool.AdditionalOptions, " ") + << attrE(_Culture, tool.Culture, /*ifNot*/ rcUseDefault) + << attrX(_FullIncludePath, tool.FullIncludePath) + << attrT(_IgnoreStandardIncludePath, tool.IgnoreStandardIncludePath) + << attrX(_PreprocessorDefinitions, tool.PreprocessorDefinitions) + << attrS(_ResourceOutputFileName, tool.ResourceOutputFileName) + << attrE(_ShowProgress, tool.ShowProgress, /*ifNot*/ linkProgressNotSet) + << closetag(_Tool); +} + +void VCProjectWriter::write(XmlOutput &xml, const VCEventTool &tool) +{ + xml + << tag(_Tool) + << attrS(_Name, tool.ToolName) + << attrS(_Path, tool.ToolPath) + << attrS(_CommandLine, tool.CommandLine.join(vcCommandSeparator())) + << attrS(_Description, tool.Description) + << attrT(_ExcludedFromBuild, tool.ExcludedFromBuild) + << closetag(_Tool); +} + +void VCProjectWriter::write(XmlOutput &xml, const VCDeploymentTool &tool) +{ + if (tool.AdditionalFiles.isEmpty()) + return; + xml << tag(tool.DeploymentTag) + << attrS(_RemoteDirectory, tool.RemoteDirectory) + << attrE(_RegisterOutput, tool.RegisterOutput) + << attrS(_AdditionalFiles, tool.AdditionalFiles) + << closetag(tool.DeploymentTag); +} + +void VCProjectWriter::write(XmlOutput &xml, const VCConfiguration &tool) +{ + xml << tag(_Configuration) + << attrS(_Name, tool.Name) + << attrS(_OutputDirectory, tool.OutputDirectory) + << attrT(_ATLMinimizesCRunTimeLibraryUsage, tool.ATLMinimizesCRunTimeLibraryUsage) + << attrT(_BuildBrowserInformation, tool.BuildBrowserInformation) + << attrE(_CharacterSet, tool.CharacterSet, /*ifNot*/ charSetNotSet) + << attrE(_ConfigurationType, tool.ConfigurationType) + << attrS(_DeleteExtensionsOnClean, tool.DeleteExtensionsOnClean) + << attrS(_ImportLibrary, tool.ImportLibrary) + << attrS(_IntermediateDirectory, tool.IntermediateDirectory) + << attrS(_PrimaryOutput, tool.PrimaryOutput) + << attrS(_ProgramDatabase, tool.ProgramDatabase) + << attrT(_RegisterOutput, tool.RegisterOutput) + << attrE(_UseOfATL, tool.UseOfATL, /*ifNot*/ useATLNotSet) + << attrE(_UseOfMfc, tool.UseOfMfc) + << attrT(_WholeProgramOptimization, tool.WholeProgramOptimization); + write(xml, tool.compiler); + write(xml, tool.custom); + if (tool.ConfigurationType == typeStaticLibrary) + write(xml, tool.librarian); + else + write(xml, tool.linker); + write(xml, tool.idl); + write(xml, tool.postBuild); + write(xml, tool.preBuild); + write(xml, tool.preLink); + write(xml, tool.resource); + write(xml, tool.deployment); + xml << closetag(_Configuration); +} + +void VCProjectWriter::write(XmlOutput &xml, VCFilter &tool) +{ + if(!tool.Files.count()) + return; + + if (!tool.Name.isEmpty()) { + xml << tag(_Filter) + << attrS(_Name, tool.Name) + << attrS(_Filter, tool.Filter) + << attrS(_UniqueIdentifier, tool.Guid) + << attrT(_ParseFiles, tool.ParseFiles); + } + for (int i = 0; i < tool.Files.count(); ++i) { + const VCFilterFile &info = tool.Files.at(i); + xml << tag(q_File) + << attrS(_RelativePath, Option::fixPathToLocalOS(info.file)) + << data(); // In case no custom builds, to avoid "/>" endings + outputFileConfig(tool, xml, tool.Files.at(i).file); + xml << closetag(q_File); + } + if (!tool.Name.isEmpty()) + xml << closetag(_Filter); +} + +// outputs a given filter for all existing configurations of a project +void VCProjectWriter::outputFilter(VCProject &project, XmlOutput &xml, const QString &filtername) +{ + Node *root; + if (project.SingleProjects.at(0).flat_files) + root = new FlatNode; + else + root = new TreeNode; + + QString name, extfilter, guid; + triState parse; + + for (int i = 0; i < project.SingleProjects.count(); ++i) { + VCFilter filter; + const VCProjectSingleConfig &projectSingleConfig = project.SingleProjects.at(i); + if (filtername == "RootFiles") { + filter = projectSingleConfig.RootFiles; + } else if (filtername == "Sources") { + filter = projectSingleConfig.SourceFiles; + } else if (filtername == "Headers") { + filter = projectSingleConfig.HeaderFiles; + } else if (filtername == "GeneratedFiles") { + filter = projectSingleConfig.GeneratedFiles; + } else if (filtername == "LexYaccFiles") { + filter = projectSingleConfig.LexYaccFiles; + } else if (filtername == "TranslationFiles") { + filter = projectSingleConfig.TranslationFiles; + } else if (filtername == "FormFiles") { + filter = projectSingleConfig.FormFiles; + } else if (filtername == "ResourceFiles") { + filter = projectSingleConfig.ResourceFiles; + } else { + // ExtraCompilers + filter = project.SingleProjects[i].filterForExtraCompiler(filtername); + } + + // Merge all files in this filter to root tree + for (int x = 0; x < filter.Files.count(); ++x) + root->addElement(filter.Files.at(x)); + + // Save filter setting from first filter. Next filters + // may differ but we cannot handle that. (ex. extfilter) + if (name.isEmpty()) { + name = filter.Name; + extfilter = filter.Filter; + parse = filter.ParseFiles; + guid = filter.Guid; + } + } + + if (!root->hasElements()) + return; + + // Actual XML output ---------------------------------- + if (!name.isEmpty()) { + xml << tag(_Filter) + << attrS(_Name, name) + << attrS(_Filter, extfilter) + << attrS(_UniqueIdentifier, guid) + << attrT(_ParseFiles, parse); + } + root->generateXML(xml, "", project, filtername); // output root tree + if (!name.isEmpty()) + xml << closetag(_Filter); +} + +// Output all configurations (by filtername) for a file (by info) +// A filters config output is in VCFilter.outputFileConfig() +void VCProjectWriter::outputFileConfigs(VCProject &project, XmlOutput &xml, const VCFilterFile &info, const QString &filtername) +{ + xml << tag(q_File) + << attrS(_RelativePath, Option::fixPathToLocalOS(info.file)); + for (int i = 0; i < project.SingleProjects.count(); ++i) { + VCFilter filter; + const VCProjectSingleConfig &projectSingleConfig = project.SingleProjects.at(i); + if (filtername == "RootFiles") { + filter = projectSingleConfig.RootFiles; + } else if (filtername == "Sources") { + filter = projectSingleConfig.SourceFiles; + } else if (filtername == "Headers") { + filter = projectSingleConfig.HeaderFiles; + } else if (filtername == "GeneratedFiles") { + filter = projectSingleConfig.GeneratedFiles; + } else if (filtername == "LexYaccFiles") { + filter = projectSingleConfig.LexYaccFiles; + } else if (filtername == "TranslationFiles") { + filter = projectSingleConfig.TranslationFiles; + } else if (filtername == "FormFiles") { + filter = projectSingleConfig.FormFiles; + } else if (filtername == "ResourceFiles") { + filter = projectSingleConfig.ResourceFiles; + } else { + // ExtraCompilers + filter = project.SingleProjects[i].filterForExtraCompiler(filtername); + } + + if (filter.Config) // only if the filter is not empty + outputFileConfig(filter, xml, info.file); + } + xml << closetag(q_File); +} + +void VCProjectWriter::outputFileConfig(VCFilter &filter, XmlOutput &xml, const QString &filename) +{ + // Clearing each filter tool + filter.useCustomBuildTool = false; + filter.useCompilerTool = false; + filter.CustomBuildTool = VCCustomBuildTool(); + filter.CompilerTool = VCCLCompilerTool(); + + // Unset some default options + filter.CustomBuildTool.config = filter.Config; + filter.CompilerTool.BufferSecurityCheck = unset; + filter.CompilerTool.DebugInformationFormat = debugUnknown; + filter.CompilerTool.ExceptionHandling = ehDefault; + filter.CompilerTool.GeneratePreprocessedFile = preprocessUnknown; + filter.CompilerTool.Optimization = optimizeDefault; + filter.CompilerTool.ProgramDataBaseFileName.clear(); + filter.CompilerTool.RuntimeLibrary = rtUnknown; + filter.CompilerTool.WarningLevel = warningLevelUnknown; + filter.CompilerTool.config = filter.Config; + + bool inBuild = false; + VCFilterFile info; + for (int i = 0; i < filter.Files.count(); ++i) { + if (filter.Files.at(i).file == filename) { + info = filter.Files.at(i); + inBuild = true; + } + } + inBuild &= !info.excludeFromBuild; + + if (inBuild) { + filter.addExtraCompiler(info); + if(filter.Project->usePCH) + filter.modifyPCHstage(info.file); + } else { + // Excluded files uses an empty compiler stage + if(info.excludeFromBuild) + filter.useCompilerTool = true; + } + + // Actual XML output ---------------------------------- + if (filter.useCustomBuildTool || filter.useCompilerTool || !inBuild) { + xml << tag(_FileConfiguration) + << attr(_Name, filter.Config->Name) + << (!inBuild ? attrS(_ExcludedFromBuild, "true") : noxml()); + if (filter.useCustomBuildTool) + filter.Project->projectWriter->write(xml, filter.CustomBuildTool); + if (filter.useCompilerTool) + filter.Project->projectWriter->write(xml, filter.CompilerTool); + xml << closetag(_FileConfiguration); + } +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/msvc_objectmodel.h b/qmake/generators/win32/msvc_objectmodel.h new file mode 100644 index 0000000000..3e62fb41ee --- /dev/null +++ b/qmake/generators/win32/msvc_objectmodel.h @@ -0,0 +1,1141 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MSVC_OBJECTMODEL_H +#define MSVC_OBJECTMODEL_H + +#include "project.h" +#include "xmloutput.h" +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +enum DotNET { + NETUnknown = 0, + NET2002 = 0x70, + NET2003 = 0x71, + NET2005 = 0x80, + NET2008 = 0x90, + NET2010 = 0xa0 +}; + +/* + This Object model is of course VERY simplyfied, + and does not actually follow the original MSVC + object model. However, it fulfilles the basic + needs for qmake +*/ + +/* + If a triState value is 'unset' then the + corresponding property is not in the output, + forcing the tool to utilize default values. + False/True values will be in the output... +*/ +enum customBuildCheck { + none, + mocSrc, + mocHdr, + lexyacc +}; +enum triState { + unset = -1, + _False = 0, + _True = 1 +}; + +triState operator!(const triState &rhs); + +enum addressAwarenessType { + addrAwareDefault, + addrAwareNoLarge, + addrAwareLarge +}; +enum asmListingOption { + asmListingNone, + asmListingAssemblyOnly, + asmListingAsmMachineSrc, + asmListingAsmMachine, + asmListingAsmSrc +}; +enum basicRuntimeCheckOption { + runtimeBasicCheckNone, + runtimeCheckStackFrame, + runtimeCheckUninitVariables, + runtimeBasicCheckAll +}; +enum browseInfoOption { + brInfoNone, + brAllInfo, + brNoLocalSymbols +}; +enum callingConventionOption { + callConventionDefault = -1, + callConventionCDecl, + callConventionFastCall, + callConventionStdCall +}; +enum charSet { + charSetNotSet, + charSetUnicode, + charSetMBCS +}; +enum compileAsManagedOptions { + managedDefault = -1, // Was: noAssembly + managedAssembly = 1, + managedAssemblyPure = 2, // Old was: Assembly + managedAssemblySafe = 3, + managedAssemblyOldSyntax = 4 +}; +enum CompileAsOptions{ + compileAsDefault, + compileAsC, + compileAsCPlusPlus +}; +enum ConfigurationTypes { + typeUnknown = 0, + typeApplication = 1, + typeDynamicLibrary = 2, + typeStaticLibrary = 4, + typeGeneric = 10 +}; +enum debugOption { + debugUnknown = -1, + debugDisabled, + debugOldStyleInfo, + debugLineInfoOnly, + debugEnabled, + debugEditAndContinue +}; +enum eAppProtectionOption { + eAppProtectUnchanged, + eAppProtectLow, + eAppProtectMedium, + eAppProtectHigh +}; +enum enhancedInstructionSetOption { + archNotSet = 0, + archSSE = 1, + archSSE2 = 2 +}; +enum exceptionHandling { + ehDefault = -1, + ehNone = 0, + ehNoSEH = 1, + ehSEH = 2 +}; +enum enumResourceLangID { + rcUseDefault = 0, + rcAfrikaans = 1078, + rcAlbanian = 1052, + rcArabicAlgeria = 5121, + rcArabicBahrain = 15361, + rcArabicEgypt = 3073, + rcArabicIraq = 2049, + rcArabicJordan = 11265, + rcArabicKuwait = 13313, + rcArabicLebanon = 12289, + rcArabicLibya = 4097, + rcArabicMorocco = 6145, + rcArabicOman = 8193, + rcArabicQatar = 16385, + rcArabicSaudi = 1025, + rcArabicSyria = 10241, + rcArabicTunisia = 7169, + rcArabicUnitedArabEmirates = 14337, + rcArabicYemen = 9217, + rcBasque = 1069, + rcBulgarian = 1026, + rcByelorussian = 1059, + rcCatalan = 1027, + rcChineseHongKong = 3076, + rcChinesePRC = 2052, + rcChineseSingapore = 4100, + rcChineseTaiwan = 1028, + rcCroatian = 1050, + rcCzech = 1029, + rcDanish = 1030, + rcDutchBelgium = 2067, + rcDutchStandard = 1043, + rcEnglishAustralia = 3081, + rcEnglishBritain = 2057, + rcEnglishCanada = 4105, + RcEnglishCaribbean = 9225, + rcEnglishIreland = 6153, + rcEnglishJamaica = 8201, + rcEnglishNewZealand = 5129, + rcEnglishSouthAfrica = 7177, + rcEnglishUS = 1033, + rcEstonian = 1061, + rcFarsi = 1065, + rcFinnish = 1035, + rcFrenchBelgium = 2060, + rcFrenchCanada = 3084, + rcFrenchLuxembourg = 5132, + rcFrenchStandard = 1036, + rcFrenchSwitzerland = 4108, + rcGermanAustria = 3079, + rcGermanLichtenstein = 5127, + rcGermanLuxembourg = 4103, + rcGermanStandard = 1031, + rcGermanSwitzerland = 2055, + rcGreek = 1032, + rcHebrew = 1037, + rcHungarian = 1038, + rcIcelandic = 1039, + rcIndonesian = 1057, + rcItalianStandard = 1040, + rcItalianSwitzerland = 2064, + rcJapanese = 1041, + rcKorean = 1042, + rcKoreanJohab = 2066, + rcLatvian = 1062, + rcLithuanian = 1063, + rcNorwegianBokmal = 1044, + rcNorwegianNynorsk = 2068, + rcPolish = 1045, + rcPortugueseBrazilian = 1046, + rcPortugueseStandard = 2070, + rcRomanian = 1048, + rcRussian = 1049, + rcSerbian = 2074, + rcSlovak = 1051, + rcSpanishArgentina = 11274, + rcSpanishBolivia = 16394, + rcSpanishChile = 13322, + rcSpanishColombia = 9226, + rcSpanishCostaRica = 5130, + rcSpanishDominicanRepublic = 7178, + rcSpanishEcuador = 12298, + rcSpanishGuatemala = 4106, + rcSpanishMexico = 2058, + rcSpanishModern = 3082, + rcSpanishPanama = 6154, + rcSpanishParaguay = 15370, + rcSpanishPeru = 10250, + rcSpanishTraditional = 1034, + rcSpanishUruguay = 14346, + rcSpanishVenezuela = 8202, + rcSwedish = 1053, + rcThai = 1054, + rcTurkish = 1055, + rcUkrainian = 1058, + rcUrdu = 1056 +}; +enum enumSccEvent { + eProjectInScc, + ePreDirtyNotification +}; +enum favorSizeOrSpeedOption { + favorNone, + favorSpeed, + favorSize +}; +enum floatingPointModel { + floatingPointNotSet = -1, + floatingPointPrecise, + floatingPointStrict, + floatingPointFast +}; +enum genProxyLanguage { + genProxyNative, + genProxyManaged +}; +enum inlineExpansionOption { + expandDisable, + expandOnlyInline, + expandAnySuitable, + expandDefault // Not useful number, but stops the output +}; +enum linkIncrementalType { + linkIncrementalDefault, + linkIncrementalNo, + linkIncrementalYes +}; +enum linkProgressOption { + linkProgressNotSet, + linkProgressAll, + linkProgressLibs +}; +enum machineTypeOption { + machineNotSet, + machineX86, + machineX64 = 17 +}; +enum midlCharOption { + midlCharUnsigned, + midlCharSigned, + midlCharAscii7 +}; +enum midlErrorCheckOption { + midlEnableCustom, + midlDisableAll, + midlEnableAll +}; +enum midlStructMemberAlignOption { + midlAlignNotSet, + midlAlignSingleByte, + midlAlignTwoBytes, + midlAlignFourBytes, + midlAlignEightBytes, + midlAlignSixteenBytes +}; +enum midlTargetEnvironment { + midlTargetNotSet, + midlTargetWin32, + midlTargetWin64 +}; +enum midlWarningLevelOption { + midlWarningLevel_0, + midlWarningLevel_1, + midlWarningLevel_2, + midlWarningLevel_3, + midlWarningLevel_4 +}; +enum optFoldingType { + optFoldingDefault, + optNoFolding, + optFolding +}; +enum optimizeOption { + optimizeDisabled, + optimizeMinSpace, + optimizeMaxSpeed, + optimizeFull, + optimizeCustom, + optimizeDefault // Not useful number, but stops the output +}; +enum optRefType { + optReferencesDefault, + optNoReferences, + optReferences +}; +enum optWin98Type { + optWin98Default, + optWin98No, + optWin98Yes +}; +enum optLinkTimeCodeGenType { + optLTCGDefault, + optLTCGEnabled, + optLTCGInstrument, + optLTCGOptimize, + optLTCGUpdate +}; +enum pchOption { + pchUnset = -1, + pchNone, + pchCreateUsingSpecific, + pchGenerateAuto, + pchUseUsingSpecific +}; +enum preprocessOption { + preprocessUnknown = -1, + preprocessNo, + preprocessYes, + preprocessNoLineNumbers +}; +enum ProcessorOptimizeOption { + procOptimizeBlended, //GB + procOptimizePentium, //G5 + procOptimizePentiumProAndAbove, //G6 + procOptimizePentium4AndAbove //G7 +}; +enum RegisterDeployOption { + registerNo = 0, + registerYes +}; +enum RemoteDebuggerType { + DbgLocal, + DbgRemote, + DbgRemoteTCPIP +}; +enum runtimeLibraryOption { + rtUnknown = -1, + rtMultiThreaded, + rtMultiThreadedDebug, + rtMultiThreadedDLL, + rtMultiThreadedDebugDLL, + rtSingleThreaded, + rtSingleThreadedDebug +}; +enum structMemberAlignOption { + alignNotSet, + alignSingleByte, + alignTwoBytes, + alignFourBytes, + alignEightBytes, + alignSixteenBytes +}; +enum subSystemOption { + subSystemNotSet, + subSystemConsole, + subSystemWindows +}; +enum termSvrAwarenessType { + termSvrAwareDefault, + termSvrAwareNo, + termSvrAwareYes +}; +enum toolSetType { + toolSetUtility, + toolSetMakefile, + toolSetLinker, + toolSetLibrarian, + toolSetAll +}; +enum TypeOfDebugger { + DbgNativeOnly, + DbgManagedOnly, + DbgMixed, + DbgAuto +}; +enum useOfATL { + useATLNotSet, + useATLStatic, + useATLDynamic +}; +enum useOfMfc { + useMfcStdWin, + useMfcStatic, + useMfcDynamic +}; +enum useOfArchitecture { + archUnknown = -1, + archArmv4, + archArmv5, + archArmv4T, + archArmv5T, + archMips1 = 0, + archMips2 = 1, + archMips3 = 2, + archMips4 = 3, + archMips5 = 4, + archMips16 = 5, + archMips32 = 6, + archMips64 = 7 +}; +enum warningLevelOption { + warningLevelUnknown = -1, + warningLevel_0, + warningLevel_1, + warningLevel_2, + warningLevel_3, + warningLevel_4 +}; + + +class VCToolBase { +protected: + // Functions + VCToolBase(){} + virtual ~VCToolBase(){} + virtual bool parseOption(const char* option) = 0; +public: + void parseOptions(QStringList& options) { + for (QStringList::ConstIterator it=options.begin(); (it!=options.end()); it++) + parseOption((*it).toLatin1()); + } + static QStringList fixCommandLine(const QString &input); +}; + +class VCConfiguration; +class VCProject; + +class VCCLCompilerTool : public VCToolBase +{ +public: + // Functions + VCCLCompilerTool(); + virtual ~VCCLCompilerTool(){} + bool parseOption(const char* option); + + // Variables + QStringList AdditionalIncludeDirectories; + QStringList AdditionalOptions; + QStringList AdditionalUsingDirectories; + QString AssemblerListingLocation; + asmListingOption AssemblerOutput; + basicRuntimeCheckOption BasicRuntimeChecks; + browseInfoOption BrowseInformation; + QString BrowseInformationFile; + triState BufferSecurityCheck; + callingConventionOption CallingConvention; + CompileAsOptions CompileAs; + compileAsManagedOptions CompileAsManaged; + triState CompileOnly; + debugOption DebugInformationFormat; + triState DefaultCharIsUnsigned; + triState Detect64BitPortabilityProblems; + triState DisableLanguageExtensions; + QStringList DisableSpecificWarnings; + enhancedInstructionSetOption EnableEnhancedInstructionSet; + triState EnableFiberSafeOptimizations; + triState EnableFunctionLevelLinking; + triState EnableIntrinsicFunctions; + exceptionHandling ExceptionHandling; + triState ExpandAttributedSource; + favorSizeOrSpeedOption FavorSizeOrSpeed; + floatingPointModel FloatingPointModel; + triState FloatingPointExceptions; + triState ForceConformanceInForLoopScope; + QStringList ForcedIncludeFiles; + QStringList ForcedUsingFiles; + preprocessOption GeneratePreprocessedFile; + triState PreprocessSuppressLineNumbers; + triState GlobalOptimizations; + triState IgnoreStandardIncludePath; + triState ImproveFloatingPointConsistency; + inlineExpansionOption InlineFunctionExpansion; + triState KeepComments; + triState MinimalRebuild; + QString ObjectFile; + triState OmitDefaultLibName; + triState OmitFramePointers; + triState OpenMP; + optimizeOption Optimization; + ProcessorOptimizeOption OptimizeForProcessor; + triState OptimizeForWindowsApplication; + QString OutputFile; + QString PrecompiledHeaderFile; + QString PrecompiledHeaderThrough; + QStringList PreprocessorDefinitions; + QString ProgramDataBaseFileName; + runtimeLibraryOption RuntimeLibrary; + triState RuntimeTypeInfo; + triState ShowIncludes; + triState SmallerTypeCheck; + triState StringPooling; + structMemberAlignOption StructMemberAlignment; + triState SuppressStartupBanner; + triState TreatWChar_tAsBuiltInType; + triState TurnOffAssemblyGeneration; + triState UndefineAllPreprocessorDefinitions; + QStringList UndefinePreprocessorDefinitions; + pchOption UsePrecompiledHeader; + triState UseUnicodeForAssemblerListing; + triState WarnAsError; + warningLevelOption WarningLevel; + triState WholeProgramOptimization; + useOfArchitecture CompileForArchitecture; + triState InterworkCalls; + + // VS2010 + triState EnablePREfast; + triState DisplayFullPaths; + triState MultiProcessorCompilation; + QString MultiProcessorCompilationProcessorCount; + triState GenerateXMLDocumentationFiles; + QString XMLDocumentationFileName; + QString ErrorReporting; + triState CreateHotpatchableImage; + QString PreprocessOutputPath; + + VCConfiguration* config; +}; + +class VCLinkerTool : public VCToolBase +{ +public: + // Functions + VCLinkerTool(); + virtual ~VCLinkerTool(){} + bool parseOption(const char* option); + + // Variables + QStringList AdditionalDependencies; + QStringList AdditionalLibraryDirectories; + QStringList AdditionalOptions; + QStringList AddModuleNamesToAssembly; + QString BaseAddress; + triState DataExecutionPrevention; + QStringList DelayLoadDLLs; + optFoldingType EnableCOMDATFolding; + QString EntryPointSymbol; + QStringList ForceSymbolReferences; + QString FunctionOrder; + triState GenerateDebugInformation; + triState GenerateMapFile; + qlonglong HeapCommitSize; + qlonglong HeapReserveSize; + triState IgnoreAllDefaultLibraries; + QStringList IgnoreDefaultLibraryNames; + triState IgnoreEmbeddedIDL; + triState IgnoreImportLibrary; + QString ImportLibrary; + addressAwarenessType LargeAddressAware; + triState LinkDLL; + linkIncrementalType LinkIncremental; + optLinkTimeCodeGenType LinkTimeCodeGeneration; + QString LinkToManagedResourceFile; + triState MapExports; + QString MapFileName; + triState MapLines; + QString MergedIDLBaseFileName; + QString MergeSections; // Should be list? + QString MidlCommandFile; + QString ModuleDefinitionFile; // Should be list? + optWin98Type OptimizeForWindows98; + optRefType OptimizeReferences; + QString OutputFile; + QString ProgramDatabaseFile; + triState RandomizedBaseAddress; + triState RegisterOutput; + triState ResourceOnlyDLL; + triState SetChecksum; + linkProgressOption ShowProgress; + qlonglong StackCommitSize; + qlonglong StackReserveSize; + QString StripPrivateSymbols; // Should be list? + subSystemOption SubSystem; + triState SupportUnloadOfDelayLoadedDLL; + triState SuppressStartupBanner; + triState SwapRunFromCD; + triState SwapRunFromNet; + machineTypeOption TargetMachine; + termSvrAwarenessType TerminalServerAware; + triState TreatWarningsAsErrors; + triState TurnOffAssemblyGeneration; + QString TypeLibraryFile; + qlonglong TypeLibraryResourceID; + QString Version; + + // VS2010 + triState GenerateManifest; + QStringList AdditionalManifestDependencies; + QString ManifestFile; + triState EnableUAC; + QString UACExecutionLevel; + triState UACUIAccess; + qlonglong SectionAlignment; + triState PreventDllBinding; + triState AllowIsolation; + triState AssemblyDebug; + QStringList AssemblyLinkResource; + QString CLRImageType; + QString CLRSupportLastError; + QString CLRThreadAttribute; + triState CLRUnmanagedCodeCheck; + triState DelaySign; + QString KeyContainer; + QString KeyFile; + QString LinkErrorReporting; + + VCConfiguration* config; +}; + +class VCMIDLTool : public VCToolBase +{ +public: + // Functions + VCMIDLTool(); + virtual ~VCMIDLTool(){} + bool parseOption(const char* option); + + // Variables + QStringList AdditionalIncludeDirectories; + QStringList AdditionalOptions; + QStringList CPreprocessOptions; + midlCharOption DefaultCharType; + QString DLLDataFileName; // Should be list? + midlErrorCheckOption EnableErrorChecks; + triState ErrorCheckAllocations; + triState ErrorCheckBounds; + triState ErrorCheckEnumRange; + triState ErrorCheckRefPointers; + triState ErrorCheckStubData; + QStringList FullIncludePath; + triState GenerateStublessProxies; + triState GenerateTypeLibrary; + QString HeaderFileName; + triState IgnoreStandardIncludePath; + QString InterfaceIdentifierFileName; + triState MkTypLibCompatible; + QString OutputDirectory; + QStringList PreprocessorDefinitions; + QString ProxyFileName; + QString RedirectOutputAndErrors; + midlStructMemberAlignOption StructMemberAlignment; + triState SuppressStartupBanner; + midlTargetEnvironment TargetEnvironment; + QString TypeLibraryName; + QStringList UndefinePreprocessorDefinitions; + triState ValidateParameters; + triState WarnAsError; + midlWarningLevelOption WarningLevel; + + // VS 2010 + triState ApplicationConfigurationMode; + QString GenerateClientFiles; + QString ClientStubFile; + QString TypeLibFormat; + triState ValidateAllParameters; + triState SuppressCompilerWarnings; + QString GenerateServerFiles; + QString ServerStubFile; + qlonglong LocaleID; + + VCConfiguration* config; +}; + +class VCLibrarianTool : public VCToolBase +{ +public: + // Functions + VCLibrarianTool(); + virtual ~VCLibrarianTool(){} + bool parseOption(const char*){ return false; } + + // Variables + QStringList AdditionalDependencies; + QStringList AdditionalLibraryDirectories; + QStringList AdditionalOptions; + QStringList ExportNamedFunctions; + QStringList ForceSymbolReferences; + triState IgnoreAllDefaultLibraries; + QStringList IgnoreDefaultLibraryNames; + QString ModuleDefinitionFile; + QString OutputFile; + triState SuppressStartupBanner; +}; + +class VCCustomBuildTool : public VCToolBase +{ +public: + // Functions + VCCustomBuildTool(); + virtual ~VCCustomBuildTool(){} + bool parseOption(const char*){ return false; } + + // Variables + QStringList AdditionalDependencies; + QStringList CommandLine; + QString Description; + QStringList Outputs; + QString ToolName; + QString ToolPath; + + VCConfiguration* config; +}; + +class VCResourceCompilerTool : public VCToolBase +{ +public: + // Functions + VCResourceCompilerTool(); + virtual ~VCResourceCompilerTool(){} + bool parseOption(const char*){ return false; } + + // Variables + QStringList AdditionalIncludeDirectories; + QStringList AdditionalOptions; + enumResourceLangID Culture; + QStringList FullIncludePath; + triState IgnoreStandardIncludePath; + QStringList PreprocessorDefinitions; + QString ResourceOutputFileName; + linkProgressOption ShowProgress; + QString ToolPath; + triState SuppressStartupBanner; +}; + +class VCDeploymentTool +{ +public: + // Functions + VCDeploymentTool(); + virtual ~VCDeploymentTool() {} + + // Variables + QString DeploymentTag; + QString RemoteDirectory; + RegisterDeployOption RegisterOutput; + QString AdditionalFiles; +}; + +class VCEventTool : public VCToolBase +{ +protected: + // Functions + VCEventTool(const QString &eventName); + virtual ~VCEventTool(){} + bool parseOption(const char*){ return false; } + +public: + // Variables + QStringList CommandLine; + QString Description; + triState ExcludedFromBuild; + QString EventName; + QString ToolName; + QString ToolPath; +}; + +class VCPostBuildEventTool : public VCEventTool +{ +public: + VCPostBuildEventTool(); + ~VCPostBuildEventTool(){} +}; + +class VCPreBuildEventTool : public VCEventTool +{ +public: + VCPreBuildEventTool(); + ~VCPreBuildEventTool(){} +}; + +class VCPreLinkEventTool : public VCEventTool +{ +public: + VCPreLinkEventTool(); + ~VCPreLinkEventTool(){} +}; + +class VCConfiguration +{ +public: + // Functions + VCConfiguration(); + ~VCConfiguration(){} + + DotNET CompilerVersion; + + // Variables + triState ATLMinimizesCRunTimeLibraryUsage; + triState BuildBrowserInformation; + charSet CharacterSet; + ConfigurationTypes ConfigurationType; + QString DeleteExtensionsOnClean; + QString ImportLibrary; + QString IntermediateDirectory; + QString Name; // "ConfigurationName|PlatformName" + QString ConfigurationName; + QString OutputDirectory; + QString PrimaryOutput; + QString ProgramDatabase; + triState RegisterOutput; + useOfATL UseOfATL; + useOfMfc UseOfMfc; + triState WholeProgramOptimization; + + // XML sub-parts + VCCLCompilerTool compiler; + VCLinkerTool linker; + VCLibrarianTool librarian; + VCCustomBuildTool custom; + VCMIDLTool idl; + VCPostBuildEventTool postBuild; + VCPreBuildEventTool preBuild; + VCDeploymentTool deployment; + VCPreLinkEventTool preLink; + VCResourceCompilerTool resource; +}; + +struct VCFilterFile +{ + VCFilterFile() + { excludeFromBuild = false; } + VCFilterFile(const QString &filename, bool exclude = false ) + { file = filename; excludeFromBuild = exclude; } + VCFilterFile(const QString &filename, const QString &additional, bool exclude = false ) + { file = filename; excludeFromBuild = exclude; additionalFile = additional; } + bool operator==(const VCFilterFile &other){ + return file == other.file + && additionalFile == other.additionalFile + && excludeFromBuild == other.excludeFromBuild; + } + + bool excludeFromBuild; + QString file; + QString additionalFile; // For tools like MOC +}; + +#ifndef QT_NO_DEBUG_OUTPUT +inline QDebug operator<<(QDebug dbg, const VCFilterFile &p) +{ + dbg.nospace() << "VCFilterFile(file(" << p.file + << ") additionalFile(" << p.additionalFile + << ") excludeFromBuild(" << p.excludeFromBuild << "))" << endl; + return dbg.space(); +} +#endif + +class VcprojGenerator; +class VCFilter +{ +public: + // Functions + VCFilter(); + ~VCFilter(){} + + void addFile(const QString& filename); + void addFile(const VCFilterFile& fileInfo); + void addFiles(const QStringList& fileList); + bool addExtraCompiler(const VCFilterFile &info); + void modifyPCHstage(QString str); + + // Variables + QString Name; + QString Filter; + QString Guid; + triState ParseFiles; + VcprojGenerator* Project; + VCConfiguration* Config; + QList Files; + + customBuildCheck CustomBuild; + + bool useCustomBuildTool; + VCCustomBuildTool CustomBuildTool; + + bool useCompilerTool; + VCCLCompilerTool CompilerTool; +}; + +typedef QList VCFilterList; +class VCProjectSingleConfig +{ +public: + enum FilterTypes { + None, + Source, + Header, + Generated, + LexYacc, + Translation, + Resources, + Extras + }; + // Functions + VCProjectSingleConfig(){} + ~VCProjectSingleConfig(){} + + // Variables + QString Name; + QString Version; + QString ProjectGUID; + QString Keyword; + QString SccProjectName; + QString SccLocalPath; + QString PlatformName; + + // XML sub-parts + VCConfiguration Configuration; + VCFilter RootFiles; + VCFilter SourceFiles; + VCFilter HeaderFiles; + VCFilter GeneratedFiles; + VCFilter LexYaccFiles; + VCFilter TranslationFiles; + VCFilter FormFiles; + VCFilter ResourceFiles; + VCFilterList ExtraCompilersFiles; + + bool flat_files; + + // Accessor for extracompilers + VCFilter &filterForExtraCompiler(const QString &compilerName); +}; + +// Tree & Flat view of files -------------------------------------------------- +class VCFilter; +class Node +{ +public: + virtual ~Node() { } + 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, const QString &tagName, VCProject &tool, const QString &filter) = 0; + virtual bool hasElements() = 0; +}; + +class TreeNode : public Node +{ + typedef QMap ChildrenMap; + VCFilterFile info; + ChildrenMap children; + +public: + virtual ~TreeNode() { 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){ + QString newNodeName(filepath); + + int index = pathIndex(filepath); + if (index != -1) + newNodeName = filepath.left(index); + + TreeNode *n = children.value(newNodeName); + if (!n) { + n = new TreeNode; + n->info = allInfo; + children.insert(newNodeName, n); + } + if (index != -1) + n->addElement(filepath.mid(index+1), allInfo); + } + + void removeElements() { + 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, const QString &tagName, VCProject &tool, const QString &filter); + bool hasElements() { + return children.size() != 0; + } +}; + +class FlatNode : public Node +{ + typedef QMap ChildrenMapFlat; + ChildrenMapFlat children; + +public: + virtual ~FlatNode() { 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){ + 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() { + children.clear(); + } + + void generateXML(XmlOutput &xml, const QString &tagName, VCProject &proj, const QString &filter); + bool hasElements() { + return children.size() != 0; + } +}; +// ---------------------------------------------------------------------------- + +class VCProject +{ +public: + // Variables + QString Name; + QString Version; + QString ProjectGUID; + QString Keyword; + QString SccProjectName; + QString SccLocalPath; + QString PlatformName; + + // Single projects + QList SingleProjects; + + // List of all extracompilers + QStringList ExtraCompilers; +}; + +class VCProjectWriter +{ +public: + virtual ~VCProjectWriter() {} + + virtual void write(XmlOutput &, VCProjectSingleConfig &); + virtual void write(XmlOutput &, VCProject &); + + virtual void write(XmlOutput &, const VCCLCompilerTool &); + virtual void write(XmlOutput &, const VCLinkerTool &); + virtual void write(XmlOutput &, const VCMIDLTool &); + virtual void write(XmlOutput &, const VCCustomBuildTool &); + virtual void write(XmlOutput &, const VCLibrarianTool &); + virtual void write(XmlOutput &, const VCResourceCompilerTool &); + virtual void write(XmlOutput &, const VCEventTool &); + virtual void write(XmlOutput &, const VCDeploymentTool &); + virtual void write(XmlOutput &, const VCConfiguration &); + virtual void write(XmlOutput &, VCFilter &); + +private: + static void outputFilter(VCProject &project, XmlOutput &xml, const QString &filtername); + static void outputFileConfigs(VCProject &project, XmlOutput &xml, const VCFilterFile &info, const QString &filtername); + static void outputFileConfig(VCFilter &filter, XmlOutput &xml, const QString &filename); + + friend class TreeNode; + friend class FlatNode; +}; + +QT_END_NAMESPACE + +#endif // MSVC_OBJECTMODEL_H diff --git a/qmake/generators/win32/msvc_vcproj.cpp b/qmake/generators/win32/msvc_vcproj.cpp new file mode 100644 index 0000000000..56f3bfdb94 --- /dev/null +++ b/qmake/generators/win32/msvc_vcproj.cpp @@ -0,0 +1,1569 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "msvc_vcproj.h" +#include "option.h" +#include "xmloutput.h" +#include +#include +#include +#include +#include +#include +#include + +//#define DEBUG_SOLUTION_GEN + +QT_BEGIN_NAMESPACE +// Filter GUIDs (Do NOT change these!) ------------------------------ +const char _GUIDSourceFiles[] = "{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"; +const char _GUIDHeaderFiles[] = "{93995380-89BD-4b04-88EB-625FBE52EBFB}"; +const char _GUIDGeneratedFiles[] = "{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}"; +const char _GUIDResourceFiles[] = "{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}"; +const char _GUIDLexYaccFiles[] = "{E12AE0D2-192F-4d59-BD23-7D3FA58D3183}"; +const char _GUIDTranslationFiles[] = "{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}"; +const char _GUIDFormFiles[] = "{99349809-55BA-4b9d-BF79-8FDBB0286EB3}"; +const char _GUIDExtraCompilerFiles[] = "{E0D8C965-CC5F-43d7-AD63-FAEF0BBC0F85}"; +QT_END_NAMESPACE + +#ifdef Q_OS_WIN32 +#include +#include + +QT_BEGIN_NAMESPACE + +struct { + DotNET version; + const char *versionStr; + const char *regKey; +} dotNetCombo[] = { +#ifdef Q_OS_WIN64 + {NET2010, "MSVC.NET 2010 (10.0)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\10.0\\Setup\\VC\\ProductDir"}, + {NET2010, "MSVC.NET 2010 Express Edition (10.0)", "Software\\Wow6432Node\\Microsoft\\VCExpress\\10.0\\Setup\\VC\\ProductDir"}, + {NET2008, "MSVC.NET 2008 (9.0)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\9.0\\Setup\\VC\\ProductDir"}, + {NET2008, "MSVC.NET 2008 Express Edition (9.0)", "Software\\Wow6432Node\\Microsoft\\VCExpress\\9.0\\Setup\\VC\\ProductDir"}, + {NET2005, "MSVC.NET 2005 (8.0)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir"}, + {NET2005, "MSVC.NET 2005 Express Edition (8.0)", "Software\\Wow6432Node\\Microsoft\\VCExpress\\8.0\\Setup\\VC\\ProductDir"}, + {NET2003, "MSVC.NET 2003 (7.1)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\7.1\\Setup\\VC\\ProductDir"}, + {NET2002, "MSVC.NET 2002 (7.0)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\7.0\\Setup\\VC\\ProductDir"}, +#else + {NET2010, "MSVC.NET 2010 (10.0)", "Software\\Microsoft\\VisualStudio\\10.0\\Setup\\VC\\ProductDir"}, + {NET2010, "MSVC.NET 2010 Express Edition (10.0)", "Software\\Microsoft\\VCExpress\\10.0\\Setup\\VC\\ProductDir"}, + {NET2008, "MSVC.NET 2008 (9.0)", "Software\\Microsoft\\VisualStudio\\9.0\\Setup\\VC\\ProductDir"}, + {NET2008, "MSVC.NET 2008 Express Edition (9.0)", "Software\\Microsoft\\VCExpress\\9.0\\Setup\\VC\\ProductDir"}, + {NET2005, "MSVC.NET 2005 (8.0)", "Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir"}, + {NET2005, "MSVC.NET 2005 Express Edition (8.0)", "Software\\Microsoft\\VCExpress\\8.0\\Setup\\VC\\ProductDir"}, + {NET2003, "MSVC.NET 2003 (7.1)", "Software\\Microsoft\\VisualStudio\\7.1\\Setup\\VC\\ProductDir"}, + {NET2002, "MSVC.NET 2002 (7.0)", "Software\\Microsoft\\VisualStudio\\7.0\\Setup\\VC\\ProductDir"}, +#endif + {NETUnknown, "", ""}, +}; + +QT_END_NAMESPACE +#endif + +QT_BEGIN_NAMESPACE +DotNET which_dotnet_version() +{ +#ifndef Q_OS_WIN32 + return NET2002; // Always generate 7.0 versions on other platforms +#else + // Only search for the version once + static DotNET current_version = NETUnknown; + if(current_version != NETUnknown) + return current_version; + + // Fallback to .NET 2002 + current_version = NET2002; + + QStringList warnPath; + int installed = 0; + int i = 0; + for(; dotNetCombo[i].version; ++i) { + QString path = qt_readRegistryKey(HKEY_LOCAL_MACHINE, dotNetCombo[i].regKey); + if(!path.isEmpty()) { + ++installed; + current_version = dotNetCombo[i].version; + warnPath += QString("%1").arg(dotNetCombo[i].versionStr); + } + } + + if (installed < 2) + return current_version; + + // More than one version installed, search directory path + QString paths = qgetenv("PATH"); + QStringList pathlist = paths.toLower().split(";"); + + i = installed = 0; + for(; dotNetCombo[i].version; ++i) { + QString productPath = qt_readRegistryKey(HKEY_LOCAL_MACHINE, dotNetCombo[i].regKey).toLower(); + if (productPath.isEmpty()) + continue; + QStringList::iterator it; + for(it = pathlist.begin(); it != pathlist.end(); ++it) { + if((*it).contains(productPath)) { + ++installed; + current_version = dotNetCombo[i].version; + warnPath += QString("%1 in path").arg(dotNetCombo[i].versionStr); + break; + } + } + } + switch(installed) { + case 1: + break; + case 0: + warn_msg(WarnLogic, "Generator: MSVC.NET: Found more than one version of Visual Studio, but" + " none in your path! Fallback to lowest version (%s)", warnPath.join(", ").toLatin1().data()); + break; + default: + warn_msg(WarnLogic, "Generator: MSVC.NET: Found more than one version of Visual Studio in" + " your path! Fallback to lowest version (%s)", warnPath.join(", ").toLatin1().data()); + break; + } + + return current_version; +#endif +}; + +// Flatfile Tags ---------------------------------------------------- +const char _slnHeader70[] = "Microsoft Visual Studio Solution File, Format Version 7.00"; +const char _slnHeader71[] = "Microsoft Visual Studio Solution File, Format Version 8.00"; +const char _slnHeader80[] = "Microsoft Visual Studio Solution File, Format Version 9.00" + "\n# Visual Studio 2005"; +const char _slnHeader90[] = "Microsoft Visual Studio Solution File, Format Version 10.00" + "\n# Visual Studio 2008"; +const char _slnHeader100[] = "Microsoft Visual Studio Solution File, Format Version 11.00" + "\n# Visual Studio 2010"; + // The following UUID _may_ change for later servicepacks... + // If so we need to search through the registry at + // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.0\Projects + // to find the subkey that contains a "PossibleProjectExtension" + // containing "vcproj"... + // Use the hardcoded value for now so projects generated on other + // platforms are actually usable. +const char _slnMSVCvcprojGUID[] = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"; +const char _slnProjectBeg[] = "\nProject(\""; +const char _slnProjectMid[] = "\") = "; +const char _slnProjectEnd[] = "\nEndProject"; +const char _slnGlobalBeg[] = "\nGlobal"; +const char _slnGlobalEnd[] = "\nEndGlobal"; +const char _slnSolutionConf[] = "\n\tGlobalSection(SolutionConfiguration) = preSolution" + "\n\t\tConfigName.0 = Debug|Win32" + "\n\t\tConfigName.1 = Release|Win32" + "\n\tEndGlobalSection"; +const char _slnProjDepBeg[] = "\n\tGlobalSection(ProjectDependencies) = postSolution"; +const char _slnProjDepEnd[] = "\n\tEndGlobalSection"; +const char _slnProjConfBeg[] = "\n\tGlobalSection(ProjectConfiguration) = postSolution"; +const char _slnProjRelConfTag1[]= ".Release|%1.ActiveCfg = Release|"; +const char _slnProjRelConfTag2[]= ".Release|%1.Build.0 = Release|"; +const char _slnProjDbgConfTag1[]= ".Debug|%1.ActiveCfg = Debug|"; +const char _slnProjDbgConfTag2[]= ".Debug|%1.Build.0 = Debug|"; +const char _slnProjConfEnd[] = "\n\tEndGlobalSection"; +const char _slnExtSections[] = "\n\tGlobalSection(ExtensibilityGlobals) = postSolution" + "\n\tEndGlobalSection" + "\n\tGlobalSection(ExtensibilityAddIns) = postSolution" + "\n\tEndGlobalSection"; +// ------------------------------------------------------------------ + +VcprojGenerator::VcprojGenerator() + : Win32MakefileGenerator(), + init_flag(false), + projectWriter(0) +{ +} + +VcprojGenerator::~VcprojGenerator() +{ + delete projectWriter; +} + +bool VcprojGenerator::writeMakefile(QTextStream &t) +{ + initProject(); // Fills the whole project with proper data + + // Generate solution file + if(project->first("TEMPLATE") == "vcsubdirs") { + if (!project->isActiveConfig("build_pass")) { + debug_msg(1, "Generator: MSVC.NET: Writing solution file"); + writeSubDirs(t); + } else { + debug_msg(1, "Generator: MSVC.NET: Not writing solution file for build_pass configs"); + } + return true; + } else + // Generate single configuration project file + if (project->first("TEMPLATE") == "vcapp" || + project->first("TEMPLATE") == "vclib") { + if(!project->isActiveConfig("build_pass")) { + debug_msg(1, "Generator: MSVC.NET: Writing single configuration project file"); + XmlOutput xmlOut(t); + projectWriter->write(xmlOut, vcProject); + } + return true; + } + return project->isActiveConfig("build_pass"); +} + +bool VcprojGenerator::writeProjectMakefile() +{ + usePlatformDir(); + QTextStream t(&Option::output); + + // Check if all requirements are fulfilled + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n", + var("QMAKE_FAILED_REQUIREMENTS").toLatin1().constData()); + return true; + } + + // Generate project file + if(project->first("TEMPLATE") == "vcapp" || + project->first("TEMPLATE") == "vclib") { + if (!mergedProjects.count()) { + warn_msg(WarnLogic, "Generator: MSVC.NET: no single configuration created, cannot output project!"); + return false; + } + + debug_msg(1, "Generator: MSVC.NET: Writing project file"); + VCProject mergedProject; + for (int i = 0; i < mergedProjects.count(); ++i) { + VCProjectSingleConfig *singleProject = &(mergedProjects.at(i)->vcProject); + mergedProject.SingleProjects += *singleProject; + for (int j = 0; j < singleProject->ExtraCompilersFiles.count(); ++j) { + const QString &compilerName = singleProject->ExtraCompilersFiles.at(j).Name; + if (!mergedProject.ExtraCompilers.contains(compilerName)) + mergedProject.ExtraCompilers += compilerName; + } + } + + if(mergedProjects.count() > 1 && + mergedProjects.at(0)->vcProject.Name == + mergedProjects.at(1)->vcProject.Name) + mergedProjects.at(0)->writePrlFile(); + mergedProject.Name = project->first("QMAKE_PROJECT_NAME"); + mergedProject.Version = mergedProjects.at(0)->vcProject.Version; + mergedProject.ProjectGUID = project->isEmpty("QMAKE_UUID") ? getProjectUUID().toString().toUpper() : project->first("QMAKE_UUID"); + mergedProject.Keyword = project->first("VCPROJ_KEYWORD"); + mergedProject.SccProjectName = mergedProjects.at(0)->vcProject.SccProjectName; + mergedProject.SccLocalPath = mergedProjects.at(0)->vcProject.SccLocalPath; + mergedProject.PlatformName = mergedProjects.at(0)->vcProject.PlatformName; + + XmlOutput xmlOut(t); + projectWriter->write(xmlOut, mergedProject); + return true; + } else if(project->first("TEMPLATE") == "vcsubdirs") { + return writeMakefile(t); + } + return false; +} + +struct VcsolutionDepend { + QString uuid; + QString vcprojFile, orig_target, target; + Target targetType; + QStringList dependencies; +}; + +QUuid VcprojGenerator::getProjectUUID(const QString &filename) +{ + bool validUUID = true; + + // Read GUID from variable-space + QUuid uuid = project->first("GUID"); + + // If none, create one based on the MD5 of absolute project path + if(uuid.isNull() || !filename.isEmpty()) { + QString abspath = Option::fixPathToLocalOS(filename.isEmpty()?project->first("QMAKE_MAKEFILE"):filename); + QByteArray digest = QCryptographicHash::hash(abspath.toUtf8(), QCryptographicHash::Md5); + memcpy((unsigned char*)(&uuid), digest.constData(), sizeof(QUuid)); + validUUID = !uuid.isNull(); + uuid.data4[0] = (uuid.data4[0] & 0x3F) | 0x80; // UV_DCE variant + uuid.data3 = (uuid.data3 & 0x0FFF) | (QUuid::Name<<12); + } + + // If still not valid, generate new one, and suggest adding to .pro + if(uuid.isNull() || !validUUID) { + uuid = QUuid::createUuid(); + fprintf(stderr, + "qmake couldn't create a GUID based on filepath, and we couldn't\nfind a valid GUID in the .pro file (Consider adding\n'GUID = %s' to the .pro file)\n", + uuid.toString().toUpper().toLatin1().constData()); + } + + // Store GUID in variable-space + project->values("GUID") = QStringList(uuid.toString().toUpper()); + return uuid; +} + +QUuid VcprojGenerator::increaseUUID(const QUuid &id) +{ + QUuid result(id); + qint64 dataFirst = (result.data4[0] << 24) + + (result.data4[1] << 16) + + (result.data4[2] << 8) + + result.data4[3]; + qint64 dataLast = (result.data4[4] << 24) + + (result.data4[5] << 16) + + (result.data4[6] << 8) + + result.data4[7]; + + if(!(dataLast++)) + dataFirst++; + + result.data4[0] = uchar((dataFirst >> 24) & 0xff); + result.data4[1] = uchar((dataFirst >> 16) & 0xff); + result.data4[2] = uchar((dataFirst >> 8) & 0xff); + result.data4[3] = uchar(dataFirst & 0xff); + result.data4[4] = uchar((dataLast >> 24) & 0xff); + result.data4[5] = uchar((dataLast >> 16) & 0xff); + result.data4[6] = uchar((dataLast >> 8) & 0xff); + result.data4[7] = uchar(dataLast & 0xff); + return result; +} + +QStringList VcprojGenerator::collectSubDirs(QMakeProject *proj) +{ + QStringList subdirs; + QStringList tmp_proj_subdirs = proj->variables()["SUBDIRS"]; + for(int x = 0; x < tmp_proj_subdirs.size(); ++x) { + QString tmpdir = tmp_proj_subdirs.at(x); + if(!proj->isEmpty(tmpdir + ".file")) { + if(!proj->isEmpty(tmpdir + ".subdir")) + warn_msg(WarnLogic, "Cannot assign both file and subdir for subdir %s", + tmpdir.toLatin1().constData()); + tmpdir = proj->first(tmpdir + ".file"); + } else if(!proj->isEmpty(tmpdir + ".subdir")) { + tmpdir = proj->first(tmpdir + ".subdir"); + } + subdirs += tmpdir; + } + return subdirs; +} + +void VcprojGenerator::writeSubDirs(QTextStream &t) +{ + // Check if all requirements are fulfilled + if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { + fprintf(stderr, "Project file not generated because all requirements not met:\n\t%s\n", + var("QMAKE_FAILED_REQUIREMENTS").toLatin1().constData()); + return; + } + + switch(which_dotnet_version()) { + case NET2010: + t << _slnHeader100; + break; + case NET2008: + t << _slnHeader90; + break; + case NET2005: + t << _slnHeader80; + break; + case NET2003: + t << _slnHeader71; + break; + case NET2002: + t << _slnHeader70; + break; + default: + t << _slnHeader70; + warn_msg(WarnLogic, "Generator: MSVC.NET: Unknown version (%d) of MSVC detected for .sln", which_dotnet_version()); + break; + } + + QHash solution_depends; + QList solution_cleanup; + + QString oldpwd = qmake_getpwd(); + + // Make sure that all temp projects are configured + // for release so that the depends are created + // without the debug dxxx.lib name mangling + QStringList old_after_vars = Option::after_user_vars; + Option::after_user_vars.append("CONFIG+=release"); + + QStringList subdirs = collectSubDirs(project); + for(int i = 0; i < subdirs.size(); ++i) { + QString tmp = subdirs.at(i); + QFileInfo fi(fileInfo(Option::fixPathToLocalOS(tmp, true))); + if(fi.exists()) { + if(fi.isDir()) { + QString profile = tmp; + if(!profile.endsWith(Option::dir_sep)) + profile += Option::dir_sep; + profile += fi.baseName() + Option::pro_ext; + subdirs.append(profile); + } else { + QMakeProject tmp_proj; + QString dir = fi.path(), fn = fi.fileName(); + if(!dir.isEmpty()) { + if(!qmake_setpwd(dir)) + fprintf(stderr, "Cannot find directory: %s\n", dir.toLatin1().constData()); + } + if(tmp_proj.read(fn)) { + // Check if all requirements are fulfilled + if(!tmp_proj.variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) { + fprintf(stderr, "Project file(%s) not added to Solution because all requirements not met:\n\t%s\n", + fn.toLatin1().constData(), tmp_proj.values("QMAKE_FAILED_REQUIREMENTS").join(" ").toLatin1().constData()); + continue; + } + if(tmp_proj.first("TEMPLATE") == "vcsubdirs") { + foreach(const QString &tmpdir, collectSubDirs(&tmp_proj)) + subdirs += fileFixify(tmpdir); + } else if(tmp_proj.first("TEMPLATE") == "vcapp" || tmp_proj.first("TEMPLATE") == "vclib") { + // Initialize a 'fake' project to get the correct variables + // and to be able to extract all the dependencies + Option::QMAKE_MODE old_mode = Option::qmake_mode; + Option::qmake_mode = Option::QMAKE_GENERATE_NOTHING; + VcprojGenerator tmp_vcproj; + tmp_vcproj.setNoIO(true); + tmp_vcproj.setProjectFile(&tmp_proj); + Option::qmake_mode = old_mode; + if(Option::debug_level) { + debug_msg(1, "Dumping all variables:"); + QMap &vars = tmp_proj.variables(); + for(QMap::Iterator it = vars.begin(); + it != vars.end(); ++it) { + if(it.key().left(1) != "." && !it.value().isEmpty()) + debug_msg(1, "%s: %s === %s", fn.toLatin1().constData(), it.key().toLatin1().constData(), + it.value().join(" :: ").toLatin1().constData()); + } + } + + // We assume project filename is [QMAKE_PROJECT_NAME].vcproj + QString vcproj = unescapeFilePath(tmp_vcproj.project->first("QMAKE_PROJECT_NAME") + project->first("VCPROJ_EXTENSION")); + QString vcprojDir = qmake_getpwd(); + + // If file doesn't exsist, then maybe the users configuration + // doesn't allow it to be created. Skip to next... + if(!exists(vcprojDir + Option::dir_sep + vcproj)) { + + // Try to find the directory which fits relative + // to the output path, which represents the shadow + // path in case we are shadow building + QStringList list = fi.path().split(QLatin1Char('/')); + QString tmpDir = QFileInfo(Option::output).path() + Option::dir_sep; + bool found = false; + for (int i = list.size() - 1; i >= 0; --i) { + QString curr; + for (int j = i; j < list.size(); ++j) + curr += list.at(j) + Option::dir_sep; + if (exists(tmpDir + curr + vcproj)) { + vcprojDir = QDir::cleanPath(tmpDir + curr); + found = true; + break; + } + } + if (!found) { + warn_msg(WarnLogic, "Ignored (not found) '%s'", QString(vcprojDir + Option::dir_sep + vcproj).toLatin1().constData()); + goto nextfile; // # Dirty! + } + } + + VcsolutionDepend *newDep = new VcsolutionDepend; + newDep->vcprojFile = vcprojDir + Option::dir_sep + vcproj; + newDep->orig_target = unescapeFilePath(tmp_proj.first("QMAKE_ORIG_TARGET")); + newDep->target = tmp_proj.first("MSVCPROJ_TARGET").section(Option::dir_sep, -1); + newDep->targetType = tmp_vcproj.projectTarget; + newDep->uuid = tmp_proj.isEmpty("QMAKE_UUID") ? getProjectUUID(Option::fixPathToLocalOS(vcprojDir + QDir::separator() + vcproj)).toString().toUpper(): tmp_proj.first("QMAKE_UUID"); + + // We want to store it as the .lib name. + if(newDep->target.endsWith(".dll")) + newDep->target = newDep->target.left(newDep->target.length()-3) + "lib"; + + // All ActiveQt Server projects are dependent on idc.exe + if(tmp_proj.variables()["CONFIG"].contains("qaxserver")) + newDep->dependencies << "idc.exe"; + + // All extra compilers which has valid input are considered dependencies + const QStringList &quc = tmp_proj.variables()["QMAKE_EXTRA_COMPILERS"]; + for(QStringList::ConstIterator it = quc.constBegin(); it != quc.constEnd(); ++it) { + const QStringList &invar = tmp_proj.variables().value((*it) + ".input"); + for(QStringList::ConstIterator iit = invar.constBegin(); iit != invar.constEnd(); ++iit) { + const QStringList fileList = tmp_proj.variables().value(*iit); + if (!fileList.isEmpty()) { + const QStringList &cmdsParts = tmp_proj.variables().value((*it) + ".commands"); + bool startOfLine = true; + foreach(QString cmd, cmdsParts) { + if (!startOfLine) { + if (cmd.contains("\r")) + startOfLine = true; + continue; + } + if (cmd.isEmpty()) + continue; + + startOfLine = false; + // Extra compiler commands might be defined in variables, so + // expand them (don't care about the in/out files) + cmd = tmp_vcproj.replaceExtraCompilerVariables(cmd, QStringList(), QStringList()); + // Pull out command based on spaces and quoting, if the + // command starts with that + cmd = cmd.left(cmd.indexOf(cmd.at(0) == '"' ? '"' : ' ', 1)); + QString dep = cmd.section('/', -1).section('\\', -1); + if (!newDep->dependencies.contains(dep)) + newDep->dependencies << dep; + } + } + } + } + + // Add all unknown libs to the deps + QStringList where = QStringList() << "QMAKE_LIBS" << "QMAKE_LIBS_PRIVATE"; + if(!tmp_proj.isEmpty("QMAKE_INTERNAL_PRL_LIBS")) + where = tmp_proj.variables()["QMAKE_INTERNAL_PRL_LIBS"]; + for(QStringList::iterator wit = where.begin(); + wit != where.end(); ++wit) { + QStringList &l = tmp_proj.variables()[(*wit)]; + for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + QString opt = (*it); + if(!opt.startsWith("/") && // Not a switch + opt != newDep->target && // Not self + opt != "opengl32.lib" && // We don't care about these libs + opt != "glu32.lib" && // to make depgen alittle faster + opt != "kernel32.lib" && + opt != "user32.lib" && + opt != "gdi32.lib" && + opt != "comdlg32.lib" && + opt != "advapi32.lib" && + opt != "shell32.lib" && + opt != "ole32.lib" && + opt != "oleaut32.lib" && + opt != "uuid.lib" && + opt != "imm32.lib" && + opt != "winmm.lib" && + opt != "wsock32.lib" && + opt != "ws2_32.lib" && + opt != "winspool.lib" && + opt != "delayimp.lib") + { + newDep->dependencies << opt.section(Option::dir_sep, -1); + } + } + } +#ifdef DEBUG_SOLUTION_GEN + qDebug("Deps for %20s: [%s]", newDep->target.toLatin1().constData(), newDep->dependencies.join(" :: ").toLatin1().constData()); +#endif + solution_cleanup.append(newDep); + solution_depends.insert(newDep->target, newDep); + t << _slnProjectBeg << _slnMSVCvcprojGUID << _slnProjectMid + << "\"" << newDep->orig_target << "\", \"" << newDep->vcprojFile + << "\", \"" << newDep->uuid << "\""; + t << _slnProjectEnd; + } + } +nextfile: + qmake_setpwd(oldpwd); + } + } + } + t << _slnGlobalBeg; + if (!project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH")) { + QString slnConfCE = _slnSolutionConf; + QString platform = QString("|") + project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; + slnConfCE.replace(QString("|Win32"), platform); + t << slnConfCE; + } else { + t << _slnSolutionConf; + } + t << _slnProjDepBeg; + + // Restore previous after_user_var options + Option::after_user_vars = old_after_vars; + + // Figure out dependencies + for(QList::Iterator it = solution_cleanup.begin(); it != solution_cleanup.end(); ++it) { + int cnt = 0; + for(QStringList::iterator dit = (*it)->dependencies.begin(); dit != (*it)->dependencies.end(); ++dit) { + if(VcsolutionDepend *vc = solution_depends[*dit]) + t << "\n\t\t" << (*it)->uuid << "." << cnt++ << " = " << vc->uuid; + } + } + t << _slnProjDepEnd; + t << _slnProjConfBeg; + for(QList::Iterator it = solution_cleanup.begin(); it != solution_cleanup.end(); ++it) { + QString platform = "Win32"; + if (!project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH")) + platform = project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjDbgConfTag1).arg(platform) << platform; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjDbgConfTag2).arg(platform) << platform; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjRelConfTag1).arg(platform) << platform; + t << "\n\t\t" << (*it)->uuid << QString(_slnProjRelConfTag2).arg(platform) << platform; + } + t << _slnProjConfEnd; + t << _slnExtSections; + t << _slnGlobalEnd; + + + while (!solution_cleanup.isEmpty()) + delete solution_cleanup.takeFirst(); +} + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ + +bool VcprojGenerator::hasBuiltinCompiler(const QString &file) +{ + // Source files + for (int i = 0; i < Option::cpp_ext.count(); ++i) + if (file.endsWith(Option::cpp_ext.at(i))) + return true; + for (int i = 0; i < Option::c_ext.count(); ++i) + if (file.endsWith(Option::c_ext.at(i))) + return true; + if (file.endsWith(".rc") + || file.endsWith(".idl")) + return true; + return false; +} + +void VcprojGenerator::init() +{ + if (init_flag) + return; + init_flag = true; + projectWriter = createProjectWriter(); + + if(project->first("TEMPLATE") == "vcsubdirs") //too much work for subdirs + return; + + debug_msg(1, "Generator: MSVC.NET: Initializing variables"); + + // this should probably not be here, but I'm using it to wrap the .t files + if (project->first("TEMPLATE") == "vcapp") + project->values("QMAKE_APP_FLAG").append("1"); + else if (project->first("TEMPLATE") == "vclib") + project->values("QMAKE_LIB_FLAG").append("1"); + if (project->values("QMAKESPEC").isEmpty()) + project->values("QMAKESPEC").append(qgetenv("QMAKESPEC")); + + processVars(); + + project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS")); + project->values("QMAKE_LIBS_PRIVATE") += escapeFilePaths(project->values("LIBS_PRIVATE")); + + if(!project->values("VERSION").isEmpty()) { + QString version = project->values("VERSION")[0]; + int firstDot = version.indexOf("."); + QString major = version.left(firstDot); + QString minor = version.right(version.length() - firstDot - 1); + minor.replace(QRegExp("\\."), ""); + project->values("QMAKE_LFLAGS").append("/VERSION:" + major + "." + minor); + } + + MakefileGenerator::init(); + initOld(); // Currently calling old DSP code to set variables. CLEAN UP! + + // Figure out what we're trying to build + if(project->first("TEMPLATE") == "vcapp") { + projectTarget = Application; + } else if(project->first("TEMPLATE") == "vclib") { + if(project->isActiveConfig("staticlib")) { + if (!project->values("RES_FILE").isEmpty()) + project->values("MSVCPROJ_LIBS") += escapeFilePaths(project->values("RES_FILE")); + projectTarget = StaticLib; + } else + projectTarget = SharedLib; + } + + // Setup PCH variables + precompH = project->first("PRECOMPILED_HEADER"); + precompCPP = project->first("PRECOMPILED_SOURCE"); + usePCH = !precompH.isEmpty() && project->isActiveConfig("precompile_header"); + if (usePCH) { + precompHFilename = fileInfo(precompH).fileName(); + // Created files + QString origTarget = unescapeFilePath(project->first("QMAKE_ORIG_TARGET")); + precompObj = origTarget + Option::obj_ext; + precompPch = origTarget + ".pch"; + // Add PRECOMPILED_HEADER to HEADERS + if (!project->values("HEADERS").contains(precompH)) + project->values("HEADERS") += precompH; + // Return to variable pool + project->values("PRECOMPILED_OBJECT") = QStringList(precompObj); + project->values("PRECOMPILED_PCH") = QStringList(precompPch); + + autogenPrecompCPP = precompCPP.isEmpty() && project->isActiveConfig("autogen_precompile_source"); + if (autogenPrecompCPP) { + precompCPP = precompH + + (Option::cpp_ext.count() ? Option::cpp_ext.at(0) : QLatin1String(".cpp")); + project->values("GENERATED_SOURCES") += precompCPP; + } else if (!precompCPP.isEmpty()) { + project->values("SOURCES") += precompCPP; + } + } + + // Add all input files for a custom compiler into a map for uniqueness, + // unless the compiler is configure as a combined stage, then use the first one + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.constBegin(); it != quc.constEnd(); ++it) { + const QStringList &invar = project->variables().value((*it) + ".input"); + const QString compiler_out = project->first((*it) + ".output"); + for(QStringList::ConstIterator iit = invar.constBegin(); iit != invar.constEnd(); ++iit) { + QStringList fileList = project->variables().value(*iit); + if (!fileList.isEmpty()) { + if (project->values((*it) + ".CONFIG").indexOf("combine") != -1) + fileList = QStringList(fileList.first()); + for(QStringList::ConstIterator fit = fileList.constBegin(); fit != fileList.constEnd(); ++fit) { + QString file = (*fit); + if (verifyExtraCompiler((*it), file)) { + if (!hasBuiltinCompiler(file)) { + extraCompilerSources[file] += *it; + } else { + QString out = Option::fixPathToTargetOS(replaceExtraCompilerVariables( + compiler_out, file, QString()), false); + extraCompilerSources[out] += *it; + extraCompilerOutputs[out] = QStringList(file); // Can only have one + } + } + } + } + } + } + +#if 0 // Debugging + Q_FOREACH(QString aKey, extraCompilerSources.keys()) { + qDebug("Extracompilers for %s are (%s)", aKey.toLatin1().constData(), extraCompilerSources.value(aKey).join(", ").toLatin1().constData()); + } + Q_FOREACH(QString aKey, extraCompilerOutputs.keys()) { + qDebug("Object mapping for %s is (%s)", aKey.toLatin1().constData(), extraCompilerOutputs.value(aKey).join(", ").toLatin1().constData()); + } + qDebug(""); +#endif +} + +bool VcprojGenerator::mergeBuildProject(MakefileGenerator *other) +{ + if (!other || !other->projectFile()) { + warn_msg(WarnLogic, "VcprojGenerator: Cannot merge null project."); + return false; + } + if (other->projectFile()->first("MAKEFILE_GENERATOR") != project->first("MAKEFILE_GENERATOR")) { + warn_msg(WarnLogic, "VcprojGenerator: Cannot merge other types of projects! (ignored)"); + return false; + } + + VcprojGenerator *otherVC = static_cast(other); + mergedProjects += otherVC; + return true; +} + +void VcprojGenerator::initProject() +{ + // Initialize XML sub elements + // - Do this first since project elements may need + // - to know of certain configuration options + initConfiguration(); + initRootFiles(); + initSourceFiles(); + initHeaderFiles(); + initGeneratedFiles(); + initLexYaccFiles(); + initTranslationFiles(); + initFormFiles(); + initResourceFiles(); + initExtraCompilerOutputs(); + + // Own elements ----------------------------- + vcProject.Name = unescapeFilePath(project->first("QMAKE_ORIG_TARGET")); + switch(which_dotnet_version()) { + case NET2010: + vcProject.Version = "10.00"; + break; + case NET2008: + vcProject.Version = "9,00"; + break; + case NET2005: + //### using ',' because of a bug in 2005 B2 + //### VS uses '.' or ',' depending on the regional settings! Using ',' always works. + vcProject.Version = "8,00"; + break; + case NET2003: + vcProject.Version = "7.10"; + break; + case NET2002: + vcProject.Version = "7.00"; + break; + default: + vcProject.Version = "7.00"; + warn_msg(WarnLogic, "Generator: MSVC.NET: Unknown version (%d) of MSVC detected for .vcproj", which_dotnet_version()); + break; + } + + vcProject.Keyword = project->first("VCPROJ_KEYWORD"); + if (project->isEmpty("CE_SDK") || project->isEmpty("CE_ARCH")) { + vcProject.PlatformName = (vcProject.Configuration.idl.TargetEnvironment == midlTargetWin64 ? "Win64" : "Win32"); + } else { + vcProject.PlatformName = project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; + } + // These are not used by Qt, but may be used by customers + vcProject.SccProjectName = project->first("SCCPROJECTNAME"); + vcProject.SccLocalPath = project->first("SCCLOCALPATH"); + vcProject.flat_files = project->isActiveConfig("flat"); +} + +void VcprojGenerator::initConfiguration() +{ + // Initialize XML sub elements + // - Do this first since main configuration elements may need + // - to know of certain compiler/linker options + VCConfiguration &conf = vcProject.Configuration; + conf.CompilerVersion = which_dotnet_version(); + + initCompilerTool(); + + // Only on configuration per build + bool isDebug = project->isActiveConfig("debug"); + + if(projectTarget == StaticLib) + initLibrarianTool(); + else { + conf.linker.GenerateDebugInformation = isDebug ? _True : _False; + initLinkerTool(); + } + initResourceTool(); + initIDLTool(); + + // Own elements ----------------------------- + QString temp = project->first("BuildBrowserInformation"); + switch (projectTarget) { + case SharedLib: + conf.ConfigurationType = typeDynamicLibrary; + break; + case StaticLib: + conf.ConfigurationType = typeStaticLibrary; + break; + case Application: + default: + conf.ConfigurationType = typeApplication; + break; + } + + conf.OutputDirectory = project->first("DESTDIR"); + if (conf.OutputDirectory.isEmpty()) + conf.OutputDirectory = ".\\"; + if (!conf.OutputDirectory.endsWith("\\")) + conf.OutputDirectory += '\\'; + if (conf.CompilerVersion >= NET2010) { + // The target name could have been changed. + conf.PrimaryOutput = project->first("TARGET"); + if ( !conf.PrimaryOutput.isEmpty() && !project->first("TARGET_VERSION_EXT").isEmpty() && project->isActiveConfig("shared")) + conf.PrimaryOutput.append(project->first("TARGET_VERSION_EXT")); + } + + conf.Name = project->values("BUILD_NAME").join(" "); + if (conf.Name.isEmpty()) + conf.Name = isDebug ? "Debug" : "Release"; + conf.ConfigurationName = conf.Name; + if (project->isEmpty("CE_SDK") || project->isEmpty("CE_ARCH")) { + conf.Name += (conf.idl.TargetEnvironment == midlTargetWin64 ? "|Win64" : "|Win32"); + } else { + conf.Name += "|" + project->values("CE_SDK").join(" ") + " (" + project->first("CE_ARCH") + ")"; + } + conf.ATLMinimizesCRunTimeLibraryUsage = (project->first("ATLMinimizesCRunTimeLibraryUsage").isEmpty() ? _False : _True); + conf.BuildBrowserInformation = triState(temp.isEmpty() ? (short)unset : temp.toShort()); + temp = project->first("CharacterSet"); + conf.CharacterSet = charSet(temp.isEmpty() ? (short)charSetNotSet : temp.toShort()); + conf.DeleteExtensionsOnClean = project->first("DeleteExtensionsOnClean"); + conf.ImportLibrary = conf.linker.ImportLibrary; + conf.IntermediateDirectory = project->first("OBJECTS_DIR"); + conf.WholeProgramOptimization = conf.compiler.WholeProgramOptimization; + temp = project->first("UseOfATL"); + if(!temp.isEmpty()) + conf.UseOfATL = useOfATL(temp.toShort()); + temp = project->first("UseOfMfc"); + if(!temp.isEmpty()) + conf.UseOfMfc = useOfMfc(temp.toShort()); + + // Configuration does not need parameters from + // these sub XML items; + initCustomBuildTool(); + initPreBuildEventTools(); + initPostBuildEventTools(); + // Only deploy for CE projects + if (!project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH")) + initDeploymentTool(); + initPreLinkEventTools(); + + // Set definite values in both configurations + if (isDebug) { + conf.compiler.PreprocessorDefinitions.removeAll("NDEBUG"); + } else { + conf.compiler.PreprocessorDefinitions += "NDEBUG"; + } +} + +void VcprojGenerator::initCompilerTool() +{ + QString placement = project->first("OBJECTS_DIR"); + if(placement.isEmpty()) + placement = ".\\"; + + VCConfiguration &conf = vcProject.Configuration; + conf.compiler.AssemblerListingLocation = placement ; + conf.compiler.ProgramDataBaseFileName = ".\\" ; + conf.compiler.ObjectFile = placement ; + conf.compiler.ExceptionHandling = ehNone; + // PCH + if (usePCH) { + conf.compiler.UsePrecompiledHeader = pchUseUsingSpecific; + conf.compiler.PrecompiledHeaderFile = "$(IntDir)\\" + precompPch; + conf.compiler.PrecompiledHeaderThrough = project->first("PRECOMPILED_HEADER"); + conf.compiler.ForcedIncludeFiles = project->values("PRECOMPILED_HEADER"); + + if (conf.CompilerVersion <= NET2003) { + // Minimal build option triggers an Internal Compiler Error + // when used in conjunction with /FI and /Yu, so remove it + // ### work-around for a VS 2003 bug. Move to some prf file or remove completely. + project->values("QMAKE_CFLAGS_DEBUG").removeAll("-Gm"); + project->values("QMAKE_CFLAGS_DEBUG").removeAll("/Gm"); + project->values("QMAKE_CXXFLAGS_DEBUG").removeAll("-Gm"); + project->values("QMAKE_CXXFLAGS_DEBUG").removeAll("/Gm"); + } + } + + conf.compiler.parseOptions(project->values("QMAKE_CXXFLAGS")); + + if (project->isActiveConfig("windows")) + conf.compiler.PreprocessorDefinitions += "_WINDOWS"; + else if (project->isActiveConfig("console")) + conf.compiler.PreprocessorDefinitions += "_CONSOLE"; + + conf.compiler.PreprocessorDefinitions += project->values("DEFINES"); + conf.compiler.PreprocessorDefinitions += project->values("PRL_EXPORT_DEFINES"); + conf.compiler.parseOptions(project->values("MSVCPROJ_INCPATH")); +} + +void VcprojGenerator::initLibrarianTool() +{ + VCConfiguration &conf = vcProject.Configuration; + conf.librarian.OutputFile = "$(OutDir)\\"; + conf.librarian.OutputFile += project->first("MSVCPROJ_TARGET"); + conf.librarian.AdditionalOptions += project->values("QMAKE_LIBFLAGS"); +} + +void VcprojGenerator::initLinkerTool() +{ + findLibraries(); // Need to add the highest version of the libs + VCConfiguration &conf = vcProject.Configuration; + conf.linker.parseOptions(project->values("QMAKE_LFLAGS")); + + foreach (const QString &libDir, project->values("QMAKE_LIBDIR")) { + if (libDir.startsWith("/LIBPATH:")) + conf.linker.AdditionalLibraryDirectories += libDir.mid(9); + else + conf.linker.AdditionalLibraryDirectories += libDir; + } + + if (!project->values("DEF_FILE").isEmpty()) + conf.linker.ModuleDefinitionFile = project->first("DEF_FILE"); + + foreach(QString libs, project->values("MSVCPROJ_LIBS")) { + if (libs.left(9).toUpper() == "/LIBPATH:") { + QStringList l = QStringList(libs); + conf.linker.parseOptions(l); + } else { + conf.linker.AdditionalDependencies += libs; + } + } + + conf.linker.OutputFile = "$(OutDir)\\"; + conf.linker.OutputFile += project->first("MSVCPROJ_TARGET"); + + if(project->isActiveConfig("dll")){ + conf.linker.parseOptions(project->values("QMAKE_LFLAGS_QT_DLL")); + } +} + +void VcprojGenerator::initResourceTool() +{ + VCConfiguration &conf = vcProject.Configuration; + conf.resource.PreprocessorDefinitions = conf.compiler.PreprocessorDefinitions; + + // We need to add _DEBUG for the debug version of the project, since the normal compiler defines + // do not contain it. (The compiler defines this symbol automatically, which is wy we don't need + // to add it for the compiler) However, the resource tool does not do this. + if(project->isActiveConfig("debug")) + conf.resource.PreprocessorDefinitions += "_DEBUG"; + if(project->isActiveConfig("staticlib")) + conf.resource.ResourceOutputFileName = "$(OutDir)\\$(InputName).res"; +} + +void VcprojGenerator::initIDLTool() +{ +} + +void VcprojGenerator::initCustomBuildTool() +{ +} + +void VcprojGenerator::initPreBuildEventTools() +{ +} + +void VcprojGenerator::initPostBuildEventTools() +{ + VCConfiguration &conf = vcProject.Configuration; + if (!project->values("QMAKE_POST_LINK").isEmpty()) { + QStringList cmdline = VCToolBase::fixCommandLine(var("QMAKE_POST_LINK")); + conf.postBuild.CommandLine = cmdline; + conf.postBuild.Description = cmdline.join(QLatin1String("\r\n")); + conf.postBuild.ExcludedFromBuild = _False; + } + + QString signature = !project->isEmpty("SIGNATURE_FILE") ? var("SIGNATURE_FILE") : var("DEFAULT_SIGNATURE"); + bool useSignature = !signature.isEmpty() && !project->isActiveConfig("staticlib") && + !project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH"); + if (useSignature) { + conf.postBuild.CommandLine.prepend( + QLatin1String("signtool sign /F ") + signature + QLatin1String(" \"$(TargetPath)\"")); + conf.postBuild.ExcludedFromBuild = _False; + } + + if (!project->values("MSVCPROJ_COPY_DLL").isEmpty()) { + conf.postBuild.Description += var("MSVCPROJ_COPY_DLL_DESC"); + conf.postBuild.CommandLine += var("MSVCPROJ_COPY_DLL"); + conf.postBuild.ExcludedFromBuild = _False; + } +} + +void VcprojGenerator::initDeploymentTool() +{ + VCConfiguration &conf = vcProject.Configuration; + QString targetPath = project->values("deploy.path").join(" "); + if (targetPath.isEmpty()) + targetPath = QString("%CSIDL_PROGRAM_FILES%\\") + project->first("TARGET"); + if (targetPath.endsWith("/") || targetPath.endsWith("\\")) + targetPath.chop(1); + + // Only deploy Qt libs for shared build + if (!project->values("QMAKE_QT_DLL").isEmpty()) { + QStringList& arg = project->values("MSVCPROJ_LIBS"); + for (QStringList::ConstIterator it = arg.constBegin(); it != arg.constEnd(); ++it) { + if (it->contains(project->first("QMAKE_LIBDIR"))) { + QString dllName = *it; + + if (dllName.contains(QLatin1String("QAxContainer")) + || dllName.contains(QLatin1String("qtmain")) + || dllName.contains(QLatin1String("QtUiTools"))) + continue; + dllName.replace(QLatin1String(".lib") , QLatin1String(".dll")); + QFileInfo info(dllName); + conf.deployment.AdditionalFiles += info.fileName() + + "|" + QDir::toNativeSeparators(info.absolutePath()) + + "|" + targetPath + + "|0;"; + } + } + } + + // C-runtime deployment + QString runtime = project->values("QT_CE_C_RUNTIME").join(QLatin1String(" ")); + if (!runtime.isEmpty() && (runtime != QLatin1String("no"))) { + QString runtimeVersion = QLatin1String("msvcr"); + QString mkspec = project->first("QMAKESPEC"); + // If no .qmake.cache has been found, we fallback to the original mkspec + if (mkspec.isEmpty()) + mkspec = project->first("QMAKESPEC_ORIGINAL"); + + if (!mkspec.isEmpty()) { + if (mkspec.endsWith("2008")) + runtimeVersion.append("90"); + else + runtimeVersion.append("80"); + if (project->isActiveConfig("debug")) + runtimeVersion.append("d"); + runtimeVersion.append(".dll"); + + if (runtime == "yes") { + // Auto-find C-runtime + QString vcInstallDir = qgetenv("VCINSTALLDIR"); + if (!vcInstallDir.isEmpty()) { + vcInstallDir += "\\ce\\dll\\"; + vcInstallDir += project->values("CE_ARCH").join(QLatin1String(" ")); + if (!QFileInfo(vcInstallDir + QDir::separator() + runtimeVersion).exists()) + runtime.clear(); + else + runtime = vcInstallDir; + } + } + } + + if (!runtime.isEmpty() && runtime != QLatin1String("yes")) { + conf.deployment.AdditionalFiles += runtimeVersion + + "|" + QDir::toNativeSeparators(runtime) + + "|" + targetPath + + "|0;"; + } + } + + // foreach item in DEPLOYMENT + foreach(QString item, project->values("DEPLOYMENT")) { + // get item.path + QString devicePath = project->first(item + ".path"); + if (devicePath.isEmpty()) + devicePath = targetPath; + // check if item.path is relative (! either /,\ or %) + if (!(devicePath.at(0) == QLatin1Char('/') + || devicePath.at(0) == QLatin1Char('\\') + || devicePath.at(0) == QLatin1Char('%'))) { + // create output path + devicePath = Option::fixPathToLocalOS(QDir::cleanPath(targetPath + QLatin1Char('\\') + devicePath)); + } + // foreach d in item.sources + // ### Qt 5: remove .sources, inconsistent with INSTALLS + foreach(QString source, project->values(item + ".sources") + project->values(item + ".files")) { + QString itemDevicePath = devicePath; + source = Option::fixPathToLocalOS(source); + QString nameFilter; + QFileInfo info(source); + QString searchPath; + if (info.isDir()) { + nameFilter = QLatin1String("*"); + itemDevicePath += "\\" + info.fileName(); + searchPath = info.absoluteFilePath(); + } else { + nameFilter = source.split('\\').last(); + searchPath = info.absolutePath(); + } + + int pathSize = searchPath.size(); + QDirIterator iterator(searchPath, QStringList() << nameFilter + , QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks + , QDirIterator::Subdirectories); + // foreach dirIterator-entry in d + while(iterator.hasNext()) { + iterator.next(); + QString absoluteItemPath = Option::fixPathToLocalOS(QFileInfo(iterator.filePath()).absolutePath()); + // Identify if it is just another subdir + int diffSize = absoluteItemPath.size() - pathSize; + // write out rules + conf.deployment.AdditionalFiles += iterator.fileName() + + "|" + absoluteItemPath + + "|" + itemDevicePath + (diffSize ? (absoluteItemPath.right(diffSize)) : QLatin1String("")) + + "|0;"; + } + } + } +} + +void VcprojGenerator::initPreLinkEventTools() +{ + VCConfiguration &conf = vcProject.Configuration; + if(!project->values("QMAKE_PRE_LINK").isEmpty()) { + QStringList cmdline = VCToolBase::fixCommandLine(var("QMAKE_PRE_LINK")); + conf.preLink.CommandLine = cmdline; + conf.preLink.Description = cmdline.join(QLatin1String("\r\n")); + conf.preLink.ExcludedFromBuild = _False; + } +} + +void VcprojGenerator::initRootFiles() +{ + // Note: Root files do _not_ have any filter name, filter nor GUID! + vcProject.RootFiles.addFiles(project->values("RC_FILE")); + + vcProject.RootFiles.Project = this; + vcProject.RootFiles.Config = &(vcProject.Configuration); + vcProject.RootFiles.CustomBuild = none; +} + +void VcprojGenerator::initSourceFiles() +{ + vcProject.SourceFiles.Name = "Source Files"; + vcProject.SourceFiles.Filter = "cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"; + vcProject.SourceFiles.Guid = _GUIDSourceFiles; + + vcProject.SourceFiles.addFiles(project->values("SOURCES")); + + vcProject.SourceFiles.Project = this; + vcProject.SourceFiles.Config = &(vcProject.Configuration); + vcProject.SourceFiles.CustomBuild = none; +} + +void VcprojGenerator::initHeaderFiles() +{ + vcProject.HeaderFiles.Name = "Header Files"; + vcProject.HeaderFiles.Filter = "h;hpp;hxx;hm;inl;inc;xsd"; + vcProject.HeaderFiles.Guid = _GUIDHeaderFiles; + + vcProject.HeaderFiles.addFiles(project->values("HEADERS")); + if (usePCH) // Generated PCH cpp file + vcProject.HeaderFiles.addFile(precompH); + + vcProject.HeaderFiles.Project = this; + vcProject.HeaderFiles.Config = &(vcProject.Configuration); +// vcProject.HeaderFiles.CustomBuild = mocHdr; +// addMocArguments(vcProject.HeaderFiles); +} + +void VcprojGenerator::initGeneratedFiles() +{ + vcProject.GeneratedFiles.Name = "Generated Files"; + vcProject.GeneratedFiles.Filter = "cpp;c;cxx;moc;h;def;odl;idl;res;"; + vcProject.GeneratedFiles.Guid = _GUIDGeneratedFiles; + + // ### These cannot have CustomBuild (mocSrc)!! + vcProject.GeneratedFiles.addFiles(project->values("GENERATED_SOURCES")); + vcProject.GeneratedFiles.addFiles(project->values("GENERATED_FILES")); + vcProject.GeneratedFiles.addFiles(project->values("IDLSOURCES")); + vcProject.GeneratedFiles.addFiles(project->values("RES_FILE")); + vcProject.GeneratedFiles.addFiles(project->values("QMAKE_IMAGE_COLLECTION")); // compat + if(!extraCompilerOutputs.isEmpty()) + vcProject.GeneratedFiles.addFiles(extraCompilerOutputs.keys()); + + vcProject.GeneratedFiles.Project = this; + vcProject.GeneratedFiles.Config = &(vcProject.Configuration); +// vcProject.GeneratedFiles.CustomBuild = mocSrc; +} + +void VcprojGenerator::initLexYaccFiles() +{ + vcProject.LexYaccFiles.Name = "Lex / Yacc Files"; + vcProject.LexYaccFiles.ParseFiles = _False; + vcProject.LexYaccFiles.Filter = "l;y"; + vcProject.LexYaccFiles.Guid = _GUIDLexYaccFiles; + + vcProject.LexYaccFiles.addFiles(project->values("LEXSOURCES")); + vcProject.LexYaccFiles.addFiles(project->values("YACCSOURCES")); + + vcProject.LexYaccFiles.Project = this; + vcProject.LexYaccFiles.Config = &(vcProject.Configuration); + vcProject.LexYaccFiles.CustomBuild = lexyacc; +} + +void VcprojGenerator::initTranslationFiles() +{ + vcProject.TranslationFiles.Name = "Translation Files"; + vcProject.TranslationFiles.ParseFiles = _False; + vcProject.TranslationFiles.Filter = "ts;xlf"; + vcProject.TranslationFiles.Guid = _GUIDTranslationFiles; + + vcProject.TranslationFiles.addFiles(project->values("TRANSLATIONS")); + + vcProject.TranslationFiles.Project = this; + vcProject.TranslationFiles.Config = &(vcProject.Configuration); + vcProject.TranslationFiles.CustomBuild = none; +} + +void VcprojGenerator::initFormFiles() +{ + vcProject.FormFiles.Name = "Form Files"; + vcProject.FormFiles.ParseFiles = _False; + vcProject.FormFiles.Filter = "ui"; + vcProject.FormFiles.Guid = _GUIDFormFiles; + + vcProject.FormFiles.addFiles(project->values("FORMS")); + vcProject.FormFiles.addFiles(project->values("FORMS3")); + + vcProject.FormFiles.Project = this; + vcProject.FormFiles.Config = &(vcProject.Configuration); + vcProject.FormFiles.CustomBuild = none; +} + +void VcprojGenerator::initResourceFiles() +{ + vcProject.ResourceFiles.Name = "Resource Files"; + vcProject.ResourceFiles.ParseFiles = _False; + vcProject.ResourceFiles.Filter = "qrc;*"; //"rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;ts;xlf;qrc"; + vcProject.ResourceFiles.Guid = _GUIDResourceFiles; + + // Bad hack, please look away ------------------------------------- + QString rcc_dep_cmd = project->values("rcc.depend_command").join(" "); + if(!rcc_dep_cmd.isEmpty()) { + QStringList qrc_files = project->values("RESOURCES"); + QStringList deps; + if(!qrc_files.isEmpty()) { + for (int i = 0; i < qrc_files.count(); ++i) { + char buff[256]; + QString dep_cmd = replaceExtraCompilerVariables(rcc_dep_cmd, qrc_files.at(i),""); + + dep_cmd = Option::fixPathToLocalOS(dep_cmd, true, false); + if(canExecute(dep_cmd)) { + dep_cmd.prepend(QLatin1String("cd ") + + escapeFilePath(Option::fixPathToLocalOS(Option::output_dir, false)) + + QLatin1String(" && ")); + if(FILE *proc = QT_POPEN(dep_cmd.toLatin1().constData(), "r")) { + QString indeps; + while(!feof(proc)) { + int read_in = (int)fread(buff, 1, 255, proc); + if(!read_in) + break; + indeps += QByteArray(buff, read_in); + } + QT_PCLOSE(proc); + if(!indeps.isEmpty()) + deps += fileFixify(indeps.replace('\n', ' ').simplified().split(' '), + QString(), Option::output_dir); + } + } + } + vcProject.ResourceFiles.addFiles(deps); + } + } + // You may look again -------------------------------------------- + + vcProject.ResourceFiles.addFiles(project->values("RESOURCES")); + vcProject.ResourceFiles.addFiles(project->values("IMAGES")); + + vcProject.ResourceFiles.Project = this; + vcProject.ResourceFiles.Config = &(vcProject.Configuration); + vcProject.ResourceFiles.CustomBuild = none; +} + +void VcprojGenerator::initExtraCompilerOutputs() +{ + QStringList otherFilters; + otherFilters << "FORMS" + << "FORMS3" + << "GENERATED_FILES" + << "GENERATED_SOURCES" + << "HEADERS" + << "IDLSOURCES" + << "IMAGES" + << "LEXSOURCES" + << "QMAKE_IMAGE_COLLECTION" + << "RC_FILE" + << "RESOURCES" + << "RES_FILE" + << "SOURCES" + << "TRANSLATIONS" + << "YACCSOURCES"; + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + QString extracompilerName = project->first((*it) + ".name"); + if (extracompilerName.isEmpty()) + extracompilerName = (*it); + + // Create an extra compiler filter and add the files + VCFilter extraCompile; + extraCompile.Name = extracompilerName; + extraCompile.ParseFiles = _False; + extraCompile.Filter = ""; + extraCompile.Guid = QString(_GUIDExtraCompilerFiles) + "-" + (*it); + + + // If the extra compiler has a variable_out set the output file + // is added to an other file list, and does not need its own.. + bool addOnInput = hasBuiltinCompiler(project->first((*it) + ".output")); + QString tmp_other_out = project->first((*it) + ".variable_out"); + if (!tmp_other_out.isEmpty() && !addOnInput) + continue; + + if (!addOnInput) { + QString tmp_out = project->first((*it) + ".output"); + if (project->values((*it) + ".CONFIG").indexOf("combine") != -1) { + // Combined output, only one file result + extraCompile.addFile( + Option::fixPathToTargetOS(replaceExtraCompilerVariables(tmp_out, QString(), QString()), false)); + } else { + // One output file per input + QStringList tmp_in = project->values(project->first((*it) + ".input")); + for (int i = 0; i < tmp_in.count(); ++i) { + const QString &filename = tmp_in.at(i); + if (extraCompilerSources.contains(filename)) + extraCompile.addFile( + Option::fixPathToTargetOS(replaceExtraCompilerVariables(filename, tmp_out, QString()), false)); + } + } + } else { + // In this case we the outputs have a built-in compiler, so we cannot add the custom + // build steps there. So, we turn it around and add it to the input files instead, + // provided that the input file variable is not handled already (those in otherFilters + // are handled, so we avoid them). + QStringList inputVars = project->values((*it) + ".input"); + foreach(QString inputVar, inputVars) { + if (!otherFilters.contains(inputVar)) { + QStringList tmp_in = project->values(inputVar); + for (int i = 0; i < tmp_in.count(); ++i) { + const QString &filename = tmp_in.at(i); + if (extraCompilerSources.contains(filename)) + extraCompile.addFile( + Option::fixPathToTargetOS(replaceExtraCompilerVariables(filename, QString(), QString()), false)); + } + } + } + } + extraCompile.Project = this; + extraCompile.Config = &(vcProject.Configuration); + extraCompile.CustomBuild = none; + + vcProject.ExtraCompilersFiles.append(extraCompile); + } +} + +void VcprojGenerator::initOld() +{ + // $$QMAKE.. -> $$MSVCPROJ.. ------------------------------------- + project->values("MSVCPROJ_LIBS") += project->values("QMAKE_LIBS"); + project->values("MSVCPROJ_LIBS") += project->values("QMAKE_LIBS_PRIVATE"); + QStringList &incs = project->values("INCLUDEPATH"); + for(QStringList::Iterator incit = incs.begin(); incit != incs.end(); ++incit) { + QString inc = (*incit); + if (!inc.startsWith('"') && !inc.endsWith('"')) + inc = QString("\"%1\"").arg(inc); // Quote all paths if not quoted already + project->values("MSVCPROJ_INCPATH").append("-I" + inc); + } + project->values("MSVCPROJ_INCPATH").append("-I" + specdir()); + + QString dest; + project->values("MSVCPROJ_TARGET") = QStringList(project->first("TARGET")); + Option::fixPathToTargetOS(project->first("TARGET")); + dest = project->first("TARGET") + project->first("TARGET_EXT"); + project->values("MSVCPROJ_TARGET") = QStringList(dest); + + // DLL COPY ------------------------------------------------------ + if(project->isActiveConfig("dll") && !project->values("DLLDESTDIR").isEmpty()) { + QStringList dlldirs = project->values("DLLDESTDIR"); + QString copydll(""); + QStringList::Iterator dlldir; + for(dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) { + if(!copydll.isEmpty()) + copydll += " && "; + copydll += "copy \"$(TargetPath)\" \"" + *dlldir + "\""; + } + + QString deststr("Copy " + dest + " to "); + for(dlldir = dlldirs.begin(); dlldir != dlldirs.end();) { + deststr += *dlldir; + ++dlldir; + if(dlldir != dlldirs.end()) + deststr += ", "; + } + + project->values("MSVCPROJ_COPY_DLL").append(copydll); + project->values("MSVCPROJ_COPY_DLL_DESC").append(deststr); + } + + project->values("QMAKE_INTERNAL_PRL_LIBS") << "MSVCPROJ_LIBS"; + + // Verbose output if "-d -d"... + outputVariables(); +} + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ + +VCProjectWriter *VcprojGenerator::createProjectWriter() +{ + return new VCProjectWriter; +} + +QString VcprojGenerator::replaceExtraCompilerVariables(const QString &var, const QStringList &in, const QStringList &out) +{ + QString ret = MakefileGenerator::replaceExtraCompilerVariables(var, in, out); + + QStringList &defines = project->values("VCPROJ_MAKEFILE_DEFINES"); + if(defines.isEmpty()) + defines.append(varGlue("PRL_EXPORT_DEFINES"," -D"," -D","") + + varGlue("DEFINES"," -D"," -D","")); + ret.replace("$(DEFINES)", defines.first()); + + QStringList &incpath = project->values("VCPROJ_MAKEFILE_INCPATH"); + if(incpath.isEmpty() && !this->var("MSVCPROJ_INCPATH").isEmpty()) + incpath.append(this->var("MSVCPROJ_INCPATH")); + ret.replace("$(INCPATH)", incpath.join(" ")); + + return ret; +} + +bool VcprojGenerator::openOutput(QFile &file, const QString &/*build*/) const +{ + QString outdir; + if(!file.fileName().isEmpty()) { + QFileInfo fi(fileInfo(file.fileName())); + if(fi.isDir()) + outdir = file.fileName() + QDir::separator(); + } + if(!outdir.isEmpty() || file.fileName().isEmpty()) { + QString ext = project->first("VCPROJ_EXTENSION"); + if(project->first("TEMPLATE") == "vcsubdirs") + ext = project->first("VCSOLUTION_EXTENSION"); + QString outputName = unescapeFilePath(project->first("TARGET")); + if (!project->first("MAKEFILE").isEmpty()) + outputName = project->first("MAKEFILE"); + file.setFileName(outdir + outputName + ext); + } + return Win32MakefileGenerator::openOutput(file, QString()); +} + +QString VcprojGenerator::fixFilename(QString ofile) const +{ + ofile = Option::fixPathToLocalOS(ofile); + int slashfind = ofile.lastIndexOf(Option::dir_sep); + if(slashfind == -1) { + ofile = ofile.replace('-', '_'); + } else { + int hyphenfind = ofile.indexOf('-', slashfind); + while (hyphenfind != -1 && slashfind < hyphenfind) { + ofile = ofile.replace(hyphenfind, 1, '_'); + hyphenfind = ofile.indexOf('-', hyphenfind + 1); + } + } + return ofile; +} + +QString VcprojGenerator::findTemplate(QString file) +{ + QString ret; + if(!exists((ret = file)) && + !exists((ret = QString(Option::mkfile::qmakespec + "/" + file))) && + !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::DataPath) + "/win32-msvc.net/" + file))) && + !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::DataPath) + "/win32-msvc2002/" + file))) && + !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::DataPath) + "/win32-msvc2003/" + file))) && + !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::DataPath) + "/win32-msvc2005/" + file))) && + !exists((ret = QString(QLibraryInfo::location(QLibraryInfo::DataPath) + "/win32-msvc2008/" + file)))) + return ""; + debug_msg(1, "Generator: MSVC.NET: Found template \'%s\'", ret.toLatin1().constData()); + return ret; +} + +void VcprojGenerator::outputVariables() +{ +#if 0 + qDebug("Generator: MSVC.NET: List of current variables:"); + for(QMap::ConstIterator it = project->variables().begin(); it != project->variables().end(); ++it) + qDebug("Generator: MSVC.NET: %s => %s", qPrintable(it.key()), qPrintable(it.value().join(" | "))); +#endif +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/msvc_vcproj.h b/qmake/generators/win32/msvc_vcproj.h new file mode 100644 index 0000000000..afe8f9ff21 --- /dev/null +++ b/qmake/generators/win32/msvc_vcproj.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MSVC_VCPROJ_H +#define MSVC_VCPROJ_H + +#include "winmakefile.h" +#include "msvc_objectmodel.h" + +QT_BEGIN_NAMESPACE + +enum Target { + Application, + SharedLib, + StaticLib +}; + +struct QUuid; +class VcprojGenerator : public Win32MakefileGenerator +{ + bool init_flag; + bool writeVcprojParts(QTextStream &); + + bool writeMakefile(QTextStream &); + bool writeProjectMakefile(); + + QString findTemplate(QString file); + void init(); + +public: + VcprojGenerator(); + ~VcprojGenerator(); + + QString defaultMakefile() const; + QString precompH, precompHFilename, precompCPP, + precompObj, precompPch; + bool autogenPrecompCPP; + static bool hasBuiltinCompiler(const QString &file); + + QMap extraCompilerSources; + QMap extraCompilerOutputs; + bool usePCH; + VCProjectWriter *projectWriter; + +protected: + virtual VCProjectWriter *createProjectWriter(); + virtual bool doDepends() const { return false; } //never necesary + virtual void processSources() { filterIncludedFiles("SOURCES"); filterIncludedFiles("GENERATED_SOURCES"); } + virtual QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &); + inline QString replaceExtraCompilerVariables(const QString &val, const QString &in, const QString &out) + { return MakefileGenerator::replaceExtraCompilerVariables(val, in, out); } + virtual bool supportsMetaBuild() { return true; } + virtual bool supportsMergedBuilds() { return true; } + virtual bool mergeBuildProject(MakefileGenerator *other); + + virtual bool openOutput(QFile &file, const QString &build) const; + virtual bool findLibraries(); + virtual void outputVariables(); + QString fixFilename(QString ofile) const; + + void initOld(); + virtual void initProject(); + void initConfiguration(); + void initCompilerTool(); + void initLinkerTool(); + void initLibrarianTool(); + void initResourceTool(); + void initIDLTool(); + void initCustomBuildTool(); + void initPreBuildEventTools(); + void initPostBuildEventTools(); + void initDeploymentTool(); + void initPreLinkEventTools(); + void initRootFiles(); + void initSourceFiles(); + void initHeaderFiles(); + void initGeneratedFiles(); + void initTranslationFiles(); + void initFormFiles(); + void initResourceFiles(); + 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 mergedProjects; + +private: + QStringList collectSubDirs(QMakeProject *proj); + QUuid increaseUUID(const QUuid &id); + friend class VCFilter; +}; + +inline QString VcprojGenerator::defaultMakefile() const +{ + return project->first("TARGET") + project->first("VCPROJ_EXTENSION"); +} + +inline bool VcprojGenerator::findLibraries() +{ + return Win32MakefileGenerator::findLibraries("MSVCPROJ_LIBS"); +} + +QT_END_NAMESPACE + +#endif // MSVC_VCPROJ_H diff --git a/qmake/generators/win32/msvc_vcxproj.cpp b/qmake/generators/win32/msvc_vcxproj.cpp new file mode 100644 index 0000000000..1e7c4b7a93 --- /dev/null +++ b/qmake/generators/win32/msvc_vcxproj.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "msvc_vcxproj.h" +#include "msbuild_objectmodel.h" +#include +#include +#include + + +QT_BEGIN_NAMESPACE +// Filter GUIDs (Do NOT change these!) ------------------------------ +const char _GUIDSourceFiles[] = "{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"; +const char _GUIDHeaderFiles[] = "{93995380-89BD-4b04-88EB-625FBE52EBFB}"; +const char _GUIDGeneratedFiles[] = "{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}"; +const char _GUIDResourceFiles[] = "{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}"; +const char _GUIDLexYaccFiles[] = "{E12AE0D2-192F-4d59-BD23-7D3FA58D3183}"; +const char _GUIDTranslationFiles[] = "{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}"; +const char _GUIDFormFiles[] = "{99349809-55BA-4b9d-BF79-8FDBB0286EB3}"; +const char _GUIDExtraCompilerFiles[] = "{E0D8C965-CC5F-43d7-AD63-FAEF0BBC0F85}"; +QT_END_NAMESPACE + +QT_BEGIN_NAMESPACE + + +VcxprojGenerator::VcxprojGenerator() : VcprojGenerator() +{ +} + +VCProjectWriter *VcxprojGenerator::createProjectWriter() +{ + return new VCXProjectWriter; +} + +QT_END_NAMESPACE + diff --git a/qmake/generators/win32/msvc_vcxproj.h b/qmake/generators/win32/msvc_vcxproj.h new file mode 100644 index 0000000000..3283cc7493 --- /dev/null +++ b/qmake/generators/win32/msvc_vcxproj.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MSVC_VCXPROJ_H +#define MSVC_VCXPROJ_H + +#include "winmakefile.h" +#include "msbuild_objectmodel.h" +#include "msvc_vcproj.h" + +QT_BEGIN_NAMESPACE + +class VcxprojGenerator : public VcprojGenerator +{ +public: + VcxprojGenerator(); + ~VcxprojGenerator(); + +protected: + virtual VCProjectWriter *createProjectWriter(); + +private: + friend class VCFilter; + +}; + +inline VcxprojGenerator::~VcxprojGenerator() +{ } + +QT_END_NAMESPACE + +#endif // MSVC_VCXPROJ_H diff --git a/qmake/generators/win32/winmakefile.cpp b/qmake/generators/win32/winmakefile.cpp new file mode 100644 index 0000000000..ef234ec506 --- /dev/null +++ b/qmake/generators/win32/winmakefile.cpp @@ -0,0 +1,895 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "winmakefile.h" +#include "option.h" +#include "project.h" +#include "meta.h" +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +Win32MakefileGenerator::Win32MakefileGenerator() : MakefileGenerator() +{ +} + +int +Win32MakefileGenerator::findHighestVersion(const QString &d, const QString &stem, const QString &ext) +{ + QString bd = Option::fixPathToLocalOS(d, true); + if(!exists(bd)) + return -1; + + QString dllStem = stem + QTDLL_POSTFIX; + QMakeMetaInfo libinfo; + bool libInfoRead = libinfo.readLib(bd + Option::dir_sep + dllStem); + + // If the library, for which we're trying to find the highest version + // number, is a static library + if (libInfoRead && libinfo.values("QMAKE_PRL_CONFIG").contains("staticlib")) + return -1; + + if(!project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").isEmpty()) + return project->values("QMAKE_" + stem.toUpper() + "_VERSION_OVERRIDE").first().toInt(); + + int biggest=-1; + if(!project->isActiveConfig("no_versionlink")) { + static QHash dirEntryListCache; + QStringList entries = dirEntryListCache.value(bd); + if (entries.isEmpty()) { + QDir dir(bd); + entries = dir.entryList(); + dirEntryListCache.insert(bd, entries); + } + + QRegExp regx(QString("((lib)?%1([0-9]*)).(%2|prl)$").arg(dllStem).arg(ext), Qt::CaseInsensitive); + for(QStringList::Iterator it = entries.begin(); it != entries.end(); ++it) { + if(regx.exactMatch((*it))) { + if (!regx.cap(3).isEmpty()) { + bool ok = true; + int num = regx.cap(3).toInt(&ok); + biggest = qMax(biggest, (!ok ? -1 : num)); + } + } + } + } + if(libInfoRead + && !libinfo.values("QMAKE_PRL_CONFIG").contains("staticlib") + && !libinfo.isEmpty("QMAKE_PRL_VERSION")) + biggest = qMax(biggest, libinfo.first("QMAKE_PRL_VERSION").replace(".", "").toInt()); + return biggest; +} + +bool +Win32MakefileGenerator::findLibraries(const QString &where) +{ + QStringList &l = project->values(where); + QList dirs; + { + QStringList &libpaths = project->values("QMAKE_LIBDIR"); + for(QStringList::Iterator libpathit = libpaths.begin(); + libpathit != libpaths.end(); ++libpathit) + dirs.append(QMakeLocalFileName((*libpathit))); + } + for(QStringList::Iterator it = l.begin(); it != l.end();) { + QChar quote; + bool modified_opt = false, remove = false; + QString opt = (*it).trimmed(); + if((opt[0] == '\'' || opt[0] == '"') && opt[(int)opt.length()-1] == opt[0]) { + quote = opt[0]; + opt = opt.mid(1, opt.length()-2); + } + if(opt.startsWith("/LIBPATH:")) { + dirs.append(QMakeLocalFileName(opt.mid(9))); + } else if(opt.startsWith("-L") || opt.startsWith("/L")) { + QString libpath = opt.mid(2); + QMakeLocalFileName l(libpath); + if(!dirs.contains(l)) { + dirs.append(l); + modified_opt = true; + if (!quote.isNull()) { + libpath = quote + libpath + quote; + quote = QChar(); + } + (*it) = "/LIBPATH:" + libpath; + } else { + remove = true; + } + } else if(opt.startsWith("-l") || opt.startsWith("/l")) { + QString lib = opt.right(opt.length() - 2), out; + if(!lib.isEmpty()) { + QString suffix; + if(!project->isEmpty("QMAKE_" + lib.toUpper() + "_SUFFIX")) + suffix = project->first("QMAKE_" + lib.toUpper() + "_SUFFIX"); + for(QList::Iterator it = dirs.begin(); + it != dirs.end(); ++it) { + QString extension; + int ver = findHighestVersion((*it).local(), lib); + if(ver > 0) + extension += QString::number(ver); + extension += suffix; + extension += ".lib"; + if(QMakeMetaInfo::libExists((*it).local() + Option::dir_sep + lib) || + exists((*it).local() + Option::dir_sep + lib + extension)) { + out = (*it).real() + Option::dir_sep + lib + extension; + if (out.contains(QLatin1Char(' '))) { + out.prepend(QLatin1Char('\"')); + out.append(QLatin1Char('\"')); + } + break; + } + } + } + if(out.isEmpty()) + out = lib + ".lib"; + modified_opt = true; + (*it) = out; + } else if(!exists(Option::fixPathToLocalOS(opt))) { + QList lib_dirs; + QString file = opt; + int slsh = file.lastIndexOf(Option::dir_sep); + if(slsh != -1) { + lib_dirs.append(QMakeLocalFileName(file.left(slsh+1))); + file = file.right(file.length() - slsh - 1); + } else { + lib_dirs = dirs; + } + if(file.endsWith(".lib")) { + file = file.left(file.length() - 4); + if(!file.at(file.length()-1).isNumber()) { + QString suffix; + if(!project->isEmpty("QMAKE_" + file.section(Option::dir_sep, -1).toUpper() + "_SUFFIX")) + suffix = project->first("QMAKE_" + file.section(Option::dir_sep, -1).toUpper() + "_SUFFIX"); + for(QList::Iterator dep_it = lib_dirs.begin(); dep_it != lib_dirs.end(); ++dep_it) { + QString lib_tmpl(file + "%1" + suffix + ".lib"); + int ver = findHighestVersion((*dep_it).local(), file); + if(ver != -1) { + if(ver) + lib_tmpl = lib_tmpl.arg(ver); + else + lib_tmpl = lib_tmpl.arg(""); + if(slsh != -1) { + QString dir = (*dep_it).real(); + if(!dir.endsWith(Option::dir_sep)) + dir += Option::dir_sep; + lib_tmpl.prepend(dir); + } + modified_opt = true; + (*it) = lib_tmpl; + break; + } + } + } + } + } + if(remove) { + it = l.erase(it); + } else { + if(!quote.isNull() && modified_opt) + (*it) = quote + (*it) + quote; + ++it; + } + } + return true; +} + +void +Win32MakefileGenerator::processPrlFiles() +{ + QHash processed; + QList libdirs; + { + QStringList &libpaths = project->values("QMAKE_LIBDIR"); + for(QStringList::Iterator libpathit = libpaths.begin(); libpathit != libpaths.end(); ++libpathit) + libdirs.append(QMakeLocalFileName((*libpathit))); + } + for(bool ret = false; true; ret = false) { + //read in any prl files included.. + QStringList l_out; + QString where = "QMAKE_LIBS"; + if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS")) + where = project->first("QMAKE_INTERNAL_PRL_LIBS"); + QStringList l = project->values(where); + for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) { + QString opt = (*it).trimmed(); + if((opt[0] == '\'' || opt[0] == '"') && opt[(int)opt.length()-1] == opt[0]) + opt = opt.mid(1, opt.length()-2); + if(opt.startsWith("/")) { + if(opt.startsWith("/LIBPATH:")) { + QMakeLocalFileName l(opt.mid(9)); + if(!libdirs.contains(l)) + libdirs.append(l); + } + } else if(!processed.contains(opt)) { + if(processPrlFile(opt)) { + processed.insert(opt, true); + ret = true; + } else if(QDir::isRelativePath(opt) || opt.startsWith("-l")) { + QString tmp; + if (opt.startsWith("-l")) + tmp = opt.mid(2); + else + tmp = opt; + for(QList::Iterator it = libdirs.begin(); it != libdirs.end(); ++it) { + QString prl = (*it).local() + Option::dir_sep + tmp; + // the original is used as the key + QString orgprl = prl; + if(processed.contains(prl)) { + break; + } else if(processPrlFile(prl)) { + processed.insert(orgprl, true); + ret = true; + break; + } + } + } + } + if(!opt.isEmpty()) + l_out.append(opt); + } + if(ret) + l = l_out; + else + break; + } +} + + +void Win32MakefileGenerator::processVars() +{ + //If the TARGET looks like a path split it into DESTDIR and the resulting TARGET + if(!project->isEmpty("TARGET")) { + QString targ = project->first("TARGET"); + int slsh = qMax(targ.lastIndexOf('/'), targ.lastIndexOf(Option::dir_sep)); + if(slsh != -1) { + if(project->isEmpty("DESTDIR")) + project->values("DESTDIR").append(""); + else if(project->first("DESTDIR").right(1) != Option::dir_sep) + project->values("DESTDIR") = QStringList(project->first("DESTDIR") + Option::dir_sep); + project->values("DESTDIR") = QStringList(project->first("DESTDIR") + targ.left(slsh+1)); + project->values("TARGET") = QStringList(targ.mid(slsh+1)); + } + } + + 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"); + + if (!project->values("QMAKE_INCDIR").isEmpty()) + project->values("INCLUDEPATH") += project->values("QMAKE_INCDIR"); + + if (!project->values("VERSION").isEmpty()) { + QStringList l = project->first("VERSION").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->values("TARGET_VERSION_EXT").isEmpty() + && !project->values("VER_MAJ").isEmpty() + && project->values("QMAKE_SYMBIAN_SHLIB").isEmpty()) + project->values("TARGET_VERSION_EXT").append(project->values("VER_MAJ").first()); + + if(project->isEmpty("QMAKE_COPY_FILE")) + project->values("QMAKE_COPY_FILE").append("$(COPY)"); + if(project->isEmpty("QMAKE_COPY_DIR")) + project->values("QMAKE_COPY_DIR").append("xcopy /s /q /y /i"); + if(project->isEmpty("QMAKE_INSTALL_FILE")) + project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_PROGRAM")) + project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)"); + if(project->isEmpty("QMAKE_INSTALL_DIR")) + project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)"); + + fixTargetExt(); + processRcFileVar(); + processFileTagsVar(); + + QStringList &incDir = project->values("INCLUDEPATH"); + for(QStringList::Iterator incDir_it = incDir.begin(); incDir_it != incDir.end(); ++incDir_it) { + if(!(*incDir_it).isEmpty()) + (*incDir_it) = Option::fixPathToTargetOS((*incDir_it), false, false); + } + QStringList &libDir = project->values("QMAKE_LIBDIR"); + for(QStringList::Iterator libDir_it = libDir.begin(); libDir_it != libDir.end(); ++libDir_it) { + if(!(*libDir_it).isEmpty()) + (*libDir_it) = Option::fixPathToTargetOS((*libDir_it), false, false); + } + + 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->isEmpty("QMAKE_EXTENSION_STATICLIB")) + project->values("QMAKE_EXTENSION_STATICLIB").append("lib"); + if (project->isEmpty("QMAKE_EXTENSION_SHLIB")) + project->values("QMAKE_EXTENSION_SHLIB").append("dll"); + + if (!project->values("QMAKE_APP_FLAG").isEmpty()) { + project->values("TARGET_EXT").append(".exe"); + } else if (project->isActiveConfig("shared")) { + 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"); + } +} + +void Win32MakefileGenerator::processRcFileVar() +{ + if (Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING) + return; + + if (((!project->values("VERSION").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->values("VERSION").first().split("."); + for (int i = vers.size(); i < 4; i++) + vers += "0"; + QString versionString = vers.join("."); + + 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->values("TARGET").first(); + + QString originalName = project->values("TARGET").first() + project->values("TARGET_EXT").first(); + + ts << "# if defined(UNDER_CE)" << endl; + ts << "# include " << endl; + ts << "# else" << endl; + ts << "# include " << endl; + ts << "# endif" << endl; + ts << endl; + ts << "VS_VERSION_INFO VERSIONINFO" << endl; + ts << "\tFILEVERSION " << QString(versionString).replace(".", ",") << endl; + ts << "\tPRODUCTVERSION " << QString(versionString).replace(".", ",") << endl; + ts << "\tFILEFLAGSMASK 0x3fL" << endl; + ts << "#ifdef _DEBUG" << endl; + ts << "\tFILEFLAGS VS_FF_DEBUG" << endl; + ts << "#else" << endl; + ts << "\tFILEFLAGS 0x0L" << endl; + ts << "#endif" << endl; + ts << "\tFILEOS VOS__WINDOWS32" << endl; + if (project->isActiveConfig("shared")) + ts << "\tFILETYPE VFT_DLL" << endl; + else + ts << "\tFILETYPE VFT_APP" << endl; + ts << "\tFILESUBTYPE 0x0L" << endl; + ts << "\tBEGIN" << endl; + ts << "\t\tBLOCK \"StringFileInfo\"" << endl; + ts << "\t\tBEGIN" << endl; + ts << "\t\t\tBLOCK \"040904B0\"" << endl; + ts << "\t\t\tBEGIN" << endl; + ts << "\t\t\t\tVALUE \"CompanyName\", \"" << companyName << "\\0\"" << endl; + ts << "\t\t\t\tVALUE \"FileDescription\", \"" << description << "\\0\"" << endl; + ts << "\t\t\t\tVALUE \"FileVersion\", \"" << versionString << "\\0\"" << endl; + ts << "\t\t\t\tVALUE \"LegalCopyright\", \"" << copyright << "\\0\"" << endl; + ts << "\t\t\t\tVALUE \"OriginalFilename\", \"" << originalName << "\\0\"" << endl; + ts << "\t\t\t\tVALUE \"ProductName\", \"" << productName << "\\0\"" << endl; + ts << "\t\t\tEND" << endl; + ts << "\t\tEND" << endl; + ts << "\t\tBLOCK \"VarFileInfo\"" << endl; + ts << "\t\tBEGIN" << endl; + ts << "\t\t\tVALUE \"Translation\", 0x409, 1200" << endl; + ts << "\t\tEND" << endl; + ts << "\tEND" << endl; + ts << "/* End of Version info */" << endl; + ts << endl; + + ts.flush(); + + + QString rcFilename = project->values("OUT_PWD").first() + + "/" + + project->values("TARGET").first() + + "_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->values("RC_FILE").first(); + + // 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(".rc", Option::res_ext); + project->values("RES_FILE").prepend(fileInfo(resFile).fileName()); + if (!project->values("OBJECTS_DIR").isEmpty()) { + QString resDestDir; + if (project->isActiveConfig("staticlib")) + resDestDir = fileInfo(project->first("DESTDIR")).absoluteFilePath(); + else + resDestDir = project->first("OBJECTS_DIR"); + resDestDir.append(Option::dir_sep); + project->values("RES_FILE").first().prepend(resDestDir); + } + project->values("RES_FILE").first() = Option::fixPathToTargetOS(project->values("RES_FILE").first(), false, false); + project->values("POST_TARGETDEPS") += project->values("RES_FILE"); + project->values("CLEAN_FILES") += project->values("RES_FILE"); + } +} + +void Win32MakefileGenerator::processFileTagsVar() +{ + QStringList tags; + tags << "SOURCES" << "GENERATED_SOURCES" << "DEF_FILE" << "RC_FILE" + << "TARGET" << "QMAKE_LIBS" << "DESTDIR" << "DLLDESTDIR" << "INCLUDEPATH"; + if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) { + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) + tags += project->values((*it)+".input"); + } + + //clean path + QStringList &filetags = project->values("QMAKE_FILETAGS"); + for(int i = 0; i < tags.size(); ++i) + filetags += Option::fixPathToTargetOS(tags.at(i), false); +} + +void Win32MakefileGenerator::writeCleanParts(QTextStream &t) +{ + t << "clean: compiler_clean " << var("CLEAN_DEPS"); + { + const char *clean_targets[] = { "OBJECTS", "QMAKE_CLEAN", "CLEAN_FILES", 0 }; + for(int i = 0; clean_targets[i]; ++i) { + const QStringList &list = project->values(clean_targets[i]); + const QString del_statement("-$(DEL_FILE)"); + if(project->isActiveConfig("no_delete_multiple_files")) { + for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + t << "\n\t" << del_statement << " " << escapeFilePath((*it)); + } else { + QString files, file; + const int commandlineLimit = 2047; // NT limit, expanded + for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { + file = " " + escapeFilePath((*it)); + if(del_statement.length() + files.length() + + qMax(fixEnvVariables(file).length(), file.length()) > commandlineLimit) { + t << "\n\t" << del_statement << files; + files.clear(); + } + files += file; + } + if(!files.isEmpty()) + t << "\n\t" << del_statement << files; + } + } + } + t << endl << endl; + + t << "distclean: clean"; + { + const char *clean_targets[] = { "QMAKE_DISTCLEAN", 0 }; + for(int i = 0; clean_targets[i]; ++i) { + const QStringList &list = project->values(clean_targets[i]); + const QString del_statement("-$(DEL_FILE)"); + if(project->isActiveConfig("no_delete_multiple_files")) { + for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + t << "\n\t" << del_statement << " " << escapeFilePath((*it)); + } else { + QString files, file; + const int commandlineLimit = 2047; // NT limit, expanded + for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { + file = " " + escapeFilePath((*it)); + if(del_statement.length() + files.length() + + qMax(fixEnvVariables(file).length(), file.length()) > 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)" << endl; + { + QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName())); + if(!ofile.isEmpty()) + t << "\t-$(DEL_FILE) " << ofile << endl; + } + t << endl; +} + +void Win32MakefileGenerator::writeIncPart(QTextStream &t) +{ + t << "INCPATH = "; + + const QStringList &incs = project->values("INCLUDEPATH"); + for(int i = 0; i < incs.size(); ++i) { + QString inc = incs.at(i); + inc.replace(QRegExp("\\\\$"), ""); + inc.replace(QRegExp("\""), ""); + if(!inc.isEmpty()) + t << "-I" << "\"" << inc << "\" "; + } + t << "-I\"" << specdir() << "\"" + << endl; +} + +void Win32MakefileGenerator::writeStandardParts(QTextStream &t) +{ + t << "####### Compiler, tools and options" << endl << endl; + t << "CC = " << var("QMAKE_CC") << endl; + t << "CXX = " << var("QMAKE_CXX") << endl; + t << "DEFINES = " + << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ") + << varGlue("DEFINES","-D"," -D","") << endl; + t << "CFLAGS = " << var("QMAKE_CFLAGS") << " $(DEFINES)" << endl; + t << "CXXFLAGS = " << var("QMAKE_CXXFLAGS") << " $(DEFINES)" << endl; + + writeIncPart(t); + writeLibsPart(t); + + t << "QMAKE = " << var("QMAKE_QMAKE") << endl; + t << "IDC = " << (project->isEmpty("QMAKE_IDC") ? QString("idc") : + Option::fixPathToTargetOS(var("QMAKE_IDC"), false)) << endl; + t << "IDL = " << (project->isEmpty("QMAKE_IDL") ? QString("midl") : + Option::fixPathToTargetOS(var("QMAKE_IDL"), false)) << endl; + t << "ZIP = " << var("QMAKE_ZIP") << endl; + t << "DEF_FILE = " << varList("DEF_FILE") << endl; + t << "RES_FILE = " << varList("RES_FILE") << endl; // Not on mingw, can't see why not though... + t << "COPY = " << var("QMAKE_COPY") << endl; + t << "COPY_FILE = " << var("QMAKE_COPY_FILE") << endl; + t << "COPY_DIR = " << var("QMAKE_COPY_DIR") << endl; + t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl; + t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl; + t << "MOVE = " << var("QMAKE_MOVE") << endl; + t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl; + t << "MKDIR = " << var("QMAKE_MKDIR") << endl; + t << "INSTALL_FILE = " << var("QMAKE_INSTALL_FILE") << endl; + t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl; + t << "INSTALL_DIR = " << var("QMAKE_INSTALL_DIR") << endl; + t << endl; + + t << "####### Output directory" << endl << endl; + if(!project->values("OBJECTS_DIR").isEmpty()) + t << "OBJECTS_DIR = " << var("OBJECTS_DIR").replace(QRegExp("\\\\$"),"") << endl; + else + t << "OBJECTS_DIR = . " << endl; + t << endl; + + t << "####### Files" << endl << endl; + t << "SOURCES = " << valList(escapeFilePaths(project->values("SOURCES"))) + << " " << valList(escapeFilePaths(project->values("GENERATED_SOURCES"))) << 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")); + target.remove("\""); + project->values("DEST_TARGET").prepend(destDir + target); + + writeObjectsPart(t); + + writeExtraCompilerVariables(t); + writeExtraVariables(t); + + t << "DIST = " << varList("DISTFILES") << endl; + t << "QMAKE_TARGET = " << var("QMAKE_ORIG_TARGET") << endl; + // 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" << endl; + t << "TARGET = " << escapeFilePath(target) << endl; + t << "DESTDIR_TARGET = " << escapeFilePath(var("DEST_TARGET")) << endl; + t << endl; + + t << "####### Implicit rules" << endl << endl; + writeImplicitRulesPart(t); + + t << "####### Build rules" << endl << endl; + writeBuildRulesPart(t); + + if(project->isActiveConfig("shared") && !project->values("DLLDESTDIR").isEmpty()) { + QStringList dlldirs = project->values("DLLDESTDIR"); + for (QStringList::Iterator dlldir = dlldirs.begin(); dlldir != dlldirs.end(); ++dlldir) { + t << "\t" << "-$(COPY_FILE) \"$(DESTDIR_TARGET)\" " << Option::fixPathToTargetOS(*dlldir, false) << endl; + } + } + t << 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"); + if(!project->isEmpty("TRANSLATIONS")) + dist_files << var("TRANSLATIONS"); + if(!project->isEmpty("FORMS")) { + QStringList &forms = project->values("FORMS"); + for(QStringList::Iterator 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) " + << dist_files.join(" ") << " " << var("TRANSLATIONS") << " "; + if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) { + const QStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for(QStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + const QStringList &inputs = project->values((*it)+".input"); + for(QStringList::ConstIterator input = inputs.begin(); input != inputs.end(); ++input) { + t << (*input) << " "; + } + } + } + t << endl << endl; + + writeCleanParts(t); + writeExtraTargets(t); + writeExtraCompilerTargets(t); + t << endl << endl; +} + +void Win32MakefileGenerator::writeLibsPart(QTextStream &t) +{ + if(project->isActiveConfig("staticlib") && project->first("TEMPLATE") == "lib") { + t << "LIBAPP = " << var("QMAKE_LIB") << endl; + t << "LIBFLAGS = " << var("QMAKE_LIBFLAGS") << endl; + } else { + t << "LINK = " << var("QMAKE_LINK") << endl; + t << "LFLAGS = "; + if(!project->values("QMAKE_LIBDIR").isEmpty()) + writeLibDirPart(t); + t << var("QMAKE_LFLAGS") << endl; + t << "LIBS = " << var("QMAKE_LIBS") << " " << var("QMAKE_LIBS_PRIVATE") << endl; + } +} + +void Win32MakefileGenerator::writeLibDirPart(QTextStream &t) +{ + QStringList libDirs = project->values("QMAKE_LIBDIR"); + for (int i = 0; i < libDirs.size(); ++i) + libDirs[i].remove("\""); + t << valGlue(libDirs,"-L\"","\" -L\"","\"") << " "; +} + +void Win32MakefileGenerator::writeObjectsPart(QTextStream &t) +{ + t << "OBJECTS = " << valList(escapeDependencyPaths(project->values("OBJECTS"))) << endl; +} + +void Win32MakefileGenerator::writeImplicitRulesPart(QTextStream &t) +{ + t << ".SUFFIXES:"; + for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) + t << " " << (*cppit); + for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit) + t << " " << (*cit); + t << endl << endl; + 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") << endl << 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") << endl << endl; +} + +void Win32MakefileGenerator::writeBuildRulesPart(QTextStream &) +{ +} + +void Win32MakefileGenerator::writeRcFilePart(QTextStream &t) +{ + if(!project->values("RC_FILE").isEmpty()) { + const QString res_file = project->first("RES_FILE"), + rc_file = fileFixify(project->first("RC_FILE")); + // The resource tool needs to have the same defines passed in as the compiler, since you may + // use these defines in the .rc file itself. 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 define the .pro file. + t << res_file << ": " << rc_file << "\n\t" + << var("QMAKE_RC") << (project->isActiveConfig("debug") ? " -D_DEBUG" : "") << " $(DEFINES) -fo " << res_file << " " << rc_file; + t << endl << endl; + } +} + +QString Win32MakefileGenerator::getLibTarget() +{ + return QString(project->first("TARGET") + project->first("TARGET_VERSION_EXT") + ".lib"); +} + +QString Win32MakefileGenerator::defaultInstall(const QString &t) +{ + if((t != "target" && t != "dlltarget") || + (t == "dlltarget" && (project->first("TEMPLATE") != "lib" || !project->isActiveConfig("shared"))) || + project->first("TEMPLATE") == "subdirs") + return QString(); + + const QString root = "$(INSTALL_ROOT)"; + QStringList &uninst = project->values(t + ".uninstall"); + QString ret; + QString targetdir = Option::fixPathToTargetOS(project->first(t + ".path"), false); + targetdir = fileFixify(targetdir, FileFixifyAbsolute); + if(targetdir.right(1) != Option::dir_sep) + targetdir += Option::dir_sep; + + 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")); + int slsh = dst_prl.lastIndexOf(Option::dir_sep); + if(slsh != -1) + dst_prl = dst_prl.right(dst_prl.length() - slsh - 1); + dst_prl = filePrefixRoot(root, targetdir + dst_prl); + ret += "-$(INSTALL_FILE) \"" + project->first("QMAKE_INTERNAL_PRL_FILE") + "\" \"" + dst_prl + "\""; + if(!uninst.isEmpty()) + uninst.append("\n\t"); + uninst.append("-$(DEL_FILE) \"" + dst_prl + "\""); + } + if(project->isActiveConfig("create_pc")) { + QString dst_pc = pkgConfigFileName(false); + if (!dst_pc.isEmpty()) { + dst_pc = filePrefixRoot(root, targetdir + dst_pc); + if(!ret.isEmpty()) + ret += "\n\t"; + ret += "-$(INSTALL_FILE) \"" + pkgConfigFileName(true) + "\" \"" + dst_pc + "\""; + if(!uninst.isEmpty()) + uninst.append("\n\t"); + uninst.append("-$(DEL_FILE) \"" + dst_pc + "\""); + } + } + if(project->isActiveConfig("shared") && !project->isActiveConfig("plugin")) { + QString lib_target = getLibTarget(); + lib_target.remove('"'); + QString src_targ = (project->isEmpty("DESTDIR") ? QString("$(DESTDIR)") : project->first("DESTDIR")) + lib_target; + QString dst_targ = 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(t + ".CONFIG").indexOf("no_dll") == -1) { + QString src_targ = "$(DESTDIR_TARGET)"; + QString dst_targ = 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; +} + +QString Win32MakefileGenerator::escapeFilePath(const QString &path) const +{ + QString ret = path; + if(!ret.isEmpty()) { + ret = unescapeFilePath(ret); + if(ret.contains(" ")) + ret = "\"" + ret + "\""; + debug_msg(2, "EscapeFilePath: %s -> %s", path.toLatin1().constData(), ret.toLatin1().constData()); + } + return ret; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/win32/winmakefile.h b/qmake/generators/win32/winmakefile.h new file mode 100644 index 0000000000..fb11e1ae9d --- /dev/null +++ b/qmake/generators/win32/winmakefile.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WINMAKEFILE_H +#define WINMAKEFILE_H + +#include "makefile.h" + +QT_BEGIN_NAMESPACE + +// In the Qt evaluation and educational version, we have a postfix in the +// library name (e.g. qtmteval301.dll). QTDLL_POSTFIX is used for this. +// A script modifies these lines when building eval/edu version, so be careful +// when changing them. +#ifndef QTDLL_POSTFIX +#define QTDLL_POSTFIX "" +#endif + +class Win32MakefileGenerator : public MakefileGenerator +{ +public: + Win32MakefileGenerator(); + ~Win32MakefileGenerator(); +protected: + virtual QString defaultInstall(const QString &); + virtual void writeCleanParts(QTextStream &t); + virtual void writeStandardParts(QTextStream &t); + virtual void writeIncPart(QTextStream &t); + virtual void writeLibDirPart(QTextStream &t); + virtual void writeLibsPart(QTextStream &t); + virtual void writeObjectsPart(QTextStream &t); + virtual void writeImplicitRulesPart(QTextStream &t); + virtual void writeBuildRulesPart(QTextStream &); + virtual QString escapeFilePath(const QString &path) const; + + virtual void writeRcFilePart(QTextStream &t); + + int findHighestVersion(const QString &dir, const QString &stem, const QString &ext = QLatin1String("lib")); + bool findLibraries(const QString &); + virtual bool findLibraries(); + + virtual void processPrlFiles(); + virtual void processVars(); + virtual void fixTargetExt(); + virtual void processRcFileVar(); + virtual void processFileTagsVar(); + virtual QString getLibTarget(); +}; + +inline Win32MakefileGenerator::~Win32MakefileGenerator() +{ } + +inline bool Win32MakefileGenerator::findLibraries() +{ return findLibraries("QMAKE_LIBS") && findLibraries("QMAKE_LIBS_PRIVATE"); } + +QT_END_NAMESPACE + +#endif // WINMAKEFILE_H diff --git a/qmake/generators/xmloutput.cpp b/qmake/generators/xmloutput.cpp new file mode 100644 index 0000000000..8b049ad04a --- /dev/null +++ b/qmake/generators/xmloutput.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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(); + 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.count()) + 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").arg(currentIndent).arg(o.xo_text)); + addRaw(QString("%1").arg(o.xo_value)); + addRaw(QString("").arg(o.xo_text)); + break; + case tValueTag: + addRaw(QString("%1").arg(doConversion(o.xo_text))); + setFormat(NoNewLine); + closeTag(); + setFormat(NewLine); + break; + case tImport: + addRaw(QString("\n%1").arg(currentIndent).arg(o.xo_text).arg(o.xo_value)); + break; + case tCloseTag: + if (o.xo_value.count()) + closeAll(); + else if (o.xo_text.count()) + 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 ("", not "/>") without using addRaw().. + if (!o.xo_text.count()) { + 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(""); + addRaw(output.arg(o.xo_text)); + } + break; + case tCDATA: + { + QString output(""); + addRaw(output.arg(o.xo_text)); + } + break; + } + return *this; +} + + +// Output functions ---------------------------------------------------------- +void XmlOutput::newTag(const QString &tag) +{ + Q_ASSERT_X(tag.count(), "XmlOutput", "Cannot open an empty tag"); + newTagOpen(tag); + closeOpen(); +} + +void XmlOutput::newTagOpen(const QString &tag) +{ + Q_ASSERT_X(tag.count(), "XmlOutput", "Cannot open an empty tag"); + closeOpen(); + + if (format == NewLine) + xmlFile << 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.count()) + //warn_msg(WarnLogic, ": Cannot close tag in Bare state, %d tags on stack", tagStack.count()); + qDebug(": Cannot close tag in Bare state, %d tags on stack", tagStack.count()); + else + //warn_msg(WarnLogic, ": Cannot close tag, no tags on stack"); + qDebug(": Cannot close tag, no tags on stack"); + return; + case Tag: + decreaseIndent(); // <--- Pre-decrease indent + if (format == NewLine) + xmlFile << endl << currentIndent; + xmlFile << "'; + 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.count(); + while (left-- && cont) { + cont = tagStack.last().compare(tag) != 0; + closeTag(); + } +} + +void XmlOutput::closeAll() +{ + if (!tagStack.count()) + 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("") + .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.count() ? tagStack.last().toLatin1().constData() : "Root"), + attribute.toLatin1().constData()); + return; + case Attribute: + break; + } + if (format == NewLine) + xmlFile << 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.count() ? tagStack.last().toLatin1().constData() : "Root"), + attribute.toLatin1().constData()); + return; + case Attribute: + break; + } + xmlFile << " " << doConversion(attribute) << "=\"" << doConversion(value) << "\""; +} + +QT_END_NAMESPACE diff --git a/qmake/generators/xmloutput.h b/qmake/generators/xmloutput.h new file mode 100644 index 0000000000..9b97a3da94 --- /dev/null +++ b/qmake/generators/xmloutput.h @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XMLOUTPUT_H +#define XMLOUTPUT_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class XmlOutput +{ +public: + enum ConverstionType { + NoConversion, // No change + EscapeConversion, // Use '\"' + XMLConversion // Use " + }; + 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, // + }; + enum XMLType { + tNothing, // No XML output, and not state change + tRaw, // Raw text (no formating) + tDeclaration, // + tTag, // value + tValueTag, // value + tCloseTag, // Closes an open tag + tAttribute, // attribute2="value"> + tAttributeTag, // attribute on the same line as a tag + tData, // Tag data (formating done) + tImport, // + tComment, // + tCDATA // + }; + + 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 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 diff --git a/qmake/main.cpp b/qmake/main.cpp new file mode 100644 index 0000000000..7d4d458b0d --- /dev/null +++ b/qmake/main.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "project.h" +#include "property.h" +#include "option.h" +#include "cachekeys.h" +#include "metamakefile.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// for Borland, main is defined to qMain which breaks qmake +#undef main +#ifdef Q_OS_MAC +#endif + +/* 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) +{ + // 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); + + // 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(); +#ifdef Q_WS_WIN + if(!(oldpwd.length() == 3 && oldpwd[0].isLetter() && oldpwd.endsWith(":/"))) +#endif + { + if(oldpwd.right(1) != QString(QChar(QDir::separator()))) + oldpwd += QDir::separator(); + } + Option::output_dir = oldpwd; //for now this is the output dir + + if(Option::output.fileName() != "-") { + QFileInfo fi(Option::output); + QString dir; + if(fi.isDir()) { + dir = fi.filePath(); + } else { + QString tmp_dir = fi.path(); + if(!tmp_dir.isEmpty() && QFile::exists(tmp_dir)) + dir = tmp_dir; + } + if(!dir.isNull() && dir != ".") + Option::output_dir = dir; + if(QDir::isRelativePath(Option::output_dir)) + Option::output_dir.prepend(oldpwd); + Option::output_dir = QDir::cleanPath(Option::output_dir); + } + + QMakeProperty prop; + if(Option::qmake_mode == Option::QMAKE_QUERY_PROPERTY || + Option::qmake_mode == Option::QMAKE_SET_PROPERTY || + Option::qmake_mode == Option::QMAKE_UNSET_PROPERTY) + return prop.exec() ? 0 : 101; + + QMakeProject project(&prop); + 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::fixPathToLocalOS((*pfile)); + if(!QFile::exists(fn)) { + fprintf(stderr, "Cannot find file: %s.\n", fn.toLatin1().constData()); + exit_val = 2; + continue; + } + + //setup pwd properly + debug_msg(1, "Resetting dir to: %s", oldpwd.toLatin1().constData()); + qmake_setpwd(oldpwd); //reset the old pwd + int di = fn.lastIndexOf(QDir::separator()); + if(di != -1) { + debug_msg(1, "Changing dir to: %s", fn.left(di).toLatin1().constData()); + if(!qmake_setpwd(fn.left(di))) + fprintf(stderr, "Cannot find directory: %s\n", fn.left(di).toLatin1().constData()); + fn = fn.right(fn.length() - di - 1); + } + + // read project.. + if(!project.read(fn)) { + fprintf(stderr, "Error processing project file: %s\n", + fn == "-" ? "(stdin)" : (*pfile).toLatin1().constData()); + exit_val = 3; + continue; + } + if(Option::mkfile::do_preprocess) //no need to create makefile + continue; + } + + bool success = true; + MetaMakefileGenerator *mkfile = MetaMakefileGenerator::createMetaGenerator(&project, QString(), false, &success); + if (!success) + exit_val = 3; + + if(mkfile && !mkfile->write(oldpwd)) { + 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", (*pfile).toLatin1().constData()); + exit_val = 5; + } + delete mkfile; + mkfile = NULL; + } + qmakeClearCaches(); + return exit_val; +} + +QT_END_NAMESPACE + +int main(int argc, char **argv) +{ + return QT_PREPEND_NAMESPACE(runQMake)(argc, argv); +} diff --git a/qmake/meta.cpp b/qmake/meta.cpp new file mode 100644 index 0000000000..ef822a6940 --- /dev/null +++ b/qmake/meta.cpp @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "meta.h" +#include "project.h" +#include "option.h" +#include + +QT_BEGIN_NAMESPACE + +QMap > QMakeMetaInfo::cache_vars; + +QMakeMetaInfo::QMakeMetaInfo() +{ + +} + + +bool +QMakeMetaInfo::readLib(QString lib) +{ + clear(); + QString meta_file = findLib(lib); + + if(cache_vars.contains(meta_file)) { + vars = cache_vars[meta_file]; + return true; + } + + bool ret = false; + if(!meta_file.isNull()) { + if(meta_file.endsWith(Option::pkgcfg_ext)) { + if((ret=readPkgCfgFile(meta_file))) + meta_type = "pkgcfg"; + } else if(meta_file.endsWith(Option::libtool_ext)) { + if((ret=readLibtoolFile(meta_file))) + meta_type = "libtool"; + } else if(meta_file.endsWith(Option::prl_ext)) { + QMakeProject proj; + if(!proj.read(Option::fixPathToLocalOS(meta_file), QMakeProject::ReadProFile)) + return false; + meta_type = "qmake"; + vars = proj.variables(); + ret = true; + } else { + warn_msg(WarnLogic, "QMakeMetaInfo: unknown file format for %s", meta_file.toLatin1().constData()); + } + } + if(ret) + cache_vars.insert(meta_file, vars); + return ret; +} + + +void +QMakeMetaInfo::clear() +{ + vars.clear(); +} + + +QString +QMakeMetaInfo::findLib(QString lib) +{ + if((lib[0] == '\'' || lib[0] == '"') && + lib[lib.length()-1] == lib[0]) + lib = lib.mid(1, lib.length()-2); + lib = Option::fixPathToLocalOS(lib); + + QString ret; + QString extns[] = { Option::prl_ext, /*Option::pkgcfg_ext, Option::libtool_ext,*/ QString() }; + for(int extn = 0; !extns[extn].isNull(); extn++) { + if(lib.endsWith(extns[extn])) + ret = QFile::exists(lib) ? lib : QString(); + } + if(ret.isNull()) { + for(int extn = 0; !extns[extn].isNull(); extn++) { + if(QFile::exists(lib + extns[extn])) { + ret = lib + extns[extn]; + break; + } + } + } + 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; +} + + +bool +QMakeMetaInfo::readLibtoolFile(const QString &f) +{ + /* I can just run the .la through the .pro parser since they are compatible.. */ + QMakeProject proj; + if(!proj.read(Option::fixPathToLocalOS(f), QMakeProject::ReadProFile)) + return false; + QString dirf = Option::fixPathToTargetOS(f).section(Option::dir_sep, 0, -2); + if(dirf == f) + dirf = ""; + else if(!dirf.isEmpty() && !dirf.endsWith(Option::output_dir)) + dirf += Option::dir_sep; + QMap &v = proj.variables(); + for(QMap::Iterator it = v.begin(); it != v.end(); ++it) { + QStringList lst = it.value(); + if(lst.count() == 1 && (lst.first().startsWith("'") || lst.first().startsWith("\"")) && + lst.first().endsWith(QString(lst.first()[0]))) + lst = QStringList(lst.first().mid(1, lst.first().length() - 2)); + if(!vars.contains("QMAKE_PRL_TARGET") && + (it.key() == "dlname" || it.key() == "library_names" || it.key() == "old_library")) { + QString dir = v["libdir"].first(); + if((dir.startsWith("'") || dir.startsWith("\"")) && dir.endsWith(QString(dir[0]))) + dir = dir.mid(1, dir.length() - 2); + dir = dir.trimmed(); + if(!dir.isEmpty() && !dir.endsWith(Option::dir_sep)) + dir += Option::dir_sep; + if(lst.count() == 1) + lst = lst.first().split(" "); + for(QStringList::Iterator lst_it = lst.begin(); lst_it != lst.end(); ++lst_it) { + bool found = false; + QString dirs[] = { "", dir, dirf, dirf + ".libs" + QDir::separator(), "(term)" }; + for(int i = 0; !found && dirs[i] != "(term)"; i++) { + if(QFile::exists(dirs[i] + (*lst_it))) { + QString targ = dirs[i] + (*lst_it); + if(QDir::isRelativePath(targ)) + targ.prepend(qmake_getpwd() + QDir::separator()); + vars["QMAKE_PRL_TARGET"] << targ; + found = true; + } + } + if(found) + break; + } + } else if(it.key() == "dependency_libs") { + if(lst.count() == 1) { + QString dep = lst.first(); + if((dep.startsWith("'") || dep.startsWith("\"")) && dep.endsWith(QString(dep[0]))) + dep = dep.mid(1, dep.length() - 2); + lst = dep.trimmed().split(" "); + } + QMakeProject *conf = NULL; + for(QStringList::Iterator lit = lst.begin(); lit != lst.end(); ++lit) { + if((*lit).startsWith("-R")) { + if(!conf) { + conf = new QMakeProject; + conf->read(QMakeProject::ReadAll ^ QMakeProject::ReadProFile); + } + if(!conf->isEmpty("QMAKE_LFLAGS_RPATH")) + (*lit) = conf->first("QMAKE_LFLAGS_RPATH") + (*lit).mid(2); + } + } + if(conf) + delete conf; + vars["QMAKE_PRL_LIBS"] += lst; + } + } + return true; +} + +bool +QMakeMetaInfo::readPkgCfgFile(const QString &f) +{ + fprintf(stderr, "Must implement reading in pkg-config files (%s)!!!\n", f.toLatin1().constData()); + return false; +} + +QT_END_NAMESPACE diff --git a/qmake/meta.h b/qmake/meta.h new file mode 100644 index 0000000000..5fd3e7220d --- /dev/null +++ b/qmake/meta.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef META_H +#define META_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QMakeMetaInfo +{ + bool readLibtoolFile(const QString &f); + bool readPkgCfgFile(const QString &f); + QMap vars; + QString meta_type; + static QMap > cache_vars; + void clear(); +public: + QMakeMetaInfo(); + + bool readLib(QString lib); + static QString findLib(QString lib); + static bool libExists(QString lib); + QString type() const; + + bool isEmpty(const QString &v); + QStringList &values(const QString &v); + QString first(const QString &v); + QMap &variables(); +}; + +inline bool QMakeMetaInfo::isEmpty(const QString &v) +{ return !vars.contains(v) || vars[v].isEmpty(); } + +inline QString QMakeMetaInfo::type() const +{ return meta_type; } + +inline QStringList &QMakeMetaInfo::values(const QString &v) +{ return vars[v]; } + +inline QString QMakeMetaInfo::first(const QString &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 QString(""); + else + return vars[v].first(); +#else + return isEmpty(v) ? QString("") : vars[v].first(); +#endif +} + +inline QMap &QMakeMetaInfo::variables() +{ return vars; } + +inline bool QMakeMetaInfo::libExists(QString lib) +{ return !findLib(lib).isNull(); } + +QT_END_NAMESPACE + +#endif // META_H diff --git a/qmake/option.cpp b/qmake/option.cpp new file mode 100644 index 0000000000..dd283365f2 --- /dev/null +++ b/qmake/option.cpp @@ -0,0 +1,813 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "option.h" +#include "cachekeys.h" +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +//convenience +const char *Option::application_argv0 = 0; +QString Option::prf_ext; +QString Option::js_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; +QString Option::h_moc_ext; +QStringList Option::cpp_ext; +QStringList Option::c_ext; +QString Option::obj_ext; +QString Option::lex_ext; +QString Option::yacc_ext; +QString Option::pro_ext; +QString Option::mmp_ext; +QString Option::dir_sep; +QString Option::dirlist_sep; +QString Option::h_moc_mod; +QString Option::cpp_moc_mod; +QString Option::yacc_mod; +QString Option::lex_mod; +QString Option::sysenv_mod; +QString Option::res_ext; +char Option::field_sep; + +//mode +Option::QMAKE_MODE Option::qmake_mode = Option::QMAKE_GENERATE_NOTHING; + +//all modes +QString Option::qmake_abslocation; +int Option::warn_level = WarnLogic | WarnDeprecated; +int Option::debug_level = 0; +QFile Option::output; +QString Option::output_dir; +Option::QMAKE_RECURSIVE Option::recursive = Option::QMAKE_RECURSIVE_DEFAULT; +QStringList Option::before_user_vars; +QStringList Option::after_user_vars; +QStringList Option::user_configs; +QStringList Option::after_user_configs; +QString Option::user_template; +QString Option::user_template_prefix; +QStringList Option::shellPath; +Option::HOST_MODE Option::host_mode = Option::HOST_UNKNOWN_MODE; +Option::TARG_MODE Option::target_mode = Option::TARG_UNKNOWN_MODE; +bool Option::target_mode_overridden = 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 +QString Option::mkfile::qmakespec; +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; +bool Option::mkfile::do_stub_makefile = false; +bool Option::mkfile::do_cache = true; +QString Option::mkfile::cachefile; +QStringList Option::mkfile::project_files; +QString Option::mkfile::qmakespec_commandline; + +static Option::QMAKE_MODE default_mode(QString progname) +{ + int s = progname.lastIndexOf(QDir::separator()); + if(s != -1) + progname = progname.right(progname.length() - (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 ret; + QDir dir(path); + if(dir.exists(dir.dirName() + Option::pro_ext)) { + ret = dir.filePath(dir.dirName()) + Option::pro_ext; + } else { //last try.. + QStringList profiles = dir.entryList(QStringList("*" + Option::pro_ext)); + if(profiles.count() == 1) + ret = dir.filePath(profiles.at(0)); + } + return ret; +} + +QString project_builtin_regx(); +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 built,\n" + " defaults to %s\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 parsed *\n" + " * before [files]. *\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" + " -after All variable assignments after this will be\n" + " parsed after [files]\n" + " -norecursive Don't do a recursive search\n" + " -recursive Do a recursive search\n" + " -set Set persistent property\n" + " -unset Unset persistent property\n" + " -query Query persistent property. Show all if is empty.\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)" : "", project_builtin_regx().toLatin1().constData(), + default_mode(a0) == Option::QMAKE_GENERATE_MAKEFILE ? " (default)" : "" + ); + return false; +} + +int +Option::parseCommandLine(int argc, char **argv, int skip) +{ + bool before = true; + for(int x = skip; x < argc; x++) { + if(*argv[x] == '-' && strlen(argv[x]) > 1) { /* options */ + QString opt = argv[x] + 1; + + //first param is a mode, or we default + if(x == 1) { + bool specified = true; + if(opt == "project") { + Option::recursive = Option::QMAKE_RECURSIVE_YES; + 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 { + specified = false; + } + if(specified) + continue; + } + //all modes + if(opt == "o" || opt == "output") { + Option::output.setFileName(argv[++x]); + } else if(opt == "after") { + before = false; + } else if(opt == "t" || opt == "template") { + Option::user_template = argv[++x]; + } else if(opt == "tp" || opt == "template_prefix") { + Option::user_template_prefix = argv[++x]; + } else if(opt == "macx") { + fprintf(stderr, "-macx is deprecated.\n"); + Option::host_mode = HOST_MACX_MODE; + Option::target_mode = TARG_MACX_MODE; + Option::target_mode_overridden = true; + } else if(opt == "unix") { + fprintf(stderr, "-unix is deprecated.\n"); + Option::host_mode = HOST_UNIX_MODE; + Option::target_mode = TARG_UNIX_MODE; + Option::target_mode_overridden = true; + } else if(opt == "win32") { + fprintf(stderr, "-win32 is deprecated.\n"); + Option::host_mode = HOST_WIN_MODE; + Option::target_mode = TARG_WIN_MODE; + Option::target_mode_overridden = true; + } else if(opt == "integrity") { + Option::target_mode = TARG_INTEGRITY_MODE; + } else if(opt == "d") { + Option::debug_level++; + } else if(opt == "version" || opt == "v" || opt == "-version") { + fprintf(stdout, + "QMake version %s\n" + "Using Qt version %s in %s\n", + qmake_version(), QT_VERSION_STR, + QLibraryInfo::location(QLibraryInfo::LibrariesPath).toLatin1().constData()); +#ifdef QMAKE_OPENSOURCE_VERSION + fprintf(stdout, "QMake is Open Source software from Nokia Corporation and/or its subsidiary(-ies).\n"); +#endif + return Option::QMAKE_CMDLINE_BAIL; + } else if(opt == "h" || opt == "help") { + return Option::QMAKE_CMDLINE_SHOW_USAGE; + } else if(opt == "Wall") { + Option::warn_level |= WarnAll; + } else if(opt == "Wparser") { + Option::warn_level |= WarnParser; + } else if(opt == "Wlogic") { + Option::warn_level |= WarnLogic; + } else if(opt == "Wdeprecated") { + Option::warn_level |= WarnDeprecated; + } else if(opt == "Wnone") { + Option::warn_level = WarnNone; + } else if(opt == "r" || opt == "recursive") { + Option::recursive = Option::QMAKE_RECURSIVE_YES; + } else if(opt == "nr" || opt == "norecursive") { + Option::recursive = Option::QMAKE_RECURSIVE_NO; + } else if(opt == "config") { + Option::user_configs += argv[++x]; + } else { + if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || + Option::qmake_mode == Option::QMAKE_GENERATE_PRL) { + if(opt == "nodepend" || opt == "nodepends") { + Option::mkfile::do_deps = false; + } else if(opt == "nomoc") { + Option::mkfile::do_mocs = false; + } else if(opt == "nocache") { + Option::mkfile::do_cache = false; + } else if(opt == "createstub") { + Option::mkfile::do_stub_makefile = true; + } else if(opt == "nodependheuristics") { + Option::mkfile::do_dep_heuristics = false; + } else if(opt == "E") { + fprintf(stderr, "-E is deprecated. Use -d instead.\n"); + Option::mkfile::do_preprocess = true; + } else if(opt == "cache") { + Option::mkfile::cachefile = argv[++x]; + } else if(opt == "platform" || opt == "spec") { + Option::mkfile::qmakespec = argv[++x]; + Option::mkfile::qmakespec_commandline = argv[x]; + } else { + fprintf(stderr, "***Unknown option -%s\n", opt.toLatin1().constData()); + return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR; + } + } else if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) { + if(opt == "nopwd") { + Option::projfile::do_pwd = false; + } else { + fprintf(stderr, "***Unknown option -%s\n", opt.toLatin1().constData()); + return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR; + } + } + } + } else { + QString arg = argv[x]; + if(arg.indexOf('=') != -1) { + if(before) + Option::before_user_vars.append(arg); + else + Option::after_user_vars.append(arg); + } 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 proj = detectProjectFile(arg); + if (!proj.isNull()) + arg = proj; + } + 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; + } + } + } + } + + return Option::QMAKE_CMDLINE_SUCCESS; +} + +#ifdef Q_OS_WIN +static QStringList detectShellPath() +{ + QStringList paths; + QString path = qgetenv("PATH"); + QStringList pathlist = path.toLower().split(";"); + for (int i = 0; i < pathlist.count(); i++) { + QString maybeSh = pathlist.at(i) + "/sh.exe"; + if (QFile::exists(maybeSh)) { + paths.append(maybeSh); + } + } + return paths; +} +#endif + +int +Option::init(int argc, char **argv) +{ + Option::application_argv0 = 0; + Option::cpp_moc_mod = ""; + Option::h_moc_mod = "moc_"; + Option::lex_mod = "_lex"; + Option::yacc_mod = "_yacc"; + Option::prl_ext = ".prl"; + Option::libtool_ext = ".la"; + Option::pkgcfg_ext = ".pc"; + Option::prf_ext = ".prf"; + Option::js_ext = ".js"; + Option::ui_ext = ".ui"; + Option::h_ext << ".h" << ".hpp" << ".hh" << ".hxx"; + Option::c_ext << ".c"; +#ifndef Q_OS_WIN + Option::h_ext << ".H"; +#endif + Option::cpp_moc_ext = ".moc"; + Option::h_moc_ext = ".cpp"; + Option::cpp_ext << ".cpp" << ".cc" << ".cxx"; +#ifndef Q_OS_WIN + Option::cpp_ext << ".C"; +#endif + Option::lex_ext = ".l"; + Option::yacc_ext = ".y"; + Option::pro_ext = ".pro"; + Option::mmp_ext = ".mmp"; +#ifdef Q_OS_WIN + Option::dirlist_sep = ";"; + Option::shellPath = detectShellPath(); + Option::res_ext = ".res"; +#else + Option::dirlist_sep = ":"; + Option::shellPath = QStringList("sh"); +#endif + Option::sysenv_mod = "QMAKE_ENV_"; + Option::field_sep = ' '; + + if(argc && argv) { + Option::application_argv0 = argv[0]; + QString argv0 = argv[0]; + if(Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING) + Option::qmake_mode = default_mode(argv0); + if(!argv0.isEmpty() && !QFileInfo(argv0).isRelative()) { + Option::qmake_abslocation = argv0; + } else if (argv0.contains(QLatin1Char('/')) +#ifdef Q_OS_WIN + || argv0.contains(QLatin1Char('\\')) +#endif + ) { //relative PWD + Option::qmake_abslocation = 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(";")); +#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); +#ifdef Q_OS_WIN + candidate += ".exe"; +#endif + if (QFile::exists(candidate)) { + Option::qmake_abslocation = candidate; + break; + } + } + } + if(!Option::qmake_abslocation.isNull()) + Option::qmake_abslocation = QDir::cleanPath(Option::qmake_abslocation); + } else { + Option::qmake_mode = Option::QMAKE_GENERATE_MAKEFILE; + } + + const QByteArray envflags = qgetenv("QMAKEFLAGS"); + if (!envflags.isNull()) { + int env_argc = 0, env_size = 0, currlen=0; + char quote = 0, **env_argv = NULL; + for (int i = 0; i < envflags.size(); ++i) { + if (!quote && (envflags.at(i) == '\'' || envflags.at(i) == '"')) { + quote = envflags.at(i); + } else if (envflags.at(i) == quote) { + quote = 0; + } else if (!quote && envflags.at(i) == ' ') { + if (currlen && env_argv && env_argv[env_argc]) { + env_argv[env_argc][currlen] = '\0'; + currlen = 0; + env_argc++; + } + } else { + if(!env_argv || env_argc > env_size) { + env_argv = (char **)realloc(env_argv, sizeof(char *)*(env_size+=10)); + for(int i2 = env_argc; i2 < env_size; i2++) + env_argv[i2] = NULL; + } + if(!env_argv[env_argc]) { + currlen = 0; + env_argv[env_argc] = (char*)malloc(255); + } + if(currlen < 255) + env_argv[env_argc][currlen++] = envflags.at(i); + } + } + if(env_argv) { + if(env_argv[env_argc]) { + env_argv[env_argc][currlen] = '\0'; + currlen = 0; + env_argc++; + } + parseCommandLine(env_argc, env_argv); + for(int i2 = 0; i2 < env_size; i2++) { + if(env_argv[i2]) + free(env_argv[i2]); + } + free(env_argv); + } + } + if(argc && argv) { + int ret = parseCommandLine(argc, argv, 1); + 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; + } + } + + //last chance for defaults + if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || + Option::qmake_mode == Option::QMAKE_GENERATE_PRL) { + if(Option::mkfile::qmakespec.isNull() || Option::mkfile::qmakespec.isEmpty()) + Option::mkfile::qmakespec = QString::fromLocal8Bit(qgetenv("QMAKESPEC").constData()); + + //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); +#ifndef QT_BUILD_QMAKE_LIBRARY + if(Option::mkfile::project_files.isEmpty()) { + usage(argv[0]); + return Option::QMAKE_CMDLINE_ERROR; + } +#endif + } + } else if (Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) { +#if defined(Q_OS_MAC) + Option::host_mode = Option::HOST_MACX_MODE; + Option::target_mode = Option::TARG_MACX_MODE; +#elif defined(Q_OS_UNIX) + Option::host_mode = Option::HOST_UNIX_MODE; + Option::target_mode = Option::TARG_UNIX_MODE; +#else + Option::host_mode = Option::HOST_WIN_MODE; + Option::target_mode = Option::TARG_WIN_MODE; +#endif + } + + //defaults for globals + if (Option::host_mode != Option::HOST_UNKNOWN_MODE) + applyHostMode(); + return QMAKE_CMDLINE_SUCCESS; +} + +void Option::applyHostMode() +{ + if (Option::host_mode == Option::HOST_WIN_MODE) { + Option::dir_sep = "\\"; + Option::obj_ext = ".obj"; + } else { + Option::dir_sep = "/"; + Option::obj_ext = ".o"; + } +} + +bool Option::postProcessProject(QMakeProject *project) +{ + Option::cpp_ext = project->variables()["QMAKE_EXT_CPP"]; + if(cpp_ext.isEmpty()) + cpp_ext << ".cpp"; //something must be there + Option::h_ext = project->variables()["QMAKE_EXT_H"]; + if(h_ext.isEmpty()) + h_ext << ".h"; + Option::c_ext = project->variables()["QMAKE_EXT_C"]; + if(c_ext.isEmpty()) + c_ext << ".c"; //something must be there + + if(!project->isEmpty("QMAKE_EXT_RES")) + Option::res_ext = project->first("QMAKE_EXT_RES"); + if(!project->isEmpty("QMAKE_EXT_PKGCONFIG")) + Option::pkgcfg_ext = project->first("QMAKE_EXT_PKGCONFIG"); + if(!project->isEmpty("QMAKE_EXT_LIBTOOL")) + Option::libtool_ext = project->first("QMAKE_EXT_LIBTOOL"); + if(!project->isEmpty("QMAKE_EXT_PRL")) + Option::prl_ext = project->first("QMAKE_EXT_PRL"); + if(!project->isEmpty("QMAKE_EXT_PRF")) + Option::prf_ext = project->first("QMAKE_EXT_PRF"); + if(!project->isEmpty("QMAKE_EXT_JS")) + Option::prf_ext = project->first("QMAKE_EXT_JS"); + if(!project->isEmpty("QMAKE_EXT_UI")) + Option::ui_ext = project->first("QMAKE_EXT_UI"); + if(!project->isEmpty("QMAKE_EXT_CPP_MOC")) + Option::cpp_moc_ext = project->first("QMAKE_EXT_CPP_MOC"); + if(!project->isEmpty("QMAKE_EXT_H_MOC")) + Option::h_moc_ext = project->first("QMAKE_EXT_H_MOC"); + if(!project->isEmpty("QMAKE_EXT_LEX")) + Option::lex_ext = project->first("QMAKE_EXT_LEX"); + if(!project->isEmpty("QMAKE_EXT_YACC")) + Option::yacc_ext = project->first("QMAKE_EXT_YACC"); + if(!project->isEmpty("QMAKE_EXT_OBJ")) + Option::obj_ext = project->first("QMAKE_EXT_OBJ"); + if(!project->isEmpty("QMAKE_H_MOD_MOC")) + Option::h_moc_mod = project->first("QMAKE_H_MOD_MOC"); + if(!project->isEmpty("QMAKE_CPP_MOD_MOC")) + Option::cpp_moc_mod = project->first("QMAKE_CPP_MOD_MOC"); + if(!project->isEmpty("QMAKE_MOD_LEX")) + Option::lex_mod = project->first("QMAKE_MOD_LEX"); + if(!project->isEmpty("QMAKE_MOD_YACC")) + Option::yacc_mod = project->first("QMAKE_MOD_YACC"); + if(!project->isEmpty("QMAKE_DIR_SEP")) + Option::dir_sep = project->first("QMAKE_DIR_SEP"); + if(!project->isEmpty("QMAKE_DIRLIST_SEP")) + Option::dirlist_sep = project->first("QMAKE_DIRLIST_SEP"); + if(!project->isEmpty("QMAKE_MOD_SYSTEM_ENV")) + Option::sysenv_mod = project->first("QMAKE_MOD_SYSTEM_ENV"); + return true; +} + +QString +Option::fixString(QString string, uchar flags) +{ + //const QString orig_string = string; + static QHash *cache = 0; + if(!cache) { + cache = new QHash; + qmakeAddCacheClear(qmakeDeleteCacheClear >, (void**)&cache); + } + FixStringCacheKey cacheKey(string, flags); + + QHash::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) { + int rep; + static QRegExp reg_var("\\$\\(.*\\)"); + reg_var.setMinimal(true); + while((rep = reg_var.indexIn(string)) != -1) + string.replace(rep, reg_var.matchedLength(), + QString::fromLocal8Bit(qgetenv(string.mid(rep + 2, reg_var.matchedLength() - 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); + } + + if(string.length() > 2 && string[0].isLetter() && string[1] == QLatin1Char(':')) + string[0] = string[0].toLower(); + + //fix separators + Q_ASSERT(!((flags & Option::FixPathToLocalSeparators) && (flags & Option::FixPathToTargetSeparators))); + if(flags & Option::FixPathToLocalSeparators) { +#if defined(Q_OS_WIN32) + string = string.replace('/', '\\'); +#else + string = string.replace('\\', '/'); +#endif + } else if(flags & Option::FixPathToTargetSeparators) { + string = string.replace('/', Option::dir_sep).replace('\\', Option::dir_sep); + } + + if ((string.startsWith("\"") && string.endsWith("\"")) || + (string.startsWith("\'") && string.endsWith("\'"))) + string = string.mid(1, string.length()-2); + + //cache + //qDebug() << "Fix" << orig_string << "->" << string; + cache->insert(cacheKey, string); + return string; +} + +const char *qmake_version() +{ + static char *ret = NULL; + if(ret) + return ret; + ret = (char *)malloc(15); + qmakeAddCacheClear(qmakeFreeCacheClear, (void**)&ret); +#if defined(_MSC_VER) && _MSC_VER >= 1400 + sprintf_s(ret, 15, "%d.%02d%c", QMAKE_VERSION_MAJOR, QMAKE_VERSION_MINOR, 'a' + QMAKE_VERSION_PATCH); +#else + sprintf(ret, "%d.%02d%c", QMAKE_VERSION_MAJOR, QMAKE_VERSION_MINOR, 'a' + QMAKE_VERSION_PATCH); +#endif + return ret; +} + +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"); +} + +class QMakeCacheClearItem { +private: + qmakeCacheClearFunc func; + void **data; +public: + QMakeCacheClearItem(qmakeCacheClearFunc f, void **d) : func(f), data(d) { } + ~QMakeCacheClearItem() { + (*func)(*data); + *data = 0; + } +}; +static QList cache_items; + +void +qmakeClearCaches() +{ + qDeleteAll(cache_items); + cache_items.clear(); +} + +void +qmakeAddCacheClear(qmakeCacheClearFunc func, void **data) +{ + cache_items.append(new QMakeCacheClearItem(func, data)); +} + +#ifdef Q_OS_WIN +# include + +QT_USE_NAMESPACE +#endif + +QString qmake_libraryInfoFile() +{ + QString ret; +#if defined( Q_OS_WIN ) + wchar_t module_name[MAX_PATH]; + GetModuleFileName(0, module_name, MAX_PATH); + QFileInfo filePath = QString::fromWCharArray(module_name); + ret = filePath.filePath(); +#else + QString argv0 = QFile::decodeName(QByteArray(Option::application_argv0)); + QString absPath; + + if (!argv0.isEmpty() && argv0.at(0) == QLatin1Char('/')) { + /* + If argv0 starts with a slash, it is already an absolute + file path. + */ + absPath = argv0; + } else if (argv0.contains(QLatin1Char('/'))) { + /* + If argv0 contains one or more slashes, it is a file path + relative to the current directory. + */ + absPath = QDir::current().absoluteFilePath(argv0); + } else { + /* + Otherwise, the file path has to be determined using the + PATH environment variable. + */ + QByteArray pEnv = qgetenv("PATH"); + QDir currentDir = QDir::current(); + QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1String(":")); + for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) { + if ((*p).isEmpty()) + continue; + QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + argv0); + QFileInfo candidate_fi(candidate); + if (candidate_fi.exists() && !candidate_fi.isDir()) { + absPath = candidate; + break; + } + } + } + + absPath = QDir::cleanPath(absPath); + + QFileInfo fi(absPath); + ret = fi.exists() ? fi.canonicalFilePath() : QString(); +#endif + if(!ret.isEmpty()) + ret = QDir(QFileInfo(ret).absolutePath()).filePath("qt.conf"); + return ret; +} + +QT_END_NAMESPACE diff --git a/qmake/option.h b/qmake/option.h new file mode 100644 index 0000000000..e17ce5a3a0 --- /dev/null +++ b/qmake/option.h @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OPTION_H +#define OPTION_H + +#include "project.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#define QMAKE_VERSION_MAJOR 2 +#define QMAKE_VERSION_MINOR 1 +#define QMAKE_VERSION_PATCH 0 +const char *qmake_version(); + +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, ...); + +struct Option +{ + //simply global convenience + static QString js_ext; + 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 h_moc_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 cpp_moc_mod; + static QString lex_mod; + static QString yacc_mod; + static QString dir_sep; + static QString dirlist_sep; + static QString sysenv_mod; + static QString pro_ext; + static QString mmp_ext; + static QString res_ext; + static char field_sep; + static const char *application_argv0; + + 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=0); //parse cmdline + static void applyHostMode(); + static bool postProcessProject(QMakeProject *); + + enum StringFixFlags { + FixNone = 0x00, + FixEnvVars = 0x01, + FixPathCanonicalize = 0x02, + FixPathToLocalSeparators = 0x04, + FixPathToTargetSeparators = 0x08 + }; + 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 bool hasFileExtension(const QString &str, const QStringList &extensions) + { + foreach (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 QString qmake_abslocation; + static QFile output; + static QString output_dir; + static int debug_level; + static int warn_level; + enum QMAKE_RECURSIVE { QMAKE_RECURSIVE_DEFAULT, QMAKE_RECURSIVE_YES, QMAKE_RECURSIVE_NO }; + static QMAKE_RECURSIVE recursive; + static QStringList before_user_vars, after_user_vars, user_configs, after_user_configs; + enum HOST_MODE { HOST_UNKNOWN_MODE, HOST_UNIX_MODE, HOST_WIN_MODE, HOST_MACX_MODE }; + static HOST_MODE host_mode; + enum TARG_MODE { TARG_UNKNOWN_MODE, TARG_UNIX_MODE, TARG_WIN_MODE, TARG_MACX_MODE, + TARG_SYMBIAN_MODE, TARG_INTEGRITY_MODE }; + static TARG_MODE target_mode; + static bool target_mode_overridden; + static QString user_template, user_template_prefix; + static QStringList shellPath; + + //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 QString qmakespec; + static bool do_cache; + static bool do_deps; + static bool do_mocs; + static bool do_dep_heuristics; + static bool do_preprocess; + static bool do_stub_makefile; + static QString cachefile; + static int cachefile_depth; + static QStringList project_files; + static QString qmakespec_commandline; + }; + +private: + static int parseCommandLine(int, char **, int=0); +}; + +inline QString fixEnvVariables(const QString &x) { return Option::fixString(x, Option::FixEnvVars); } +inline QStringList splitPathList(const QString &paths) { return paths.split(Option::dirlist_sep); } + +// this is a stripped down version of the one found in QtCore +class QLibraryInfo +{ +public: + enum LibraryLocation + { + PrefixPath, + DocumentationPath, + HeadersPath, + LibrariesPath, + BinariesPath, + PluginsPath, + DataPath, + TranslationsPath, + SettingsPath, + DemosPath, + ExamplesPath, + ImportsPath + }; + static QString location(LibraryLocation); +}; + +QT_END_NAMESPACE + +#endif // OPTION_H diff --git a/qmake/project.cpp b/qmake/project.cpp new file mode 100644 index 0000000000..d4fa786ac5 --- /dev/null +++ b/qmake/project.cpp @@ -0,0 +1,3170 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "project.h" +#include "property.h" +#include "option.h" +#include "cachekeys.h" +#include "generators/metamakefile.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef Q_OS_UNIX +#include +#include +#elif defined(Q_OS_WIN32) +#include +#endif +#include +#include + +// Included from tools/shared +#include + +#ifdef Q_OS_WIN32 +#define QT_POPEN _popen +#define QT_PCLOSE _pclose +#else +#define QT_POPEN popen +#define QT_PCLOSE pclose +#endif + +QT_BEGIN_NAMESPACE + +//expand functions +enum ExpandFunc { E_MEMBER=1, E_FIRST, E_LAST, E_CAT, E_FROMFILE, E_EVAL, E_LIST, + E_SPRINTF, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION, + E_FIND, E_SYSTEM, E_UNIQUE, E_QUOTE, E_ESCAPE_EXPAND, + E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE, E_REPLACE, + E_SIZE, E_GENERATE_UID }; +QMap qmake_expandFunctions() +{ + static QMap *qmake_expand_functions = 0; + if(!qmake_expand_functions) { + qmake_expand_functions = new QMap; + qmakeAddCacheClear(qmakeDeleteCacheClear >, (void**)&qmake_expand_functions); + qmake_expand_functions->insert("member", E_MEMBER); + qmake_expand_functions->insert("first", E_FIRST); + qmake_expand_functions->insert("last", E_LAST); + qmake_expand_functions->insert("cat", E_CAT); + qmake_expand_functions->insert("fromfile", E_FROMFILE); + qmake_expand_functions->insert("eval", E_EVAL); + qmake_expand_functions->insert("list", E_LIST); + qmake_expand_functions->insert("sprintf", E_SPRINTF); + qmake_expand_functions->insert("join", E_JOIN); + qmake_expand_functions->insert("split", E_SPLIT); + qmake_expand_functions->insert("basename", E_BASENAME); + qmake_expand_functions->insert("dirname", E_DIRNAME); + qmake_expand_functions->insert("section", E_SECTION); + qmake_expand_functions->insert("find", E_FIND); + qmake_expand_functions->insert("system", E_SYSTEM); + qmake_expand_functions->insert("unique", E_UNIQUE); + qmake_expand_functions->insert("quote", E_QUOTE); + qmake_expand_functions->insert("escape_expand", E_ESCAPE_EXPAND); + qmake_expand_functions->insert("upper", E_UPPER); + qmake_expand_functions->insert("lower", E_LOWER); + qmake_expand_functions->insert("re_escape", E_RE_ESCAPE); + qmake_expand_functions->insert("files", E_FILES); + qmake_expand_functions->insert("prompt", E_PROMPT); + qmake_expand_functions->insert("replace", E_REPLACE); + qmake_expand_functions->insert("size", E_SIZE); + qmake_expand_functions->insert("generate_uid", E_GENERATE_UID); + } + return *qmake_expand_functions; +} +//replace functions +enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS, + T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM, + T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE, + T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_ERROR, + T_MESSAGE, T_WARNING, T_IF, T_OPTION }; +QMap qmake_testFunctions() +{ + static QMap *qmake_test_functions = 0; + if(!qmake_test_functions) { + qmake_test_functions = new QMap; + qmake_test_functions->insert("requires", T_REQUIRES); + qmake_test_functions->insert("greaterThan", T_GREATERTHAN); + qmake_test_functions->insert("lessThan", T_LESSTHAN); + qmake_test_functions->insert("equals", T_EQUALS); + qmake_test_functions->insert("isEqual", T_EQUALS); + qmake_test_functions->insert("exists", T_EXISTS); + qmake_test_functions->insert("export", T_EXPORT); + qmake_test_functions->insert("clear", T_CLEAR); + qmake_test_functions->insert("unset", T_UNSET); + qmake_test_functions->insert("eval", T_EVAL); + qmake_test_functions->insert("CONFIG", T_CONFIG); + qmake_test_functions->insert("if", T_IF); + qmake_test_functions->insert("isActiveConfig", T_CONFIG); + qmake_test_functions->insert("system", T_SYSTEM); + qmake_test_functions->insert("return", T_RETURN); + qmake_test_functions->insert("break", T_BREAK); + qmake_test_functions->insert("next", T_NEXT); + qmake_test_functions->insert("defined", T_DEFINED); + qmake_test_functions->insert("contains", T_CONTAINS); + qmake_test_functions->insert("infile", T_INFILE); + qmake_test_functions->insert("count", T_COUNT); + qmake_test_functions->insert("isEmpty", T_ISEMPTY); + qmake_test_functions->insert("include", T_INCLUDE); + qmake_test_functions->insert("load", T_LOAD); + qmake_test_functions->insert("debug", T_DEBUG); + qmake_test_functions->insert("error", T_ERROR); + qmake_test_functions->insert("message", T_MESSAGE); + qmake_test_functions->insert("warning", T_WARNING); + qmake_test_functions->insert("option", T_OPTION); + } + return *qmake_test_functions; +} + +struct parser_info { + QString file; + int line_no; + bool from_file; +} parser; + +static QString remove_quotes(const QString &arg) +{ + const ushort SINGLEQUOTE = '\''; + const ushort DOUBLEQUOTE = '"'; + + const QChar *arg_data = arg.data(); + const ushort first = arg_data->unicode(); + const int arg_len = arg.length(); + if(first == SINGLEQUOTE || first == DOUBLEQUOTE) { + const ushort last = (arg_data+arg_len-1)->unicode(); + if(last == first) + return arg.mid(1, arg_len-2); + } + return arg; +} + +static QString varMap(const QString &x) +{ + QString ret(x); + if(ret == "INTERFACES") + ret = "FORMS"; + else if(ret == "QMAKE_POST_BUILD") + ret = "QMAKE_POST_LINK"; + else if(ret == "TARGETDEPS") + ret = "POST_TARGETDEPS"; + else if(ret == "LIBPATH") + ret = "QMAKE_LIBDIR"; + else if(ret == "QMAKE_EXT_MOC") + ret = "QMAKE_EXT_CPP_MOC"; + else if(ret == "QMAKE_MOD_MOC") + ret = "QMAKE_H_MOD_MOC"; + else if(ret == "QMAKE_LFLAGS_SHAPP") + ret = "QMAKE_LFLAGS_APP"; + else if(ret == "PRECOMPH") + ret = "PRECOMPILED_HEADER"; + else if(ret == "PRECOMPCPP") + ret = "PRECOMPILED_SOURCE"; + else if(ret == "INCPATH") + ret = "INCLUDEPATH"; + else if(ret == "QMAKE_EXTRA_WIN_COMPILERS" || ret == "QMAKE_EXTRA_UNIX_COMPILERS") + ret = "QMAKE_EXTRA_COMPILERS"; + else if(ret == "QMAKE_EXTRA_WIN_TARGETS" || ret == "QMAKE_EXTRA_UNIX_TARGETS") + ret = "QMAKE_EXTRA_TARGETS"; + else if(ret == "QMAKE_EXTRA_UNIX_INCLUDES") + ret = "QMAKE_EXTRA_INCLUDES"; + else if(ret == "QMAKE_EXTRA_UNIX_VARIABLES") + ret = "QMAKE_EXTRA_VARIABLES"; + else if(ret == "QMAKE_RPATH") + ret = "QMAKE_LFLAGS_RPATH"; + else if(ret == "QMAKE_FRAMEWORKDIR") + ret = "QMAKE_FRAMEWORKPATH"; + else if(ret == "QMAKE_FRAMEWORKDIR_FLAGS") + ret = "QMAKE_FRAMEWORKPATH_FLAGS"; + else + return ret; + warn_msg(WarnDeprecated, "%s:%d: Variable %s is deprecated; use %s instead.", + parser.file.toLatin1().constData(), parser.line_no, + x.toLatin1().constData(), ret.toLatin1().constData()); + return ret; +} + +static QStringList split_arg_list(const QString ¶ms) +{ + int quote = 0; + QStringList args; + + const ushort LPAREN = '('; + const ushort RPAREN = ')'; + const ushort SINGLEQUOTE = '\''; + const ushort DOUBLEQUOTE = '"'; + const ushort BACKSLASH = '\\'; + const ushort COMMA = ','; + const ushort SPACE = ' '; + //const ushort TAB = '\t'; + + const QChar *params_data = params.data(); + const int params_len = params.length(); + for(int last = 0; ;) { + while(last < params_len && (params_data[last].unicode() == SPACE + /*|| params_data[last].unicode() == TAB*/)) + ++last; + for(int x = last, parens = 0; ; x++) { + if(x == params_len) { + while(x > last && params_data[x-1].unicode() == SPACE) + --x; + args << params.mid(last, x - last); + // Could do a check for unmatched parens here, but split_value_list() + // is called on all our output, so mistakes will be caught anyway. + return args; + } + ushort unicode = params_data[x].unicode(); + if(x != (int)params_len-1 && unicode == BACKSLASH && + (params_data[x+1].unicode() == SINGLEQUOTE || params_data[x+1].unicode() == DOUBLEQUOTE)) { + x++; //get that 'escape' + } else if(quote && unicode == quote) { + quote = 0; + } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) { + quote = unicode; + } else if(unicode == RPAREN) { + --parens; + } else if(unicode == LPAREN) { + ++parens; + } + if(!parens && !quote && unicode == COMMA) { + int prev = last; + last = x+1; + while(x > prev && params_data[x-1].unicode() == SPACE) + --x; + args << params.mid(prev, x - prev); + break; + } + } + } +} + +static QStringList split_value_list(const QString &vals) +{ + QString build; + QStringList ret; + ushort quote = 0; + int parens = 0; + + const ushort LPAREN = '('; + const ushort RPAREN = ')'; + const ushort SINGLEQUOTE = '\''; + const ushort DOUBLEQUOTE = '"'; + const ushort BACKSLASH = '\\'; + + ushort unicode; + const QChar *vals_data = vals.data(); + const int vals_len = vals.length(); + for(int x = 0; x < vals_len; x++) { + unicode = vals_data[x].unicode(); + if(x != (int)vals_len-1 && unicode == BACKSLASH && + (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) { + build += vals_data[x++]; //get that 'escape' + } else if(quote && unicode == quote) { + quote = 0; + } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) { + quote = unicode; + } else if(unicode == RPAREN) { + --parens; + } else if(unicode == LPAREN) { + ++parens; + } + + if(!parens && !quote && (vals_data[x] == Option::field_sep)) { + ret << build; + build.clear(); + } else { + build += vals_data[x]; + } + } + if(!build.isEmpty()) + ret << build; + if (parens) + warn_msg(WarnDeprecated, "%s:%d: Unmatched parentheses are deprecated.", + parser.file.toLatin1().constData(), parser.line_no); + // Could do a check for unmatched quotes here, but doVariableReplaceExpand() + // is called on all our output, so mistakes will be caught anyway. + return ret; +} + +//just a parsable entity +struct ParsableBlock +{ + ParsableBlock() : ref_cnt(1) { } + virtual ~ParsableBlock() { } + + struct Parse { + QString text; + parser_info pi; + Parse(const QString &t) : text(t){ pi = parser; } + }; + QList parselist; + + inline int ref() { return ++ref_cnt; } + inline int deref() { return --ref_cnt; } + +protected: + int ref_cnt; + virtual bool continueBlock() = 0; + bool eval(QMakeProject *p, QMap &place); +}; + +bool ParsableBlock::eval(QMakeProject *p, QMap &place) +{ + //save state + parser_info pi = parser; + const int block_count = p->scope_blocks.count(); + + //execute + bool ret = true; + for(int i = 0; i < parselist.count(); i++) { + parser = parselist.at(i).pi; + if(!(ret = p->parse(parselist.at(i).text, place)) || !continueBlock()) + break; + } + + //restore state + parser = pi; + while(p->scope_blocks.count() > block_count) + p->scope_blocks.pop(); + return ret; +} + +//defined functions +struct FunctionBlock : public ParsableBlock +{ + FunctionBlock() : calling_place(0), scope_level(1), cause_return(false) { } + + QMap vars; + QMap *calling_place; + QStringList return_value; + int scope_level; + bool cause_return; + + bool exec(const QList &args, + QMakeProject *p, QMap &place, QStringList &functionReturn); + virtual bool continueBlock() { return !cause_return; } +}; + +bool FunctionBlock::exec(const QList &args, + QMakeProject *proj, QMap &place, + QStringList &functionReturn) +{ + //save state +#if 1 + calling_place = &place; +#else + calling_place = &proj->variables(); +#endif + return_value.clear(); + cause_return = false; + + //execute +#if 0 + vars = proj->variables(); // should be place so that local variables can be inherited +#else + vars = place; +#endif + vars["ARGS"].clear(); + for(int i = 0; i < args.count(); i++) { + vars["ARGS"] += args[i]; + vars[QString::number(i+1)] = args[i]; + } + bool ret = ParsableBlock::eval(proj, vars); + functionReturn = return_value; + + //restore state + calling_place = 0; + return_value.clear(); + vars.clear(); + return ret; +} + +//loops +struct IteratorBlock : public ParsableBlock +{ + IteratorBlock() : scope_level(1), loop_forever(false), cause_break(false), cause_next(false) { } + + int scope_level; + + struct Test { + QString func; + QStringList args; + bool invert; + parser_info pi; + Test(const QString &f, QStringList &a, bool i) : func(f), args(a), invert(i) { pi = parser; } + }; + QList test; + + QString variable; + + bool loop_forever, cause_break, cause_next; + QStringList list; + + bool exec(QMakeProject *p, QMap &place); + virtual bool continueBlock() { return !cause_next && !cause_break; } +}; +bool IteratorBlock::exec(QMakeProject *p, QMap &place) +{ + bool ret = true; + QStringList::Iterator it; + if(!loop_forever) + it = list.begin(); + int iterate_count = 0; + //save state + IteratorBlock *saved_iterator = p->iterator; + p->iterator = this; + + //do the loop + while(loop_forever || it != list.end()) { + cause_next = cause_break = false; + if(!loop_forever && (*it).isEmpty()) { //ignore empty items + ++it; + continue; + } + + //set up the loop variable + QStringList va; + if(!variable.isEmpty()) { + va = place[variable]; + if(loop_forever) + place[variable] = QStringList(QString::number(iterate_count)); + else + place[variable] = QStringList(*it); + } + //do the iterations + bool succeed = true; + for(QList::Iterator test_it = test.begin(); test_it != test.end(); ++test_it) { + parser = (*test_it).pi; + succeed = p->doProjectTest((*test_it).func, (*test_it).args, place); + if((*test_it).invert) + succeed = !succeed; + if(!succeed) + break; + } + if(succeed) + ret = ParsableBlock::eval(p, place); + //restore the variable in the map + if(!variable.isEmpty()) + place[variable] = va; + //loop counters + if(!loop_forever) + ++it; + iterate_count++; + if(!ret || cause_break) + break; + } + + //restore state + p->iterator = saved_iterator; + return ret; +} + +QMakeProject::ScopeBlock::~ScopeBlock() +{ +#if 0 + if(iterate) + delete iterate; +#endif +} + +static void qmake_error_msg(const QString &msg) +{ + fprintf(stderr, "%s:%d: %s\n", parser.file.toLatin1().constData(), parser.line_no, + msg.toLatin1().constData()); +} + +/* + 1) environment variable QMAKEFEATURES (as separated by colons) + 2) property variable QMAKEFEATURES (as separated by colons) + 3) (where .qmake.cache lives) + FEATURES_DIR + 4) environment variable QMAKEPATH (as separated by colons) + /mkspecs/FEATURES_DIR + 5) your QMAKESPEC/features dir + 6) your data_install/mkspecs/FEATURES_DIR + 7) your QMAKESPEC/../FEATURES_DIR dir + + FEATURES_DIR is defined as: + + 1) features/(unix|win32|macx)/ + 2) features/ +*/ +QStringList qmake_feature_paths(QMakeProperty *prop=0) +{ + QStringList concat; + { + const QString base_concat = QDir::separator() + QString("features"); + switch(Option::target_mode) { + case Option::TARG_MACX_MODE: //also a unix + concat << base_concat + QDir::separator() + "mac"; + concat << base_concat + QDir::separator() + "macx"; + concat << base_concat + QDir::separator() + "unix"; + break; + default: // Can't happen, just make the compiler shut up + case Option::TARG_UNIX_MODE: + concat << base_concat + QDir::separator() + "unix"; + break; + case Option::TARG_WIN_MODE: + concat << base_concat + QDir::separator() + "win32"; + break; + case Option::TARG_SYMBIAN_MODE: + concat << base_concat + QDir::separator() + "symbian"; + break; + } + concat << base_concat; + } + const QString mkspecs_concat = QDir::separator() + QString("mkspecs"); + QStringList feature_roots; + QByteArray mkspec_path = qgetenv("QMAKEFEATURES"); + if(!mkspec_path.isNull()) + feature_roots += splitPathList(QString::fromLocal8Bit(mkspec_path)); + if(prop) + feature_roots += splitPathList(prop->value("QMAKEFEATURES")); + if(!Option::mkfile::cachefile.isEmpty()) { + QString path; + int last_slash = Option::mkfile::cachefile.lastIndexOf(QDir::separator()); + if(last_slash != -1) + path = Option::fixPathToLocalOS(Option::mkfile::cachefile.left(last_slash), false); + for(QStringList::Iterator concat_it = concat.begin(); + concat_it != concat.end(); ++concat_it) + feature_roots << (path + (*concat_it)); + } + QByteArray qmakepath = qgetenv("QMAKEPATH"); + if (!qmakepath.isNull()) { + const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath)); + for(QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) { + for(QStringList::Iterator concat_it = concat.begin(); + concat_it != concat.end(); ++concat_it) + feature_roots << ((*it) + mkspecs_concat + (*concat_it)); + } + } + if(!Option::mkfile::qmakespec.isEmpty()) + feature_roots << Option::mkfile::qmakespec + QDir::separator() + "features"; + if(!Option::mkfile::qmakespec.isEmpty()) { + QFileInfo specfi(Option::mkfile::qmakespec); + QDir specdir(specfi.absoluteFilePath()); + while(!specdir.isRoot()) { + if(!specdir.cdUp() || specdir.isRoot()) + break; + if(QFile::exists(specdir.path() + QDir::separator() + "features")) { + for(QStringList::Iterator concat_it = concat.begin(); + concat_it != concat.end(); ++concat_it) + feature_roots << (specdir.path() + (*concat_it)); + break; + } + } + } + for(QStringList::Iterator concat_it = concat.begin(); + concat_it != concat.end(); ++concat_it) + feature_roots << (QLibraryInfo::location(QLibraryInfo::PrefixPath) + + mkspecs_concat + (*concat_it)); + for(QStringList::Iterator concat_it = concat.begin(); + concat_it != concat.end(); ++concat_it) + feature_roots << (QLibraryInfo::location(QLibraryInfo::DataPath) + + mkspecs_concat + (*concat_it)); + return feature_roots; +} + +QStringList qmake_mkspec_paths() +{ + QStringList ret; + const QString concat = QDir::separator() + QString("mkspecs"); + QByteArray qmakepath = qgetenv("QMAKEPATH"); + if (!qmakepath.isEmpty()) { + const QStringList lst = splitPathList(QString::fromLocal8Bit(qmakepath)); + for(QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) + ret << ((*it) + concat); + } + ret << QLibraryInfo::location(QLibraryInfo::DataPath) + concat; + + return ret; +} + +QMakeProject::~QMakeProject() +{ + if(own_prop) + delete prop; + for(QMap::iterator it = replaceFunctions.begin(); it != replaceFunctions.end(); ++it) { + if(!it.value()->deref()) + delete it.value(); + } + replaceFunctions.clear(); + for(QMap::iterator it = testFunctions.begin(); it != testFunctions.end(); ++it) { + if(!it.value()->deref()) + delete it.value(); + } + testFunctions.clear(); +} + + +void +QMakeProject::init(QMakeProperty *p, const QMap *vars) +{ + if(vars) + base_vars = *vars; + if(!p) { + prop = new QMakeProperty; + own_prop = true; + } else { + prop = p; + own_prop = false; + } + recursive = false; + reset(); +} + +QMakeProject::QMakeProject(QMakeProject *p, const QMap *vars) +{ + init(p->properties(), vars ? vars : &p->variables()); + for(QMap::iterator it = p->replaceFunctions.begin(); it != p->replaceFunctions.end(); ++it) { + it.value()->ref(); + replaceFunctions.insert(it.key(), it.value()); + } + for(QMap::iterator it = p->testFunctions.begin(); it != p->testFunctions.end(); ++it) { + it.value()->ref(); + testFunctions.insert(it.key(), it.value()); + } +} + +void +QMakeProject::reset() +{ + // scope_blocks starts with one non-ignoring entity + scope_blocks.clear(); + scope_blocks.push(ScopeBlock()); + iterator = 0; + function = 0; + backslashWarned = false; +} + +bool +QMakeProject::parse(const QString &t, QMap &place, int numLines) +{ + // To preserve the integrity of any UTF-8 characters in .pro file, temporarily replace the + // non-breaking space (0xA0) characters with another non-space character, so that + // QString::simplified() call will not replace it with space. + // Note: There won't be any two byte characters in .pro files, so 0x10A0 should be a safe + // replacement character. + static QChar nbsp(0xA0); + static QChar nbspFix(0x01A0); + QString s; + if (t.indexOf(nbsp) != -1) { + s = t; + s.replace(nbsp, nbspFix); + s = s.simplified(); + s.replace(nbspFix, nbsp); + } else { + s = t.simplified(); + } + + int hash_mark = s.indexOf("#"); + if(hash_mark != -1) //good bye comments + s = s.left(hash_mark); + if(s.isEmpty()) // blank_line + return true; + + if(scope_blocks.top().ignore) { + bool continue_parsing = false; + // adjust scope for each block which appears on a single line + for(int i = 0; i < s.length(); i++) { + if(s[i] == '{') { + scope_blocks.push(ScopeBlock(true)); + } else if(s[i] == '}') { + if(scope_blocks.count() == 1) { + fprintf(stderr, "Braces mismatch %s:%d\n", parser.file.toLatin1().constData(), parser.line_no); + return false; + } + ScopeBlock sb = scope_blocks.pop(); + if(sb.iterate) { + sb.iterate->exec(this, place); + delete sb.iterate; + sb.iterate = 0; + } + if(!scope_blocks.top().ignore) { + debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(), + parser.line_no, scope_blocks.count()+1); + s = s.mid(i+1).trimmed(); + continue_parsing = !s.isEmpty(); + break; + } + } + } + if(!continue_parsing) { + debug_msg(1, "Project Parser: %s:%d : Ignored due to block being false.", + parser.file.toLatin1().constData(), parser.line_no); + return true; + } + } + + if(function) { + QString append; + int d_off = 0; + const QChar *d = s.unicode(); + bool function_finished = false; + while(d_off < s.length()) { + if(*(d+d_off) == QLatin1Char('}')) { + function->scope_level--; + if(!function->scope_level) { + function_finished = true; + break; + } + } else if(*(d+d_off) == QLatin1Char('{')) { + function->scope_level++; + } + append += *(d+d_off); + ++d_off; + } + if(!append.isEmpty()) + function->parselist.append(IteratorBlock::Parse(append)); + if(function_finished) { + function = 0; + s = QString(d+d_off, s.length()-d_off); + } else { + return true; + } + } else if(IteratorBlock *it = scope_blocks.top().iterate) { + QString append; + int d_off = 0; + const QChar *d = s.unicode(); + bool iterate_finished = false; + while(d_off < s.length()) { + if(*(d+d_off) == QLatin1Char('}')) { + it->scope_level--; + if(!it->scope_level) { + iterate_finished = true; + break; + } + } else if(*(d+d_off) == QLatin1Char('{')) { + it->scope_level++; + } + append += *(d+d_off); + ++d_off; + } + if(!append.isEmpty()) + scope_blocks.top().iterate->parselist.append(IteratorBlock::Parse(append)); + if(iterate_finished) { + scope_blocks.top().iterate = 0; + bool ret = it->exec(this, place); + delete it; + if(!ret) + return false; + s = s.mid(d_off); + } else { + return true; + } + } + + QString scope, var, op; + QStringList val; +#define SKIP_WS(d, o, l) while(o < l && (*(d+o) == QLatin1Char(' ') || *(d+o) == QLatin1Char('\t'))) ++o + const QChar *d = s.unicode(); + int d_off = 0; + SKIP_WS(d, d_off, s.length()); + IteratorBlock *iterator = 0; + bool scope_failed = false, else_line = false, or_op=false; + QChar quote = 0; + int parens = 0, scope_count=0, start_block = 0; + while(d_off < s.length()) { + if(!parens) { + if(*(d+d_off) == QLatin1Char('=')) + break; + if(*(d+d_off) == QLatin1Char('+') || *(d+d_off) == QLatin1Char('-') || + *(d+d_off) == QLatin1Char('*') || *(d+d_off) == QLatin1Char('~')) { + if(*(d+d_off+1) == QLatin1Char('=')) { + break; + } else if(*(d+d_off+1) == QLatin1Char(' ')) { + const QChar *k = d+d_off+1; + int k_off = 0; + SKIP_WS(k, k_off, s.length()-d_off); + if(*(k+k_off) == QLatin1Char('=')) { + QString msg; + qmake_error_msg(QString(d+d_off, 1) + "must be followed immediately by ="); + return false; + } + } + } + } + + if(!quote.isNull()) { + if(*(d+d_off) == quote) + quote = QChar(); + } else if(*(d+d_off) == '(') { + ++parens; + } else if(*(d+d_off) == ')') { + --parens; + } else if(*(d+d_off) == '"' /*|| *(d+d_off) == '\''*/) { + quote = *(d+d_off); + } + + if(!parens && quote.isNull() && + (*(d+d_off) == QLatin1Char(':') || *(d+d_off) == QLatin1Char('{') || + *(d+d_off) == QLatin1Char(')') || *(d+d_off) == QLatin1Char('|'))) { + scope_count++; + scope = var.trimmed(); + if(*(d+d_off) == QLatin1Char(')')) + scope += *(d+d_off); // need this + var = ""; + + bool test = scope_failed; + if(scope.isEmpty()) { + test = true; + } else if(scope.toLower() == "else") { //else is a builtin scope here as it modifies state + if(scope_count != 1 || scope_blocks.top().else_status == ScopeBlock::TestNone) { + qmake_error_msg(("Unexpected " + scope + " ('" + s + "')").toLatin1()); + return false; + } + else_line = true; + test = (scope_blocks.top().else_status == ScopeBlock::TestSeek); + debug_msg(1, "Project Parser: %s:%d : Else%s %s.", parser.file.toLatin1().constData(), parser.line_no, + scope == "else" ? "" : QString(" (" + scope + ")").toLatin1().constData(), + test ? "considered" : "excluded"); + } else { + QString comp_scope = scope; + bool invert_test = (comp_scope.at(0) == QLatin1Char('!')); + if(invert_test) + comp_scope = comp_scope.mid(1); + int lparen = comp_scope.indexOf('('); + if(or_op == scope_failed) { + if(lparen != -1) { // if there is an lparen in the scope, it IS a function + int rparen = comp_scope.lastIndexOf(')'); + if(rparen == -1) { + qmake_error_msg("Function missing right paren: " + comp_scope); + return false; + } + QString func = comp_scope.left(lparen); + QStringList args = split_arg_list(comp_scope.mid(lparen+1, rparen - lparen - 1)); + if(function) { + fprintf(stderr, "%s:%d: No tests can come after a function definition!\n", + parser.file.toLatin1().constData(), parser.line_no); + return false; + } else if(func == "for") { //for is a builtin function here, as it modifies state + if(args.count() > 2 || args.count() < 1) { + fprintf(stderr, "%s:%d: for(iterate, list) requires two arguments.\n", + parser.file.toLatin1().constData(), parser.line_no); + return false; + } else if(iterator) { + fprintf(stderr, "%s:%d unexpected nested for()\n", + parser.file.toLatin1().constData(), parser.line_no); + return false; + } + + iterator = new IteratorBlock; + QString it_list; + if(args.count() == 1) { + doVariableReplace(args[0], place); + it_list = args[0]; + if(args[0] != "ever") { + delete iterator; + iterator = 0; + fprintf(stderr, "%s:%d: for(iterate, list) requires two arguments.\n", + parser.file.toLatin1().constData(), parser.line_no); + return false; + } + it_list = "forever"; + } else if(args.count() == 2) { + iterator->variable = args[0]; + doVariableReplace(args[1], place); + it_list = args[1]; + } + QStringList list = place[it_list]; + if(list.isEmpty()) { + if(it_list == "forever") { + iterator->loop_forever = true; + } else { + int dotdot = it_list.indexOf(".."); + if(dotdot != -1) { + bool ok; + int start = it_list.left(dotdot).toInt(&ok); + if(ok) { + int end = it_list.mid(dotdot+2).toInt(&ok); + if(ok) { + if(start < end) { + for(int i = start; i <= end; i++) + list << QString::number(i); + } else { + for(int i = start; i >= end; i--) + list << QString::number(i); + } + } + } + } + } + } + iterator->list = list; + test = !invert_test; + } else if(iterator) { + iterator->test.append(IteratorBlock::Test(func, args, invert_test)); + test = !invert_test; + } else if(func == "defineTest" || func == "defineReplace") { + if(!function_blocks.isEmpty()) { + fprintf(stderr, + "%s:%d: cannot define a function within another definition.\n", + parser.file.toLatin1().constData(), parser.line_no); + return false; + } + if(args.count() != 1) { + fprintf(stderr, "%s:%d: %s(function_name) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData()); + return false; + } + QMap *map = 0; + if(func == "defineTest") + map = &testFunctions; + else + map = &replaceFunctions; +#if 0 + if(!map || map->contains(args[0])) { + fprintf(stderr, "%s:%d: Function[%s] multiply defined.\n", + parser.file.toLatin1().constData(), parser.line_no, args[0].toLatin1().constData()); + return false; + } +#endif + function = new FunctionBlock; + map->insert(args[0], function); + test = true; + } else { + test = doProjectTest(func, args, place); + if(*(d+d_off) == QLatin1Char(')') && d_off == s.length()-1) { + if(invert_test) + test = !test; + scope_blocks.top().else_status = + (test ? ScopeBlock::TestFound : ScopeBlock::TestSeek); + return true; // assume we are done + } + } + } else { + QString cscope = comp_scope.trimmed(); + doVariableReplace(cscope, place); + test = isActiveConfig(cscope.trimmed(), true, &place); + } + if(invert_test) + test = !test; + } + } + if(!test && !scope_failed) + debug_msg(1, "Project Parser: %s:%d : Test (%s) failed.", parser.file.toLatin1().constData(), + parser.line_no, scope.toLatin1().constData()); + if(test == or_op) + scope_failed = !test; + or_op = (*(d+d_off) == QLatin1Char('|')); + + if(*(d+d_off) == QLatin1Char('{')) { // scoping block + start_block++; + if(iterator) { + for(int off = 0, braces = 0; true; ++off) { + if(*(d+d_off+off) == QLatin1Char('{')) + ++braces; + else if(*(d+d_off+off) == QLatin1Char('}') && braces) + --braces; + if(!braces || d_off+off == s.length()) { + iterator->parselist.append(s.mid(d_off, off-1)); + if(braces > 1) + iterator->scope_level += braces-1; + d_off += off-1; + break; + } + } + } + } + } else if(!parens && *(d+d_off) == QLatin1Char('}')) { + if(start_block) { + --start_block; + } else if(!scope_blocks.count()) { + warn_msg(WarnParser, "Possible braces mismatch %s:%d", parser.file.toLatin1().constData(), parser.line_no); + } else { + if(scope_blocks.count() == 1) { + fprintf(stderr, "Braces mismatch %s:%d\n", parser.file.toLatin1().constData(), parser.line_no); + return false; + } + debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(), + parser.line_no, scope_blocks.count()); + ScopeBlock sb = scope_blocks.pop(); + if(sb.iterate) + sb.iterate->exec(this, place); + } + } else { + var += *(d+d_off); + } + ++d_off; + } + var = var.trimmed(); + + if(!else_line || (else_line && !scope_failed)) + scope_blocks.top().else_status = (!scope_failed ? ScopeBlock::TestFound : ScopeBlock::TestSeek); + if(start_block) { + ScopeBlock next_block(scope_failed); + next_block.iterate = iterator; + if(iterator) + next_block.else_status = ScopeBlock::TestNone; + else if(scope_failed) + next_block.else_status = ScopeBlock::TestSeek; + else + next_block.else_status = ScopeBlock::TestFound; + scope_blocks.push(next_block); + debug_msg(1, "Project Parser: %s:%d : Entering block %d (%d). [%s]", parser.file.toLatin1().constData(), + parser.line_no, scope_blocks.count(), scope_failed, s.toLatin1().constData()); + } else if(iterator) { + iterator->parselist.append(var+s.mid(d_off)); + bool ret = iterator->exec(this, place); + delete iterator; + return ret; + } + + if((!scope_count && !var.isEmpty()) || (scope_count == 1 && else_line)) + scope_blocks.top().else_status = ScopeBlock::TestNone; + if(d_off == s.length()) { + if(!var.trimmed().isEmpty()) + qmake_error_msg(("Parse Error ('" + s + "')").toLatin1()); + return var.isEmpty(); // allow just a scope + } + + SKIP_WS(d, d_off, s.length()); + for(; d_off < s.length() && op.indexOf('=') == -1; op += *(d+(d_off++))) + ; + op.replace(QRegExp("\\s"), ""); + + SKIP_WS(d, d_off, s.length()); + QString vals = s.mid(d_off); // vals now contains the space separated list of values + int rbraces = vals.count('}'), lbraces = vals.count('{'); + if(scope_blocks.count() > 1 && rbraces - lbraces == 1 && vals.endsWith('}')) { + debug_msg(1, "Project Parser: %s:%d : Leaving block %d", parser.file.toLatin1().constData(), + parser.line_no, scope_blocks.count()); + ScopeBlock sb = scope_blocks.pop(); + if(sb.iterate) + sb.iterate->exec(this, place); + vals.truncate(vals.length()-1); + } else if(rbraces != lbraces) { + warn_msg(WarnParser, "Possible braces mismatch {%s} %s:%d", + vals.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no); + } + if(scope_failed) + return true; // oh well +#undef SKIP_WS + + doVariableReplace(var, place); + var = varMap(var); //backwards compatibility + if(!var.isEmpty() && Option::mkfile::do_preprocess) { + static QString last_file("*none*"); + if(parser.file != last_file) { + fprintf(stdout, "#file %s:%d\n", parser.file.toLatin1().constData(), parser.line_no); + last_file = parser.file; + } + fprintf(stdout, "%s %s %s\n", var.toLatin1().constData(), op.toLatin1().constData(), vals.toLatin1().constData()); + } + + if(vals.contains('=') && numLines > 1) + warn_msg(WarnParser, "Possible accidental line continuation: {%s} at %s:%d", + var.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no); + + QStringList &varlist = place[var]; // varlist is the list in the symbol table + + if(Option::debug_level >= 1) { + QString tmp_vals = vals; + doVariableReplace(tmp_vals, place); + debug_msg(1, "Project Parser: %s:%d :%s: :%s: (%s)", parser.file.toLatin1().constData(), parser.line_no, + var.toLatin1().constData(), op.toLatin1().constData(), tmp_vals.toLatin1().constData()); + } + + // now do the operation + if(op == "~=") { + doVariableReplace(vals, place); + if(vals.length() < 4 || vals.at(0) != 's') { + qmake_error_msg(("~= operator only can handle s/// function ('" + + s + "')").toLatin1()); + return false; + } + QChar sep = vals.at(1); + QStringList func = vals.split(sep); + if(func.count() < 3 || func.count() > 4) { + qmake_error_msg(("~= operator only can handle s/// function ('" + + s + "')").toLatin1()); + return false; + } + bool global = false, case_sense = true, quote = false; + if(func.count() == 4) { + global = func[3].indexOf('g') != -1; + case_sense = func[3].indexOf('i') == -1; + quote = func[3].indexOf('q') != -1; + } + QString from = func[1], to = func[2]; + if(quote) + from = QRegExp::escape(from); + QRegExp regexp(from, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive); + for(QStringList::Iterator varit = varlist.begin(); varit != varlist.end();) { + if((*varit).contains(regexp)) { + (*varit) = (*varit).replace(regexp, to); + if ((*varit).isEmpty()) + varit = varlist.erase(varit); + else + ++varit; + if(!global) + break; + } else + ++varit; + } + } else { + QStringList vallist; + { + //doVariableReplace(vals, place); + QStringList tmp = split_value_list(vals); + for(int i = 0; i < tmp.size(); ++i) + vallist += doVariableReplaceExpand(tmp[i], place); + } + + if(op == "=") { + if(!varlist.isEmpty()) { + bool send_warning = false; + if(var != "TEMPLATE" && var != "TARGET") { + QSet incoming_vals = vallist.toSet(); + for(int i = 0; i < varlist.size(); ++i) { + const QString var = varlist.at(i).trimmed(); + if(!var.isEmpty() && !incoming_vals.contains(var)) { + send_warning = true; + break; + } + } + } + if(send_warning) + warn_msg(WarnParser, "Operator=(%s) clears variables previously set: %s:%d", + var.toLatin1().constData(), parser.file.toLatin1().constData(), parser.line_no); + } + varlist.clear(); + } + for(QStringList::ConstIterator valit = vallist.begin(); + valit != vallist.end(); ++valit) { + if((*valit).isEmpty()) + continue; + if((op == "*=" && !varlist.contains((*valit))) || + op == "=" || op == "+=") + varlist.append((*valit)); + else if(op == "-=") + varlist.removeAll((*valit)); + } + if(var == "REQUIRES") // special case to get communicated to backends! + doProjectCheckReqs(vallist, place); + } + return true; +} + +bool +QMakeProject::read(QTextStream &file, QMap &place) +{ + int numLines = 0; + bool ret = true; + QString s; + while(!file.atEnd()) { + parser.line_no++; + QString line = file.readLine().trimmed(); + int prelen = line.length(); + + int hash_mark = line.indexOf("#"); + if(hash_mark != -1) //good bye comments + line = line.left(hash_mark).trimmed(); + if(!line.isEmpty() && line.right(1) == "\\") { + if(!line.startsWith("#")) { + line.truncate(line.length() - 1); + s += line + Option::field_sep; + ++numLines; + } + } else if(!line.isEmpty() || (line.isEmpty() && !prelen)) { + if(s.isEmpty() && line.isEmpty()) + continue; + if(!line.isEmpty()) { + s += line; + ++numLines; + } + if(!s.isEmpty()) { + if(!(ret = parse(s, place, numLines))) { + s = ""; + numLines = 0; + break; + } + s = ""; + numLines = 0; + } + } + } + if (!s.isEmpty()) + ret = parse(s, place, numLines); + return ret; +} + +bool +QMakeProject::read(const QString &file, QMap &place) +{ + parser_info pi = parser; + reset(); + + const QString oldpwd = qmake_getpwd(); + QString filename = Option::fixPathToLocalOS(file, false); + bool ret = false, using_stdin = false; + QFile qfile; + if(!strcmp(filename.toLatin1(), "-")) { + qfile.setFileName(""); + ret = qfile.open(stdin, QIODevice::ReadOnly); + using_stdin = true; + } else if(QFileInfo(file).isDir()) { + return false; + } else { + qfile.setFileName(filename); + ret = qfile.open(QIODevice::ReadOnly); + qmake_setpwd(QFileInfo(filename).absolutePath()); + } + if(ret) { + parser_info pi = parser; + parser.from_file = true; + parser.file = filename; + parser.line_no = 0; + QTextStream t(&qfile); + ret = read(t, place); + if(!using_stdin) + qfile.close(); + } + if(scope_blocks.count() != 1) { + qmake_error_msg("Unterminated conditional block at end of file"); + ret = false; + } + parser = pi; + qmake_setpwd(oldpwd); + return ret; +} + +bool +QMakeProject::read(const QString &project, uchar cmd) +{ + pfile = QFileInfo(project).absoluteFilePath(); + return read(cmd); +} + +bool +QMakeProject::read(uchar cmd) +{ + if(cfile.isEmpty()) { + // hack to get the Option stuff in there + base_vars["QMAKE_EXT_CPP"] = Option::cpp_ext; + base_vars["QMAKE_EXT_C"] = Option::c_ext; + base_vars["QMAKE_EXT_H"] = Option::h_ext; + base_vars["QMAKE_SH"] = Option::shellPath; + if(!Option::user_template_prefix.isEmpty()) + base_vars["TEMPLATE_PREFIX"] = QStringList(Option::user_template_prefix); + + if(cmd & ReadCache && Option::mkfile::do_cache) { // parse the cache + int cache_depth = -1; + QString qmake_cache = Option::mkfile::cachefile; + if(qmake_cache.isEmpty()) { //find it as it has not been specified + QString dir = QDir::toNativeSeparators(Option::output_dir); + while(!QFile::exists((qmake_cache = dir + QDir::separator() + ".qmake.cache"))) { + dir = dir.left(dir.lastIndexOf(QDir::separator())); + if(dir.isEmpty() || dir.indexOf(QDir::separator()) == -1) { + qmake_cache = ""; + break; + } + if(cache_depth == -1) + cache_depth = 1; + else + cache_depth++; + } + } else { + QString abs_cache = QFileInfo(Option::mkfile::cachefile).absoluteDir().path(); + if(Option::output_dir.startsWith(abs_cache)) + cache_depth = Option::output_dir.mid(abs_cache.length()).count('/'); + } + if(!qmake_cache.isEmpty()) { + if(read(qmake_cache, cache)) { + Option::mkfile::cachefile_depth = cache_depth; + Option::mkfile::cachefile = qmake_cache; + if(Option::mkfile::qmakespec.isEmpty() && !cache["QMAKESPEC"].isEmpty()) + Option::mkfile::qmakespec = cache["QMAKESPEC"].first(); + } + } + } + if(cmd & ReadConf) { // parse mkspec + QString qmakespec = fixEnvVariables(Option::mkfile::qmakespec); + QStringList mkspec_roots = qmake_mkspec_paths(); + debug_msg(2, "Looking for mkspec %s in (%s)", qmakespec.toLatin1().constData(), + mkspec_roots.join("::").toLatin1().constData()); + if(qmakespec.isEmpty()) { + for(QStringList::ConstIterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) { + QString mkspec = (*it) + QDir::separator() + "default"; + QFileInfo default_info(mkspec); + if(default_info.exists() && default_info.isDir()) { + qmakespec = mkspec; + break; + } + } + if(qmakespec.isEmpty()) { + fprintf(stderr, "QMAKESPEC has not been set, so configuration cannot be deduced.\n"); + return false; + } + Option::mkfile::qmakespec = qmakespec; + } + + if(QDir::isRelativePath(qmakespec)) { + if (QFile::exists(Option::output_dir+"/"+qmakespec+"/qmake.conf")) { + qmakespec = Option::mkfile::qmakespec = QFileInfo(Option::output_dir+"/"+qmakespec).absoluteFilePath(); + } else if (QFile::exists(qmakespec+"/qmake.conf")) { + Option::mkfile::qmakespec = QFileInfo(Option::mkfile::qmakespec).absoluteFilePath(); + } else { + bool found_mkspec = false; + for(QStringList::ConstIterator it = mkspec_roots.begin(); it != mkspec_roots.end(); ++it) { + QString mkspec = (*it) + QDir::separator() + qmakespec; + if(QFile::exists(mkspec)) { + found_mkspec = true; + Option::mkfile::qmakespec = qmakespec = mkspec; + break; + } + } + if(!found_mkspec) { + fprintf(stderr, "Could not find mkspecs for your QMAKESPEC(%s) after trying:\n\t%s\n", + qmakespec.toLatin1().constData(), mkspec_roots.join("\n\t").toLatin1().constData()); + return false; + } + } + } + + // parse qmake configuration + while(qmakespec.endsWith(QString(QChar(QDir::separator())))) + qmakespec.truncate(qmakespec.length()-1); + QString spec = qmakespec + QDir::separator() + "qmake.conf"; + debug_msg(1, "QMAKESPEC conf: reading %s", spec.toLatin1().constData()); + if(!read(spec, base_vars)) { + fprintf(stderr, "Failure to read QMAKESPEC conf file %s.\n", spec.toLatin1().constData()); + return false; + } + validateModes(); + + if(Option::mkfile::do_cache && !Option::mkfile::cachefile.isEmpty()) { + debug_msg(1, "QMAKECACHE file: reading %s", Option::mkfile::cachefile.toLatin1().constData()); + read(Option::mkfile::cachefile, base_vars); + } + } + + if(cmd & ReadFeatures) { + debug_msg(1, "Processing default_pre: %s", vars["CONFIG"].join("::").toLatin1().constData()); + if(doProjectInclude("default_pre", IncludeFlagFeature, base_vars) == IncludeNoExist) + doProjectInclude("default", IncludeFlagFeature, base_vars); + } + } + + vars = base_vars; // start with the base + + //get a default + if(pfile != "-" && vars["TARGET"].isEmpty()) + vars["TARGET"].append(QFileInfo(pfile).baseName()); + + //before commandline + if(cmd & ReadCmdLine) { + cfile = pfile; + parser.file = "(internal)"; + parser.from_file = false; + parser.line_no = 1; //really arg count now.. duh + reset(); + for(QStringList::ConstIterator it = Option::before_user_vars.begin(); + it != Option::before_user_vars.end(); ++it) { + if(!parse((*it), vars)) { + fprintf(stderr, "Argument failed to parse: %s\n", (*it).toLatin1().constData()); + return false; + } + parser.line_no++; + } + } + + //commandline configs + if(cmd & ReadConfigs && !Option::user_configs.isEmpty()) { + parser.file = "(configs)"; + parser.from_file = false; + parser.line_no = 1; //really arg count now.. duh + parse("CONFIG += " + Option::user_configs.join(" "), vars); + } + + if(cmd & ReadProFile) { // parse project file + debug_msg(1, "Project file: reading %s", pfile.toLatin1().constData()); + if(pfile != "-" && !QFile::exists(pfile) && !pfile.endsWith(Option::pro_ext)) + pfile += Option::pro_ext; + if(!read(pfile, vars)) + return false; + } + + if(cmd & ReadCmdLine) { + parser.file = "(internal)"; + parser.from_file = false; + parser.line_no = 1; //really arg count now.. duh + reset(); + for(QStringList::ConstIterator it = Option::after_user_vars.begin(); + it != Option::after_user_vars.end(); ++it) { + if(!parse((*it), vars)) { + fprintf(stderr, "Argument failed to parse: %s\n", (*it).toLatin1().constData()); + return false; + } + parser.line_no++; + } + } + + //after configs (set in BUILDS) + if(cmd & ReadConfigs && !Option::after_user_configs.isEmpty()) { + parser.file = "(configs)"; + parser.from_file = false; + parser.line_no = 1; //really arg count now.. duh + parse("CONFIG += " + Option::after_user_configs.join(" "), vars); + } + + if(pfile != "-" && vars["TARGET"].isEmpty()) + vars["TARGET"].append(QFileInfo(pfile).baseName()); + + if(cmd & ReadConfigs && !Option::user_configs.isEmpty()) { + parser.file = "(configs)"; + parser.from_file = false; + parser.line_no = 1; //really arg count now.. duh + parse("CONFIG += " + Option::user_configs.join(" "), base_vars); + } + + if(cmd & ReadFeatures) { + debug_msg(1, "Processing default_post: %s", vars["CONFIG"].join("::").toLatin1().constData()); + doProjectInclude("default_post", IncludeFlagFeature, vars); + + QHash processed; + const QStringList &configs = vars["CONFIG"]; + debug_msg(1, "Processing CONFIG features: %s", configs.join("::").toLatin1().constData()); + while(1) { + bool finished = true; + for(int i = configs.size()-1; i >= 0; --i) { + const QString config = configs[i].toLower(); + if(!processed.contains(config)) { + processed.insert(config, true); + if(doProjectInclude(config, IncludeFlagFeature, vars) == IncludeSuccess) { + finished = false; + break; + } + } + } + if(finished) + break; + } + } + Option::postProcessProject(this); // let Option post-process + return true; +} + +void QMakeProject::validateModes() +{ + if (Option::host_mode == Option::HOST_UNKNOWN_MODE + || Option::target_mode == Option::TARG_UNKNOWN_MODE) { + Option::HOST_MODE host_mode; + Option::TARG_MODE target_mode; + const QStringList &gen = base_vars.value("MAKEFILE_GENERATOR"); + if (gen.isEmpty()) { + fprintf(stderr, "%s:%d: Using OS scope before setting MAKEFILE_GENERATOR\n", + parser.file.toLatin1().constData(), parser.line_no); + } else if (MetaMakefileGenerator::modesForGenerator(gen.first(), + &host_mode, &target_mode)) { + if (Option::host_mode == Option::HOST_UNKNOWN_MODE) { + Option::host_mode = host_mode; + Option::applyHostMode(); + } + + if (Option::target_mode == Option::TARG_UNKNOWN_MODE) { + const QStringList &tgt = base_vars.value("TARGET_PLATFORM"); + if (!tgt.isEmpty()) { + const QString &os = tgt.first(); + if (os == "unix") + Option::target_mode = Option::TARG_UNIX_MODE; + else if (os == "macx") + Option::target_mode = Option::TARG_MACX_MODE; + else if (os == "symbian") + Option::target_mode = Option::TARG_SYMBIAN_MODE; + else if (os == "win32") + Option::target_mode = Option::TARG_WIN_MODE; + else + fprintf(stderr, "Unknown target platform specified: %s\n", + os.toLatin1().constData()); + } else { + Option::target_mode = target_mode; + } + } + } + } +} + +bool +QMakeProject::isActiveConfig(const QString &x, bool regex, QMap *place) +{ + if(x.isEmpty()) + return true; + + //magic types for easy flipping + if(x == "true") + return true; + else if(x == "false") + return false; + + if (x == "unix") { + validateModes(); + return Option::target_mode == Option::TARG_UNIX_MODE + || Option::target_mode == Option::TARG_MACX_MODE + || Option::target_mode == Option::TARG_SYMBIAN_MODE; + } else if (x == "macx" || x == "mac") { + validateModes(); + return Option::target_mode == Option::TARG_MACX_MODE; + } else if (x == "symbian") { + validateModes(); + return Option::target_mode == Option::TARG_SYMBIAN_MODE; + } else if (x == "win32") { + validateModes(); + return Option::target_mode == Option::TARG_WIN_MODE; + } + + //mkspecs + static QString spec; + if(spec.isEmpty()) + spec = QFileInfo(Option::mkfile::qmakespec).fileName(); + QRegExp re(x, Qt::CaseSensitive, QRegExp::Wildcard); + if((regex && re.exactMatch(spec)) || (!regex && spec == x)) + return true; +#ifdef Q_OS_UNIX + else if(spec == "default") { + static char *buffer = NULL; + if(!buffer) { + buffer = (char *)malloc(1024); + qmakeAddCacheClear(qmakeFreeCacheClear, (void**)&buffer); + } + int l = readlink(Option::mkfile::qmakespec.toLatin1(), buffer, 1024); + if(l != -1) { + buffer[l] = '\0'; + QString r = buffer; + if(r.lastIndexOf('/') != -1) + r = r.mid(r.lastIndexOf('/') + 1); + if((regex && re.exactMatch(r)) || (!regex && r == x)) + return true; + } + } +#elif defined(Q_OS_WIN) + else if(spec == "default") { + // We can't resolve symlinks as they do on Unix, so configure.exe puts the source of the + // qmake.conf at the end of the default/qmake.conf in the QMAKESPEC_ORG variable. + const QStringList &spec_org = (place ? (*place)["QMAKESPEC_ORIGINAL"] + : vars["QMAKESPEC_ORIGINAL"]); + if (!spec_org.isEmpty()) { + spec = spec_org.at(0); + int lastSlash = spec.lastIndexOf('/'); + if(lastSlash != -1) + spec = spec.mid(lastSlash + 1); + if((regex && re.exactMatch(spec)) || (!regex && spec == x)) + return true; + } + } +#endif + + //simple matching + const QStringList &configs = (place ? (*place)["CONFIG"] : vars["CONFIG"]); + for(QStringList::ConstIterator it = configs.begin(); it != configs.end(); ++it) { + if(((regex && re.exactMatch((*it))) || (!regex && (*it) == x)) && re.exactMatch((*it))) + return true; + } + return false; +} + +bool +QMakeProject::doProjectTest(QString str, QMap &place) +{ + QString chk = remove_quotes(str); + if(chk.isEmpty()) + return true; + bool invert_test = (chk.left(1) == "!"); + if(invert_test) + chk = chk.mid(1); + + bool test=false; + int lparen = chk.indexOf('('); + if(lparen != -1) { // if there is an lparen in the chk, it IS a function + int rparen = chk.indexOf(')', lparen); + if(rparen == -1) { + qmake_error_msg("Function missing right paren: " + chk); + } else { + QString func = chk.left(lparen); + test = doProjectTest(func, chk.mid(lparen+1, rparen - lparen - 1), place); + } + } else { + test = isActiveConfig(chk, true, &place); + } + if(invert_test) + return !test; + return test; +} + +bool +QMakeProject::doProjectTest(QString func, const QString ¶ms, + QMap &place) +{ + return doProjectTest(func, split_arg_list(params), place); +} + +QMakeProject::IncludeStatus +QMakeProject::doProjectInclude(QString file, uchar flags, QMap &place) +{ + enum { UnknownFormat, ProFormat, JSFormat } format = UnknownFormat; + if(flags & IncludeFlagFeature) { + if(!file.endsWith(Option::prf_ext)) + file += Option::prf_ext; + validateModes(); // init dir_sep + if(file.indexOf(Option::dir_sep) == -1 || !QFile::exists(file)) { + static QStringList *feature_roots = 0; + if(!feature_roots) { + feature_roots = new QStringList(qmake_feature_paths(prop)); + qmakeAddCacheClear(qmakeDeleteCacheClear, (void**)&feature_roots); + } + debug_msg(2, "Looking for feature '%s' in (%s)", file.toLatin1().constData(), + feature_roots->join("::").toLatin1().constData()); + int start_root = 0; + if(parser.from_file) { + QFileInfo currFile(parser.file), prfFile(file); + if(currFile.fileName() == prfFile.fileName()) { + currFile = QFileInfo(currFile.canonicalFilePath()); + for(int root = 0; root < feature_roots->size(); ++root) { + prfFile = QFileInfo(feature_roots->at(root) + + QDir::separator() + file).canonicalFilePath(); + if(prfFile == currFile) { + start_root = root+1; + break; + } + } + } + } + for(int root = start_root; root < feature_roots->size(); ++root) { + QString prf(feature_roots->at(root) + QDir::separator() + file); + if(QFile::exists(prf + Option::js_ext)) { + format = JSFormat; + file = prf + Option::js_ext; + break; + } else if(QFile::exists(prf)) { + format = ProFormat; + file = prf; + break; + } + } + if(format == UnknownFormat) + return IncludeNoExist; + } + if(place["QMAKE_INTERNAL_INCLUDED_FEATURES"].indexOf(file) != -1) + return IncludeFeatureAlreadyLoaded; + place["QMAKE_INTERNAL_INCLUDED_FEATURES"].append(file); + } + if(QDir::isRelativePath(file)) { + QStringList include_roots; + if(Option::output_dir != qmake_getpwd()) + include_roots << qmake_getpwd(); + include_roots << Option::output_dir; + for(int root = 0; root < include_roots.size(); ++root) { + QString testName = QDir::toNativeSeparators(include_roots[root]); + if (!testName.endsWith(QString(QDir::separator()))) + testName += QDir::separator(); + testName += file; + if(QFile::exists(testName)) { + file = testName; + break; + } + } + } + if(format == UnknownFormat) { + if(QFile::exists(file)) { + if(file.endsWith(Option::js_ext)) + format = JSFormat; + else + format = ProFormat; + } else { + return IncludeNoExist; + } + } + if(Option::mkfile::do_preprocess) //nice to see this first.. + fprintf(stderr, "#switching file %s(%s) - %s:%d\n", (flags & IncludeFlagFeature) ? "load" : "include", + file.toLatin1().constData(), + parser.file.toLatin1().constData(), parser.line_no); + debug_msg(1, "Project Parser: %s'ing file %s.", (flags & IncludeFlagFeature) ? "load" : "include", + file.toLatin1().constData()); + + QString orig_file = file; + int di = file.lastIndexOf(QDir::separator()); + QString oldpwd = qmake_getpwd(); + if(di != -1) { + if(!qmake_setpwd(file.left(file.lastIndexOf(QDir::separator())))) { + fprintf(stderr, "Cannot find directory: %s\n", file.left(di).toLatin1().constData()); + return IncludeFailure; + } + } + bool parsed = false; + parser_info pi = parser; + if(format == JSFormat) { + warn_msg(WarnParser, "%s:%d: QtScript support disabled for %s.", + pi.file.toLatin1().constData(), pi.line_no, orig_file.toLatin1().constData()); + } else { + QStack sc = scope_blocks; + IteratorBlock *it = iterator; + FunctionBlock *fu = function; + if(flags & (IncludeFlagNewProject|IncludeFlagNewParser)) { + // The "project's variables" are used in other places (eg. export()) so it's not + // possible to use "place" everywhere. Instead just set variables and grab them later + QMakeProject proj(this, &place); + if(flags & IncludeFlagNewParser) { +#if 1 + if(proj.doProjectInclude("default_pre", IncludeFlagFeature, proj.variables()) == IncludeNoExist) + proj.doProjectInclude("default", IncludeFlagFeature, proj.variables()); +#endif + parsed = proj.read(file, proj.variables()); // parse just that file (fromfile, infile) + } else { + parsed = proj.read(file); // parse all aux files (load/include into) + } + place = proj.variables(); + } else { + parsed = read(file, place); + } + iterator = it; + function = fu; + scope_blocks = sc; + } + if(parsed) { + if(place["QMAKE_INTERNAL_INCLUDED_FILES"].indexOf(orig_file) == -1) + place["QMAKE_INTERNAL_INCLUDED_FILES"].append(orig_file); + } else { + warn_msg(WarnParser, "%s:%d: Failure to include file %s.", + pi.file.toLatin1().constData(), pi.line_no, orig_file.toLatin1().constData()); + } + parser = pi; + qmake_setpwd(oldpwd); + if(!parsed) + return IncludeParseFailure; + return IncludeSuccess; +} + +QStringList +QMakeProject::doProjectExpand(QString func, const QString ¶ms, + QMap &place) +{ + return doProjectExpand(func, split_arg_list(params), place); +} + +QStringList +QMakeProject::doProjectExpand(QString func, QStringList args, + QMap &place) +{ + QList args_list; + for(int i = 0; i < args.size(); ++i) { + QStringList arg = split_value_list(args[i]), tmp; + for(int i = 0; i < arg.size(); ++i) + tmp += doVariableReplaceExpand(arg[i], place);; + args_list += tmp; + } + return doProjectExpand(func, args_list, place); +} + +// defined in symbian generator +extern QString generate_test_uid(const QString& target); + +QStringList +QMakeProject::doProjectExpand(QString func, QList args_list, + QMap &place) +{ + func = func.trimmed(); + if(replaceFunctions.contains(func)) { + FunctionBlock *defined = replaceFunctions[func]; + function_blocks.push(defined); + QStringList ret; + defined->exec(args_list, this, place, ret); + Q_ASSERT(function_blocks.pop() == defined); + return ret; + } + + QStringList args; //why don't the builtin functions just use args_list? --Sam + for(int i = 0; i < args_list.size(); ++i) + args += args_list[i].join(QString(Option::field_sep)); + + ExpandFunc func_t = qmake_expandFunctions().value(func); + if (!func_t && (func_t = qmake_expandFunctions().value(func.toLower()))) + warn_msg(WarnDeprecated, "%s:%d: Using uppercased builtin functions is deprecated.", + parser.file.toLatin1().constData(), parser.line_no); + debug_msg(1, "Running project expand: %s(%s) [%d]", + func.toLatin1().constData(), args.join("::").toLatin1().constData(), func_t); + + QStringList ret; + switch(func_t) { + case E_MEMBER: { + if(args.count() < 1 || args.count() > 3) { + fprintf(stderr, "%s:%d: member(var, start, end) requires three arguments.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + bool ok = true; + const QStringList &var = values(args.first(), place); + int start = 0, end = 0; + if(args.count() >= 2) { + QString start_str = args[1]; + start = start_str.toInt(&ok); + if(!ok) { + if(args.count() == 2) { + int dotdot = start_str.indexOf(".."); + if(dotdot != -1) { + start = start_str.left(dotdot).toInt(&ok); + if(ok) + end = start_str.mid(dotdot+2).toInt(&ok); + } + } + if(!ok) + fprintf(stderr, "%s:%d: member() argument 2 (start) '%s' invalid.\n", + parser.file.toLatin1().constData(), parser.line_no, + start_str.toLatin1().constData()); + } else { + end = start; + if(args.count() == 3) + end = args[2].toInt(&ok); + if(!ok) + fprintf(stderr, "%s:%d: member() argument 3 (end) '%s' invalid.\n", + parser.file.toLatin1().constData(), parser.line_no, + args[2].toLatin1().constData()); + } + } + if(ok) { + if(start < 0) + start += var.count(); + if(end < 0) + end += var.count(); + if(start < 0 || start >= var.count() || end < 0 || end >= var.count()) { + //nothing + } else if(start < end) { + for(int i = start; i <= end && (int)var.count() >= i; i++) + ret += var[i]; + } else { + for(int i = start; i >= end && (int)var.count() >= i && i >= 0; i--) + ret += var[i]; + } + } + } + break; } + case E_FIRST: + case E_LAST: { + if(args.count() != 1) { + fprintf(stderr, "%s:%d: %s(var) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData()); + } else { + const QStringList &var = values(args.first(), place); + if(!var.isEmpty()) { + if(func_t == E_FIRST) + ret = QStringList(var[0]); + else + ret = QStringList(var[var.size()-1]); + } + } + break; } + case E_CAT: { + if(args.count() < 1 || args.count() > 2) { + fprintf(stderr, "%s:%d: cat(file) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + QString file = args[0]; + file = Option::fixPathToLocalOS(file); + + bool singleLine = true; + if(args.count() > 1) + singleLine = (args[1].toLower() == "true"); + + QFile qfile(file); + if(qfile.open(QIODevice::ReadOnly)) { + QTextStream stream(&qfile); + while(!stream.atEnd()) { + ret += split_value_list(stream.readLine().trimmed()); + if(!singleLine) + ret += "\n"; + } + qfile.close(); + } + } + break; } + case E_FROMFILE: { + if(args.count() != 2) { + fprintf(stderr, "%s:%d: fromfile(file, variable) requires two arguments.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + QString file = args[0], seek_var = args[1]; + file = Option::fixPathToLocalOS(file); + + QMap tmp; + if(doProjectInclude(file, IncludeFlagNewParser, tmp) == IncludeSuccess) { + if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES")) { + QStringList &out = place["QMAKE_INTERNAL_INCLUDED_FILES"]; + const QStringList &in = tmp["QMAKE_INTERNAL_INCLUDED_FILES"]; + for(int i = 0; i < in.size(); ++i) { + if(out.indexOf(in[i]) == -1) + out += in[i]; + } + } + ret = tmp[seek_var]; + } + } + break; } + case E_EVAL: { + if(args.count() < 1 || args.count() > 2) { + fprintf(stderr, "%s:%d: eval(variable) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no); + + } else { + const QMap *source = &place; + if(args.count() == 2) { + if(args.at(1) == "Global") { + source = &vars; + } else if(args.at(1) == "Local") { + source = &place; + } else { + fprintf(stderr, "%s:%d: unexpected source to eval.\n", parser.file.toLatin1().constData(), + parser.line_no); + } + } + ret += source->value(args.at(0)); + } + break; } + case E_LIST: { + static int x = 0; + QString tmp; + tmp.sprintf(".QMAKE_INTERNAL_TMP_VAR_%d", x++); + ret = QStringList(tmp); + QStringList &lst = (*((QMap*)&place))[tmp]; + lst.clear(); + for(QStringList::ConstIterator arg_it = args.begin(); + arg_it != args.end(); ++arg_it) + lst += split_value_list((*arg_it)); + break; } + case E_SPRINTF: { + if(args.count() < 1) { + fprintf(stderr, "%s:%d: sprintf(format, ...) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + QString tmp = args.at(0); + for(int i = 1; i < args.count(); ++i) + tmp = tmp.arg(args.at(i)); + ret = split_value_list(tmp); + } + break; } + case E_JOIN: { + if(args.count() < 1 || args.count() > 4) { + fprintf(stderr, "%s:%d: join(var, glue, before, after) requires four" + "arguments.\n", parser.file.toLatin1().constData(), parser.line_no); + } else { + QString glue, before, after; + if(args.count() >= 2) + glue = args[1]; + if(args.count() >= 3) + before = args[2]; + if(args.count() == 4) + after = args[3]; + const QStringList &var = values(args.first(), place); + if(!var.isEmpty()) + ret = split_value_list(before + var.join(glue) + after); + } + break; } + case E_SPLIT: { + if(args.count() < 1 || args.count() > 2) { + fprintf(stderr, "%s:%d split(var, sep) requires one or two arguments\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + QString sep = QString(Option::field_sep); + if(args.count() >= 2) + sep = args[1]; + QStringList var = values(args.first(), place); + for(QStringList::ConstIterator vit = var.begin(); vit != var.end(); ++vit) { + QStringList lst = (*vit).split(sep); + for(QStringList::ConstIterator spltit = lst.begin(); spltit != lst.end(); ++spltit) + ret += (*spltit); + } + } + break; } + case E_BASENAME: + case E_DIRNAME: + case E_SECTION: { + bool regexp = false; + QString sep, var; + int beg=0, end=-1; + if(func_t == E_SECTION) { + if(args.count() != 3 && args.count() != 4) { + fprintf(stderr, "%s:%d section(var, sep, begin, end) requires three argument\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + var = args[0]; + sep = args[1]; + beg = args[2].toInt(); + if(args.count() == 4) + end = args[3].toInt(); + } + } else { + if(args.count() != 1) { + fprintf(stderr, "%s:%d %s(var) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no, func.toLatin1().constData()); + } else { + var = args[0]; + regexp = true; + sep = "[" + QRegExp::escape(Option::dir_sep) + "/]"; + if(func_t == E_DIRNAME) + end = -2; + else + beg = -1; + } + } + if(!var.isNull()) { + const QStringList &l = values(var, place); + for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) { + QString separator = sep; + if(regexp) + ret += (*it).section(QRegExp(separator), beg, end); + else + ret += (*it).section(separator, beg, end); + } + } + break; } + case E_FIND: { + if(args.count() != 2) { + fprintf(stderr, "%s:%d find(var, str) requires two arguments\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + QRegExp regx(args[1]); + const QStringList &var = values(args.first(), place); + for(QStringList::ConstIterator vit = var.begin(); + vit != var.end(); ++vit) { + if(regx.indexIn(*vit) != -1) + ret += (*vit); + } + } + break; } + case E_SYSTEM: { + if(args.count() < 1 || args.count() > 2) { + fprintf(stderr, "%s:%d system(execut) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + char buff[256]; + bool singleLine = true; + if(args.count() > 1) + singleLine = (args[1].toLower() == "true"); + QString output; + FILE *proc = QT_POPEN(args[0].toLatin1(), "r"); + while(proc && !feof(proc)) { + int read_in = int(fread(buff, 1, 255, proc)); + if(!read_in) + break; + for(int i = 0; i < read_in; i++) { + if((singleLine && buff[i] == '\n') || buff[i] == '\t') + buff[i] = ' '; + } + buff[read_in] = '\0'; + output += buff; + } + ret += split_value_list(output); + if(proc) + QT_PCLOSE(proc); + } + break; } + case E_UNIQUE: { + if(args.count() != 1) { + fprintf(stderr, "%s:%d unique(var) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + const QStringList &var = values(args.first(), place); + for(int i = 0; i < var.count(); i++) { + if(!ret.contains(var[i])) + ret.append(var[i]); + } + } + break; } + case E_QUOTE: + ret = args; + break; + case E_ESCAPE_EXPAND: { + for(int i = 0; i < args.size(); ++i) { + QChar *i_data = args[i].data(); + int i_len = args[i].length(); + for(int x = 0; x < i_len; ++x) { + if(*(i_data+x) == '\\' && x < i_len-1) { + if(*(i_data+x+1) == '\\') { + ++x; + } else { + struct { + char in, out; + } mapped_quotes[] = { + { 'n', '\n' }, + { 't', '\t' }, + { 'r', '\r' }, + { 0, 0 } + }; + for(int i = 0; mapped_quotes[i].in; ++i) { + if(*(i_data+x+1) == mapped_quotes[i].in) { + *(i_data+x) = mapped_quotes[i].out; + if(x < i_len-2) + memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar)); + --i_len; + break; + } + } + } + } + } + ret.append(QString(i_data, i_len)); + } + break; } + case E_RE_ESCAPE: { + for(int i = 0; i < args.size(); ++i) + ret += QRegExp::escape(args[i]); + break; } + case E_UPPER: + case E_LOWER: { + for(int i = 0; i < args.size(); ++i) { + if(func_t == E_UPPER) + ret += args[i].toUpper(); + else + ret += args[i].toLower(); + } + break; } + case E_FILES: { + if(args.count() != 1 && args.count() != 2) { + fprintf(stderr, "%s:%d files(pattern) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + bool recursive = false; + if(args.count() == 2) + recursive = (args[1].toLower() == "true" || args[1].toInt()); + QStringList dirs; + QString r = Option::fixPathToLocalOS(args[0]); + int slash = r.lastIndexOf(QDir::separator()); + if(slash != -1) { + dirs.append(r.left(slash)); + r = r.mid(slash+1); + } else { + dirs.append(""); + } + + const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard); + for(int d = 0; d < dirs.count(); d++) { + QString dir = dirs[d]; + if(!dir.isEmpty() && !dir.endsWith(QDir::separator())) + dir += "/"; + + QDir qdir(dir); + for(int i = 0; i < (int)qdir.count(); ++i) { + if(qdir[i] == "." || qdir[i] == "..") + continue; + QString fname = dir + qdir[i]; + if(QFileInfo(fname).isDir()) { + if(recursive) + dirs.append(fname); + } + if(regex.exactMatch(qdir[i])) + ret += fname; + } + } + } + break; } + case E_PROMPT: { + if(args.count() != 1) { + fprintf(stderr, "%s:%d prompt(question) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else if(pfile == "-") { + fprintf(stderr, "%s:%d prompt(question) cannot be used when '-o -' is used.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + QString msg = fixEnvVariables(args.first()); + if(!msg.endsWith("?")) + msg += "?"; + fprintf(stderr, "Project %s: %s ", func.toUpper().toLatin1().constData(), + msg.toLatin1().constData()); + + QFile qfile; + if(qfile.open(stdin, QIODevice::ReadOnly)) { + QTextStream t(&qfile); + ret = split_value_list(t.readLine()); + } + } + break; } + case E_REPLACE: { + if(args.count() != 3 ) { + fprintf(stderr, "%s:%d replace(var, before, after) requires three arguments\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + const QRegExp before( args[1] ); + const QString after( args[2] ); + QStringList var = values(args.first(), place); + for(QStringList::Iterator it = var.begin(); it != var.end(); ++it) + ret += it->replace(before, after); + } + break; } + case E_SIZE: { + if(args.count() != 1) { + fprintf(stderr, "%s:%d: size(var) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + int size = values(args[0], place).size(); + ret += QString::number(size); + } + break; } + case E_GENERATE_UID: + if (args.count() != 1) { + fprintf(stderr, "%s:%d: generate_uid(var) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + ret += generate_test_uid(args.first()); + } + break; + default: { + fprintf(stderr, "%s:%d: Unknown replace function: %s\n", + parser.file.toLatin1().constData(), parser.line_no, + func.toLatin1().constData()); + break; } + } + return ret; +} + +bool +QMakeProject::doProjectTest(QString func, QStringList args, QMap &place) +{ + QList args_list; + for(int i = 0; i < args.size(); ++i) { + QStringList arg = split_value_list(args[i]), tmp; + for(int i = 0; i < arg.size(); ++i) + tmp += doVariableReplaceExpand(arg[i], place); + args_list += tmp; + } + return doProjectTest(func, args_list, place); +} + +bool +QMakeProject::doProjectTest(QString func, QList args_list, QMap &place) +{ + func = func.trimmed(); + + if(testFunctions.contains(func)) { + FunctionBlock *defined = testFunctions[func]; + QStringList ret; + function_blocks.push(defined); + defined->exec(args_list, this, place, ret); + Q_ASSERT(function_blocks.pop() == defined); + + if(ret.isEmpty()) { + return true; + } else { + if(ret.first() == "true") { + return true; + } else if(ret.first() == "false") { + return false; + } else { + bool ok; + int val = ret.first().toInt(&ok); + if(ok) + return val; + fprintf(stderr, "%s:%d Unexpected return value from test %s [%s].\n", + parser.file.toLatin1().constData(), + parser.line_no, func.toLatin1().constData(), + ret.join("::").toLatin1().constData()); + } + return false; + } + return false; + } + + QStringList args; //why don't the builtin functions just use args_list? --Sam + for(int i = 0; i < args_list.size(); ++i) + args += args_list[i].join(QString(Option::field_sep)); + + TestFunc func_t = qmake_testFunctions().value(func); + debug_msg(1, "Running project test: %s(%s) [%d]", + func.toLatin1().constData(), args.join("::").toLatin1().constData(), func_t); + + switch(func_t) { + case T_REQUIRES: + return doProjectCheckReqs(args, place); + case T_LESSTHAN: + case T_GREATERTHAN: { + if(args.count() != 2) { + fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(), + parser.line_no, func.toLatin1().constData()); + return false; + } + QString rhs(args[1]), lhs(values(args[0], place).join(QString(Option::field_sep))); + bool ok; + int rhs_int = rhs.toInt(&ok); + if(ok) { // do integer compare + int lhs_int = lhs.toInt(&ok); + if(ok) { + if(func_t == T_GREATERTHAN) + return lhs_int > rhs_int; + return lhs_int < rhs_int; + } + } + if(func_t == T_GREATERTHAN) + return lhs > rhs; + return lhs < rhs; } + case T_IF: { + if(args.count() != 1) { + fprintf(stderr, "%s:%d: if(condition) requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no); + return false; + } + const QString cond = args.first(); + const QChar *d = cond.unicode(); + QChar quote = 0; + bool ret = true, or_op = false; + QString test; + for(int d_off = 0, parens = 0, d_len = cond.size(); d_off < d_len; ++d_off) { + if(!quote.isNull()) { + if(*(d+d_off) == quote) + quote = QChar(); + } else if(*(d+d_off) == '(') { + ++parens; + } else if(*(d+d_off) == ')') { + --parens; + } else if(*(d+d_off) == '"' /*|| *(d+d_off) == '\''*/) { + quote = *(d+d_off); + } + if(!parens && quote.isNull() && (*(d+d_off) == QLatin1Char(':') || *(d+d_off) == QLatin1Char('|') || d_off == d_len-1)) { + if(d_off == d_len-1) + test += *(d+d_off); + if(!test.isEmpty()) { + if (or_op != ret) + ret = doProjectTest(test, place); + test.clear(); + } + if(*(d+d_off) == QLatin1Char(':')) { + or_op = false; + } else if(*(d+d_off) == QLatin1Char('|')) { + or_op = true; + } + } else { + test += *(d+d_off); + } + } + return ret; } + case T_EQUALS: + if(args.count() != 2) { + fprintf(stderr, "%s:%d: %s(variable, value) requires two arguments.\n", parser.file.toLatin1().constData(), + parser.line_no, func.toLatin1().constData()); + return false; + } + return values(args[0], place).join(QString(Option::field_sep)) == args[1]; + case T_EXISTS: { + if(args.count() != 1) { + fprintf(stderr, "%s:%d: exists(file) requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no); + return false; + } + QString file = args.first(); + file = Option::fixPathToLocalOS(file); + + if(QFile::exists(file)) + return true; + //regular expression I guess + QString dirstr = qmake_getpwd(); + int slsh = file.lastIndexOf(QDir::separator()); + if(slsh != -1) { + dirstr = file.left(slsh+1); + file = file.right(file.length() - slsh - 1); + } + return QDir(dirstr).entryList(QStringList(file)).count(); } + case T_EXPORT: + if(args.count() != 1) { + fprintf(stderr, "%s:%d: export(variable) requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no); + return false; + } + for(int i = 0; i < function_blocks.size(); ++i) { + FunctionBlock *f = function_blocks.at(i); + f->vars[args[0]] = values(args[0], place); + if(!i && f->calling_place) + (*f->calling_place)[args[0]] = values(args[0], place); + } + return true; + case T_CLEAR: + if(args.count() != 1) { + fprintf(stderr, "%s:%d: clear(variable) requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no); + return false; + } + if(!place.contains(args[0])) + return false; + place[args[0]].clear(); + return true; + case T_UNSET: + if(args.count() != 1) { + fprintf(stderr, "%s:%d: unset(variable) requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no); + return false; + } + if(!place.contains(args[0])) + return false; + place.remove(args[0]); + return true; + case T_EVAL: { + if(args.count() < 1 && 0) { + fprintf(stderr, "%s:%d: eval(project) requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no); + return false; + } + QString project = args.join(" "); + parser_info pi = parser; + parser.from_file = false; + parser.file = "(eval)"; + parser.line_no = 0; + QTextStream t(&project, QIODevice::ReadOnly); + bool ret = read(t, place); + parser = pi; + return ret; } + case T_CONFIG: { + if(args.count() < 1 || args.count() > 2) { + fprintf(stderr, "%s:%d: CONFIG(config) requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no); + return false; + } + if(args.count() == 1) + return isActiveConfig(args[0]); + const QStringList mutuals = args[1].split('|'); + const QStringList &configs = values("CONFIG", place); + for(int i = configs.size()-1; i >= 0; i--) { + for(int mut = 0; mut < mutuals.count(); mut++) { + if(configs[i] == mutuals[mut].trimmed()) + return (configs[i] == args[0]); + } + } + return false; } + case T_SYSTEM: + if(args.count() < 1 || args.count() > 2) { + fprintf(stderr, "%s:%d: system(exec) requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no); + return false; + } + if(args.count() == 2) { + const QString sarg = args[1]; + if (sarg.toLower() == "true" || sarg.toInt()) + warn_msg(WarnParser, "%s:%d: system()'s second argument is now hard-wired to false.\n", + parser.file.toLatin1().constData(), parser.line_no); + } + return system(args[0].toLatin1().constData()) == 0; + case T_RETURN: + if(function_blocks.isEmpty()) { + fprintf(stderr, "%s:%d unexpected return()\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + FunctionBlock *f = function_blocks.top(); + f->cause_return = true; + if(args_list.count() >= 1) + f->return_value += args_list[0]; + } + return true; + case T_BREAK: + if(iterator) + iterator->cause_break = true; + else + fprintf(stderr, "%s:%d unexpected break()\n", + parser.file.toLatin1().constData(), parser.line_no); + return true; + case T_NEXT: + if(iterator) + iterator->cause_next = true; + else + fprintf(stderr, "%s:%d unexpected next()\n", + parser.file.toLatin1().constData(), parser.line_no); + return true; + case T_DEFINED: + if(args.count() < 1 || args.count() > 2) { + fprintf(stderr, "%s:%d: defined(function) requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no); + } else { + if(args.count() > 1) { + if(args[1] == "test") + return testFunctions.contains(args[0]); + else if(args[1] == "replace") + return replaceFunctions.contains(args[0]); + fprintf(stderr, "%s:%d: defined(function, type): unexpected type [%s].\n", + parser.file.toLatin1().constData(), parser.line_no, + args[1].toLatin1().constData()); + } else { + if(replaceFunctions.contains(args[0]) || testFunctions.contains(args[0])) + return true; + } + } + return false; + case T_CONTAINS: { + if(args.count() < 2 || args.count() > 3) { + fprintf(stderr, "%s:%d: contains(var, val) requires at lesat 2 arguments.\n", + parser.file.toLatin1().constData(), parser.line_no); + return false; + } + QRegExp regx(args[1]); + const QStringList &l = values(args[0], place); + if(args.count() == 2) { + for(int i = 0; i < l.size(); ++i) { + const QString val = l[i]; + if(regx.exactMatch(val) || val == args[1]) + return true; + } + } else { + const QStringList mutuals = args[2].split('|'); + for(int i = l.size()-1; i >= 0; i--) { + const QString val = l[i]; + for(int mut = 0; mut < mutuals.count(); mut++) { + if(val == mutuals[mut].trimmed()) + return (regx.exactMatch(val) || val == args[1]); + } + } + } + return false; } + case T_INFILE: { + if(args.count() < 2 || args.count() > 3) { + fprintf(stderr, "%s:%d: infile(file, var, val) requires at least 2 arguments.\n", + parser.file.toLatin1().constData(), parser.line_no); + return false; + } + + bool ret = false; + QMap tmp; + if(doProjectInclude(Option::fixPathToLocalOS(args[0]), IncludeFlagNewParser, tmp) == IncludeSuccess) { + if(tmp.contains("QMAKE_INTERNAL_INCLUDED_FILES")) { + QStringList &out = place["QMAKE_INTERNAL_INCLUDED_FILES"]; + const QStringList &in = tmp["QMAKE_INTERNAL_INCLUDED_FILES"]; + for(int i = 0; i < in.size(); ++i) { + if(out.indexOf(in[i]) == -1) + out += in[i]; + } + } + if(args.count() == 2) { + ret = tmp.contains(args[1]); + } else { + QRegExp regx(args[2]); + const QStringList &l = tmp[args[1]]; + for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it) { + if(regx.exactMatch((*it)) || (*it) == args[2]) { + ret = true; + break; + } + } + } + } + return ret; } + case T_COUNT: + if(args.count() != 2 && args.count() != 3) { + fprintf(stderr, "%s:%d: count(var, count) requires two arguments.\n", parser.file.toLatin1().constData(), + parser.line_no); + return false; + } + if(args.count() == 3) { + QString comp = args[2]; + if(comp == ">" || comp == "greaterThan") + return values(args[0], place).count() > args[1].toInt(); + if(comp == ">=") + return values(args[0], place).count() >= args[1].toInt(); + if(comp == "<" || comp == "lessThan") + return values(args[0], place).count() < args[1].toInt(); + if(comp == "<=") + return values(args[0], place).count() <= args[1].toInt(); + if(comp == "equals" || comp == "isEqual" || comp == "=" || comp == "==") + return values(args[0], place).count() == args[1].toInt(); + fprintf(stderr, "%s:%d: unexpected modifier to count(%s)\n", parser.file.toLatin1().constData(), + parser.line_no, comp.toLatin1().constData()); + return false; + } + return values(args[0], place).count() == args[1].toInt(); + case T_ISEMPTY: + if(args.count() != 1) { + fprintf(stderr, "%s:%d: isEmpty(var) requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no); + return false; + } + return values(args[0], place).isEmpty(); + case T_INCLUDE: + case T_LOAD: { + QString parseInto; + const bool include_statement = (func_t == T_INCLUDE); + bool ignore_error = false; + if(args.count() >= 2) { + if(func_t == T_INCLUDE) { + parseInto = args[1]; + if (args.count() == 3){ + QString sarg = args[2]; + if (sarg.toLower() == "true" || sarg.toInt()) + ignore_error = true; + } + } else { + QString sarg = args[1]; + ignore_error = (sarg.toLower() == "true" || sarg.toInt()); + } + } else if(args.count() != 1) { + QString func_desc = "load(feature)"; + if(include_statement) + func_desc = "include(file)"; + fprintf(stderr, "%s:%d: %s requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no, func_desc.toLatin1().constData()); + return false; + } + QString file = args.first(); + file = Option::fixPathToLocalOS(file); + uchar flags = IncludeFlagNone; + if(!include_statement) + flags |= IncludeFlagFeature; + IncludeStatus stat = IncludeFailure; + if(!parseInto.isEmpty()) { + QMap symbols; + stat = doProjectInclude(file, flags|IncludeFlagNewProject, symbols); + if(stat == IncludeSuccess) { + QMap out_place; + for(QMap::ConstIterator it = place.begin(); it != place.end(); ++it) { + const QString var = it.key(); + if(var != parseInto && !var.startsWith(parseInto + ".")) + out_place.insert(var, it.value()); + } + for(QMap::ConstIterator it = symbols.begin(); it != symbols.end(); ++it) { + const QString var = it.key(); + if(!var.startsWith(".")) + out_place.insert(parseInto + "." + it.key(), it.value()); + } + place = out_place; + } + } else { + stat = doProjectInclude(file, flags, place); + } + if(stat == IncludeFeatureAlreadyLoaded) { + warn_msg(WarnParser, "%s:%d: Duplicate of loaded feature %s", + parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData()); + } else if(stat == IncludeNoExist && !ignore_error) { + warn_msg(WarnAll, "%s:%d: Unable to find file for inclusion %s", + parser.file.toLatin1().constData(), parser.line_no, file.toLatin1().constData()); + return false; + } else if(stat >= IncludeFailure) { + if(!ignore_error) { + printf("Project LOAD(): Feature %s cannot be found.\n", file.toLatin1().constData()); + if (!ignore_error) +#if defined(QT_BUILD_QMAKE_LIBRARY) + return false; +#else + exit(3); +#endif + } + return false; + } + return true; } + case T_DEBUG: { + if(args.count() != 2) { + fprintf(stderr, "%s:%d: debug(level, message) requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no); + return false; + } + QString msg = fixEnvVariables(args[1]); + debug_msg(args[0].toInt(), "Project DEBUG: %s", msg.toLatin1().constData()); + return true; } + case T_ERROR: + case T_MESSAGE: + case T_WARNING: { + if(args.count() != 1) { + fprintf(stderr, "%s:%d: %s(message) requires one argument.\n", parser.file.toLatin1().constData(), + parser.line_no, func.toLatin1().constData()); + return false; + } + QString msg = fixEnvVariables(args.first()); + fprintf(stderr, "Project %s: %s\n", func.toUpper().toLatin1().constData(), msg.toLatin1().constData()); + if(func == "error") +#if defined(QT_BUILD_QMAKE_LIBRARY) + return false; +#else + exit(2); +#endif + return true; } + case T_OPTION: + if (args.count() != 1) { + fprintf(stderr, "%s:%d: option() requires one argument.\n", + parser.file.toLatin1().constData(), parser.line_no); + return false; + } + if (args.first() == "recursive") { + recursive = true; + } else { + fprintf(stderr, "%s:%d: unrecognized option() argument '%s'.\n", + parser.file.toLatin1().constData(), parser.line_no, + args.first().toLatin1().constData()); + return false; + } + return true; + default: + fprintf(stderr, "%s:%d: Unknown test function: %s\n", parser.file.toLatin1().constData(), parser.line_no, + func.toLatin1().constData()); + } + return false; +} + +bool +QMakeProject::doProjectCheckReqs(const QStringList &deps, QMap &place) +{ + bool ret = false; + for(QStringList::ConstIterator it = deps.begin(); it != deps.end(); ++it) { + bool test = doProjectTest((*it), place); + if(!test) { + debug_msg(1, "Project Parser: %s:%d Failed test: REQUIRES = %s", + parser.file.toLatin1().constData(), parser.line_no, + (*it).toLatin1().constData()); + place["QMAKE_FAILED_REQUIREMENTS"].append((*it)); + ret = false; + } + } + return ret; +} + +bool +QMakeProject::test(const QString &v) +{ + QMap tmp = vars; + return doProjectTest(v, tmp); +} + +bool +QMakeProject::test(const QString &func, const QList &args) +{ + QMap tmp = vars; + return doProjectTest(func, args, tmp); +} + +QStringList +QMakeProject::expand(const QString &str) +{ + bool ok; + QMap tmp = vars; + const QStringList ret = doVariableReplaceExpand(str, tmp, &ok); + if(ok) + return ret; + return QStringList(); +} + +QString +QMakeProject::expand(const QString &str, const QString &file, int line) +{ + bool ok; + parser_info pi = parser; + parser.file = file; + parser.line_no = line; + parser.from_file = false; + QMap tmp = vars; + const QStringList ret = doVariableReplaceExpand(str, tmp, &ok); + parser = pi; + return ok ? ret.join(QString(Option::field_sep)) : QString(); +} + +QStringList +QMakeProject::expand(const QString &func, const QList &args) +{ + QMap tmp = vars; + return doProjectExpand(func, args, tmp); +} + +bool +QMakeProject::doVariableReplace(QString &str, QMap &place) +{ + bool ret; + str = doVariableReplaceExpand(str, place, &ret).join(QString(Option::field_sep)); + return ret; +} + +QStringList +QMakeProject::doVariableReplaceExpand(const QString &str, QMap &place, bool *ok) +{ + QStringList ret; + if(ok) + *ok = true; + if(str.isEmpty()) + return ret; + + const ushort LSQUARE = '['; + const ushort RSQUARE = ']'; + const ushort LCURLY = '{'; + const ushort RCURLY = '}'; + const ushort LPAREN = '('; + const ushort RPAREN = ')'; + const ushort DOLLAR = '$'; + const ushort SLASH = '\\'; + const ushort UNDERSCORE = '_'; + const ushort DOT = '.'; + const ushort SPACE = ' '; + const ushort TAB = '\t'; + const ushort SINGLEQUOTE = '\''; + const ushort DOUBLEQUOTE = '"'; + + ushort unicode, quote = 0; + const QChar *str_data = str.data(); + const int str_len = str.length(); + + ushort term; + QString var, args; + + int replaced = 0; + QString current; + for(int i = 0; i < str_len; ++i) { + unicode = str_data[i].unicode(); + const int start_var = i; + if(unicode == DOLLAR && str_len > i+2) { + unicode = str_data[++i].unicode(); + if(unicode == DOLLAR) { + term = 0; + var.clear(); + args.clear(); + enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR; + unicode = str_data[++i].unicode(); + if(unicode == LSQUARE) { + unicode = str_data[++i].unicode(); + term = RSQUARE; + var_type = PROPERTY; + } else if(unicode == LCURLY) { + unicode = str_data[++i].unicode(); + var_type = VAR; + term = RCURLY; + } else if(unicode == LPAREN) { + unicode = str_data[++i].unicode(); + var_type = ENVIRON; + term = RPAREN; + } + while(1) { + if(!(unicode & (0xFF<<8)) && + unicode != DOT && unicode != UNDERSCORE && + //unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE && + (unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') && + (unicode < '0' || unicode > '9')) + break; + var.append(QChar(unicode)); + if(++i == str_len) + break; + unicode = str_data[i].unicode(); + // at this point, i points to either the 'term' or 'next' character (which is in unicode) + } + if(var_type == VAR && unicode == LPAREN) { + var_type = FUNCTION; + int depth = 0; + while(1) { + if(++i == str_len) + break; + unicode = str_data[i].unicode(); + if(unicode == LPAREN) { + depth++; + } else if(unicode == RPAREN) { + if(!depth) + break; + --depth; + } + args.append(QChar(unicode)); + } + if(++i < str_len) + unicode = str_data[i].unicode(); + else + unicode = 0; + // at this point i is pointing to the 'next' character (which is in unicode) + // this might actually be a term character since you can do $${func()} + } + if(term) { + if(unicode != term) { + qmake_error_msg("Missing " + QString(term) + " terminator [found " + (unicode?QString(unicode):QString("end-of-line")) + "]"); + if(ok) + *ok = false; + return QStringList(); + } + } else { + // move the 'cursor' back to the last char of the thing we were looking at + --i; + } + // since i never points to the 'next' character, there is no reason for this to be set + unicode = 0; + + QStringList replacement; + if(var_type == ENVIRON) { + replacement = split_value_list(QString::fromLocal8Bit(qgetenv(var.toLatin1().constData()))); + } else if(var_type == PROPERTY) { + if(prop) + replacement = split_value_list(prop->value(var)); + } else if(var_type == FUNCTION) { + replacement = doProjectExpand(var, args, place); + } else if(var_type == VAR) { + replacement = values(var, place); + } + if(!(replaced++) && start_var) + current = str.left(start_var); + if(!replacement.isEmpty()) { + if(quote) { + current += replacement.join(QString(Option::field_sep)); + } else { + current += replacement.takeFirst(); + if(!replacement.isEmpty()) { + if(!current.isEmpty()) + ret.append(current); + current = replacement.takeLast(); + if(!replacement.isEmpty()) + ret += replacement; + } + } + } + debug_msg(2, "Project Parser [var replace]: %s -> %s", + str.toLatin1().constData(), var.toLatin1().constData(), + replacement.join("::").toLatin1().constData()); + } else { + if(replaced) + current.append("$"); + } + } + if(quote && unicode == quote) { + unicode = 0; + quote = 0; + } else if(unicode == SLASH) { + bool escape = false; + const char *symbols = "[]{}()$\\'\""; + for(const char *s = symbols; *s; ++s) { + if(str_data[i+1].unicode() == (ushort)*s) { + i++; + escape = true; + if(!(replaced++)) + current = str.left(start_var); + current.append(str.at(i)); + break; + } + } + if(!escape && !backslashWarned) { + backslashWarned = true; + warn_msg(WarnDeprecated, "%s:%d: Unescaped backslashes are deprecated.", + parser.file.toLatin1().constData(), parser.line_no); + } + if(escape || !replaced) + unicode =0; + } else if(!quote && (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE)) { + quote = unicode; + unicode = 0; + if(!(replaced++) && i) + current = str.left(i); + } else if(!quote && (unicode == SPACE || unicode == TAB)) { + unicode = 0; + if(!current.isEmpty()) { + ret.append(current); + current.clear(); + } + } + if(replaced && unicode) + current.append(QChar(unicode)); + } + if(!replaced) + ret = QStringList(str); + else if(!current.isEmpty()) + ret.append(current); + //qDebug() << "REPLACE" << str << ret; + if (quote) + warn_msg(WarnDeprecated, "%s:%d: Unmatched quotes are deprecated.", + parser.file.toLatin1().constData(), parser.line_no); + return ret; +} + +QStringList &QMakeProject::values(const QString &_var, QMap &place) +{ + QString var = varMap(_var); + if(var == QLatin1String("LITERAL_WHITESPACE")) { //a real space in a token) + var = ".BUILTIN." + var; + place[var] = QStringList(QLatin1String("\t")); + } else if(var == QLatin1String("LITERAL_DOLLAR")) { //a real $ + var = ".BUILTIN." + var; + place[var] = QStringList(QLatin1String("$")); + } else if(var == QLatin1String("LITERAL_HASH")) { //a real # + var = ".BUILTIN." + var; + place[var] = QStringList("#"); + } else if(var == QLatin1String("OUT_PWD")) { //the out going dir + var = ".BUILTIN." + var; + place[var] = QStringList(Option::output_dir); + } else if(var == QLatin1String("PWD") || //current working dir (of _FILE_) + var == QLatin1String("IN_PWD")) { + var = ".BUILTIN." + var; + place[var] = QStringList(qmake_getpwd()); + } else if(var == QLatin1String("DIR_SEPARATOR")) { + validateModes(); + var = ".BUILTIN." + var; + place[var] = QStringList(Option::dir_sep); + } else if(var == QLatin1String("DIRLIST_SEPARATOR")) { + var = ".BUILTIN." + var; + place[var] = QStringList(Option::dirlist_sep); + } else if(var == QLatin1String("_LINE_")) { //parser line number + var = ".BUILTIN." + var; + place[var] = QStringList(QString::number(parser.line_no)); + } else if(var == QLatin1String("_FILE_")) { //parser file + var = ".BUILTIN." + var; + place[var] = QStringList(parser.file); + } else if(var == QLatin1String("_DATE_")) { //current date/time + var = ".BUILTIN." + var; + place[var] = QStringList(QDateTime::currentDateTime().toString()); + } else if(var == QLatin1String("_PRO_FILE_")) { + var = ".BUILTIN." + var; + place[var] = QStringList(pfile); + } else if(var == QLatin1String("_PRO_FILE_PWD_")) { + var = ".BUILTIN." + var; + place[var] = QStringList(pfile.isEmpty() ? qmake_getpwd() : QFileInfo(pfile).absolutePath()); + } else if(var == QLatin1String("_QMAKE_CACHE_")) { + var = ".BUILTIN." + var; + if(Option::mkfile::do_cache) + place[var] = QStringList(Option::mkfile::cachefile); + } else if(var == QLatin1String("TEMPLATE")) { + if(!Option::user_template.isEmpty()) { + var = ".BUILTIN.USER." + var; + place[var] = QStringList(Option::user_template); + } else { + QString orig_template, real_template; + if(!place[var].isEmpty()) + orig_template = place[var].first(); + real_template = orig_template.isEmpty() ? "app" : orig_template; + if(!Option::user_template_prefix.isEmpty() && !orig_template.startsWith(Option::user_template_prefix)) + real_template.prepend(Option::user_template_prefix); + if(real_template != orig_template) { + var = ".BUILTIN." + var; + place[var] = QStringList(real_template); + } + } + } else if(var.startsWith(QLatin1String("QMAKE_HOST."))) { + QString ret, type = var.mid(11); +#if defined(Q_OS_WIN32) + if(type == "os") { + ret = "Windows"; + } else if(type == "name") { + DWORD name_length = 1024; + wchar_t name[1024]; + if (GetComputerName(name, &name_length)) + ret = QString::fromWCharArray(name); + } else if(type == "version" || type == "version_string") { + QSysInfo::WinVersion ver = QSysInfo::WindowsVersion; + if(type == "version") + ret = QString::number(ver); + else if(ver == QSysInfo::WV_Me) + ret = "WinMe"; + else if(ver == QSysInfo::WV_95) + ret = "Win95"; + else if(ver == QSysInfo::WV_98) + ret = "Win98"; + else if(ver == QSysInfo::WV_NT) + ret = "WinNT"; + else if(ver == QSysInfo::WV_2000) + ret = "Win2000"; + else if(ver == QSysInfo::WV_2000) + ret = "Win2003"; + else if(ver == QSysInfo::WV_XP) + ret = "WinXP"; + else if(ver == QSysInfo::WV_VISTA) + ret = "WinVista"; + else + ret = "Unknown"; + } else if(type == "arch") { + SYSTEM_INFO info; + GetSystemInfo(&info); + switch(info.wProcessorArchitecture) { +#ifdef PROCESSOR_ARCHITECTURE_AMD64 + case PROCESSOR_ARCHITECTURE_AMD64: + ret = "x86_64"; + break; +#endif + case PROCESSOR_ARCHITECTURE_INTEL: + ret = "x86"; + break; + case PROCESSOR_ARCHITECTURE_IA64: +#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 + case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: +#endif + ret = "IA64"; + break; + default: + ret = "Unknown"; + break; + } + } +#elif defined(Q_OS_UNIX) + struct utsname name; + if(!uname(&name)) { + if(type == "os") + ret = name.sysname; + else if(type == "name") + ret = name.nodename; + else if(type == "version") + ret = name.release; + else if(type == "version_string") + ret = name.version; + else if(type == "arch") + ret = name.machine; + } +#endif + var = ".BUILTIN.HOST." + type; + place[var] = QStringList(ret); + } else if (var == QLatin1String("QMAKE_DIR_SEP")) { + if (place[var].isEmpty()) + return values("DIR_SEPARATOR", place); + } else if (var == QLatin1String("QMAKE_EXT_OBJ")) { + if (place[var].isEmpty()) { + var = ".BUILTIN." + var; + place[var] = QStringList(Option::obj_ext); + } + } else if (var == QLatin1String("QMAKE_QMAKE")) { + if (place[var].isEmpty()) + place[var] = QStringList(Option::fixPathToTargetOS( + !Option::qmake_abslocation.isEmpty() + ? Option::qmake_abslocation + : QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmake", + false)); + } else if (var == QLatin1String("EPOCROOT")) { + if (place[var].isEmpty()) + place[var] = QStringList(qt_epocRoot()); + } +#if defined(Q_OS_WIN32) && defined(Q_CC_MSVC) + else if(var.startsWith(QLatin1String("QMAKE_TARGET."))) { + QString ret, type = var.mid(13); + if(type == "arch") { + QString paths = qgetenv("PATH"); + QString vcBin64 = qgetenv("VCINSTALLDIR").append("\\bin\\amd64"); + QString vcBinX86_64 = qgetenv("VCINSTALLDIR").append("\\bin\\x86_amd64"); + if(paths.contains(vcBin64,Qt::CaseInsensitive) || paths.contains(vcBinX86_64,Qt::CaseInsensitive)) + ret = "x86_64"; + else + ret = "x86"; + } + place[var] = QStringList(ret); + } +#endif + //qDebug("REPLACE [%s]->[%s]", qPrintable(var), qPrintable(place[var].join("::"))); + return place[var]; +} + +bool QMakeProject::isEmpty(const QString &v) +{ + QMap::ConstIterator it = vars.constFind(varMap(v)); + return it == vars.constEnd() || it->isEmpty(); +} + +QT_END_NAMESPACE diff --git a/qmake/project.h b/qmake/project.h new file mode 100644 index 0000000000..0e6131d3a2 --- /dev/null +++ b/qmake/project.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROJECT_H +#define PROJECT_H + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QMakeProperty; + +struct ParsableBlock; +struct IteratorBlock; +struct FunctionBlock; + +class QMakeProject +{ + struct ScopeBlock + { + enum TestStatus { TestNone, TestFound, TestSeek }; + ScopeBlock() : iterate(0), ignore(false), else_status(TestNone) { } + ScopeBlock(bool i) : iterate(0), ignore(i), else_status(TestNone) { } + ~ScopeBlock(); + IteratorBlock *iterate; + uint ignore : 1, else_status : 2; + }; + friend struct ParsableBlock; + friend struct IteratorBlock; + friend struct FunctionBlock; + + QStack scope_blocks; + QStack function_blocks; + IteratorBlock *iterator; + FunctionBlock *function; + QMap testFunctions, replaceFunctions; + + bool recursive; + bool own_prop; + bool backslashWarned; + QString pfile, cfile; + QMakeProperty *prop; + void reset(); + QMap vars, base_vars, cache; + bool parse(const QString &text, QMap &place, int line_count=1); + + enum IncludeStatus { + IncludeSuccess, + IncludeFeatureAlreadyLoaded, + IncludeFailure, + IncludeNoExist, + IncludeParseFailure + }; + enum IncludeFlags { + IncludeFlagNone = 0x00, + IncludeFlagFeature = 0x01, + IncludeFlagNewParser = 0x02, + IncludeFlagNewProject = 0x04 + }; + IncludeStatus doProjectInclude(QString file, uchar flags, QMap &place); + + bool doProjectCheckReqs(const QStringList &deps, QMap &place); + bool doVariableReplace(QString &str, QMap &place); + QStringList doVariableReplaceExpand(const QString &str, QMap &place, bool *ok=0); + void init(QMakeProperty *, const QMap *); + QStringList &values(const QString &v, QMap &place); + void validateModes(); + +public: + QMakeProject() { init(0, 0); } + QMakeProject(QMakeProperty *p) { init(p, 0); } + QMakeProject(QMakeProject *p, const QMap *nvars=0); + QMakeProject(const QMap &nvars) { init(0, &nvars); } + QMakeProject(QMakeProperty *p, const QMap &nvars) { init(p, &nvars); } + ~QMakeProject(); + + enum { ReadCache=0x01, ReadConf=0x02, ReadCmdLine=0x04, ReadProFile=0x08, + ReadFeatures=0x20, ReadConfigs=0x40, ReadAll=0xFF }; + inline bool parse(const QString &text) { return parse(text, vars); } + bool read(const QString &project, uchar cmd=ReadAll); + bool read(uchar cmd=ReadAll); + + QStringList userExpandFunctions() { return replaceFunctions.keys(); } + QStringList userTestFunctions() { return testFunctions.keys(); } + + QString projectFile(); + inline QMakeProperty *properties() { return prop; } + + bool doProjectTest(QString str, QMap &place); + bool doProjectTest(QString func, const QString ¶ms, + QMap &place); + bool doProjectTest(QString func, QStringList args, + QMap &place); + bool doProjectTest(QString func, QList args, + QMap &place); + QStringList doProjectExpand(QString func, const QString ¶ms, + QMap &place); + QStringList doProjectExpand(QString func, QStringList args, + QMap &place); + QStringList doProjectExpand(QString func, QList args, + QMap &place); + + QStringList expand(const QString &v); + QString expand(const QString &v, const QString &file, int line); + QStringList expand(const QString &func, const QList &args); + bool test(const QString &v); + bool test(const QString &func, const QList &args); + bool isActiveConfig(const QString &x, bool regex=false, + QMap *place=NULL); + + bool isSet(const QString &v); // No compat mapping, no magic variables + bool isEmpty(const QString &v); // With compat mapping, but no magic variables + QStringList &values(const QString &v); // With compat mapping and magic variables + QString first(const QString &v); // ditto + QMap &variables(); // No compat mapping and magic, obviously + + bool isRecursive() const { return recursive; } + +protected: + friend class MakefileGenerator; + bool read(const QString &file, QMap &place); + bool read(QTextStream &file, QMap &place); + +}; +Q_DECLARE_METATYPE(QMakeProject*) + +inline QString QMakeProject::projectFile() +{ + if (pfile == "-") + return QString("(stdin)"); + return pfile; +} + +inline QStringList &QMakeProject::values(const QString &v) +{ return values(v, vars); } + +inline bool QMakeProject::isSet(const QString &v) +{ return vars.contains(v); } + +inline QString QMakeProject::first(const QString &v) +{ + const QStringList vals = values(v); + if(vals.isEmpty()) + return QString(""); + return vals.first(); +} + +inline QMap &QMakeProject::variables() +{ return vars; } + +QT_END_NAMESPACE + +#endif // PROJECT_H diff --git a/qmake/property.cpp b/qmake/property.cpp new file mode 100644 index 0000000000..0c36c1cc59 --- /dev/null +++ b/qmake/property.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "property.h" +#include "option.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QStringList qmake_mkspec_paths(); //project.cpp + +QMakeProperty::QMakeProperty() : settings(0) +{ +} + +QMakeProperty::~QMakeProperty() +{ + delete settings; + settings = 0; +} + +void QMakeProperty::initSettings() +{ + if(!settings) { + settings = new QSettings(QSettings::UserScope, "Trolltech", "QMake"); + settings->setFallbacksEnabled(false); + } +} + +QString +QMakeProperty::keyBase(bool version) const +{ + if (version) + return QString(qmake_version()) + "/"; + return QString(); +} + +QString +QMakeProperty::value(QString v, bool just_check) +{ + if(v == "QT_INSTALL_PREFIX") + return QLibraryInfo::location(QLibraryInfo::PrefixPath); + else if(v == "QT_INSTALL_DATA") + return QLibraryInfo::location(QLibraryInfo::DataPath); + else if(v == "QT_INSTALL_DOCS") + return QLibraryInfo::location(QLibraryInfo::DocumentationPath); + else if(v == "QT_INSTALL_HEADERS") + return QLibraryInfo::location(QLibraryInfo::HeadersPath); + else if(v == "QT_INSTALL_LIBS") + return QLibraryInfo::location(QLibraryInfo::LibrariesPath); + else if(v == "QT_INSTALL_BINS") + return QLibraryInfo::location(QLibraryInfo::BinariesPath); + else if(v == "QT_INSTALL_PLUGINS") + return QLibraryInfo::location(QLibraryInfo::PluginsPath); + else if(v == "QT_INSTALL_IMPORTS") + return QLibraryInfo::location(QLibraryInfo::ImportsPath); + else if(v == "QT_INSTALL_TRANSLATIONS") + return QLibraryInfo::location(QLibraryInfo::TranslationsPath); + else if(v == "QT_INSTALL_CONFIGURATION") + return QLibraryInfo::location(QLibraryInfo::SettingsPath); + else if(v == "QT_INSTALL_EXAMPLES") + return QLibraryInfo::location(QLibraryInfo::ExamplesPath); + else if(v == "QT_INSTALL_DEMOS") + return QLibraryInfo::location(QLibraryInfo::DemosPath); + else if(v == "QMAKE_MKSPECS") + return qmake_mkspec_paths().join(Option::dirlist_sep); + else if(v == "QMAKE_VERSION") + return qmake_version(); +#ifdef QT_VERSION_STR + else if(v == "QT_VERSION") + return QT_VERSION_STR; +#endif + + initSettings(); + int slash = v.lastIndexOf('/'); + QVariant var = settings->value(keyBase(slash == -1) + v); + bool ok = var.isValid(); + QString ret = var.toString(); + if(!ok) { + QString version = qmake_version(); + if(slash != -1) { + version = v.left(slash-1); + v = v.mid(slash+1); + } + settings->beginGroup(keyBase(false)); + QStringList subs = settings->childGroups(); + settings->endGroup(); + subs.sort(); + for (int x = subs.count() - 1; x >= 0; x--) { + QString s = subs[x]; + if(s.isEmpty() || s > version) + continue; + var = settings->value(keyBase(false) + s + "/" + v); + ok = var.isValid(); + ret = var.toString(); + if (ok) { + if(!just_check) + debug_msg(1, "Fell back from %s -> %s for '%s'.", version.toLatin1().constData(), + s.toLatin1().constData(), v.toLatin1().constData()); + return ret; + } + } + } + return ok ? ret : QString(); +} + +bool +QMakeProperty::hasValue(QString v) +{ + return !value(v, true).isNull(); +} + +void +QMakeProperty::setValue(QString var, const QString &val) +{ + initSettings(); + settings->setValue(keyBase() + var, val); +} + +void +QMakeProperty::remove(const QString &var) +{ + initSettings(); + settings->remove(keyBase() + var); +} + +bool +QMakeProperty::exec() +{ + bool ret = true; + if(Option::qmake_mode == Option::QMAKE_QUERY_PROPERTY) { + if(Option::prop::properties.isEmpty()) { + initSettings(); + settings->beginGroup(keyBase(false)); + QStringList subs = settings->childGroups(); + settings->endGroup(); + subs.sort(); + for(int x = subs.count() - 1; x >= 0; x--) { + QString s = subs[x]; + if(s.isEmpty()) + continue; + settings->beginGroup(keyBase(false) + s); + QStringList keys = settings->childKeys(); + settings->endGroup(); + for(QStringList::ConstIterator it2 = keys.begin(); it2 != keys.end(); it2++) { + QString ret = settings->value(keyBase(false) + s + "/" + (*it2)).toString(); + if(s != qmake_version()) + fprintf(stdout, "%s/", s.toLatin1().constData()); + fprintf(stdout, "%s:%s\n", (*it2).toLatin1().constData(), ret.toLatin1().constData()); + } + } + QStringList specialProps; + specialProps.append("QT_INSTALL_PREFIX"); + specialProps.append("QT_INSTALL_DATA"); + specialProps.append("QT_INSTALL_DOCS"); + specialProps.append("QT_INSTALL_HEADERS"); + specialProps.append("QT_INSTALL_LIBS"); + specialProps.append("QT_INSTALL_BINS"); + specialProps.append("QT_INSTALL_PLUGINS"); + specialProps.append("QT_INSTALL_IMPORTS"); + specialProps.append("QT_INSTALL_TRANSLATIONS"); + specialProps.append("QT_INSTALL_CONFIGURATION"); + specialProps.append("QT_INSTALL_EXAMPLES"); + specialProps.append("QT_INSTALL_DEMOS"); + specialProps.append("QMAKE_MKSPECS"); + specialProps.append("QMAKE_VERSION"); +#ifdef QT_VERSION_STR + specialProps.append("QT_VERSION"); +#endif + foreach (QString prop, specialProps) + fprintf(stdout, "%s:%s\n", prop.toLatin1().constData(), value(prop).toLatin1().constData()); + return true; + } + for(QStringList::ConstIterator it = Option::prop::properties.begin(); + it != Option::prop::properties.end(); it++) { + if(Option::prop::properties.count() > 1) + fprintf(stdout, "%s:", (*it).toLatin1().constData()); + if(!hasValue((*it))) { + ret = false; + fprintf(stdout, "**Unknown**\n"); + } else { + fprintf(stdout, "%s\n", value((*it)).toLatin1().constData()); + } + } + } else if(Option::qmake_mode == Option::QMAKE_SET_PROPERTY) { + for(QStringList::ConstIterator it = Option::prop::properties.begin(); + it != Option::prop::properties.end(); it++) { + QString var = (*it); + it++; + if(it == Option::prop::properties.end()) { + ret = false; + break; + } + if(!var.startsWith(".")) + setValue(var, (*it)); + } + } else if(Option::qmake_mode == Option::QMAKE_UNSET_PROPERTY) { + for(QStringList::ConstIterator it = Option::prop::properties.begin(); + it != Option::prop::properties.end(); it++) { + QString var = (*it); + if(!var.startsWith(".")) + remove(var); + } + } + return ret; +} + +QT_END_NAMESPACE diff --git a/qmake/property.h b/qmake/property.h new file mode 100644 index 0000000000..5e8379e787 --- /dev/null +++ b/qmake/property.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROPERTY_H +#define PROPERTY_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QSettings; + +class QMakeProperty +{ + QSettings *settings; + void initSettings(); + QString keyBase(bool =true) const; + QString value(QString, bool just_check); +public: + QMakeProperty(); + ~QMakeProperty(); + + bool hasValue(QString); + QString value(QString v) { return value(v, false); } + void setValue(QString, const QString &); + void remove(const QString &); + + bool exec(); +}; + +QT_END_NAMESPACE + +#endif // PROPERTY_H diff --git a/qmake/qmake.pri b/qmake/qmake.pri new file mode 100644 index 0000000000..d6c5f09107 --- /dev/null +++ b/qmake/qmake.pri @@ -0,0 +1,162 @@ +CONFIG += depend_includepath + +QMAKE_INCREMENTAL = +SKIP_DEPENDS += qconfig.h qmodules.h +DEFINES += QT_NO_TEXTCODEC QT_NO_LIBRARY QT_NO_STL QT_NO_COMPRESS QT_NO_UNICODETABLES \ + QT_NO_GEOM_VARIANT QT_NO_DATASTREAM QLIBRARYINFO_EPOCROOT + +#qmake code +SOURCES += project.cpp property.cpp main.cpp generators/makefile.cpp \ + generators/unix/unixmake2.cpp generators/unix/unixmake.cpp meta.cpp \ + option.cpp generators/win32/winmakefile.cpp generators/win32/mingw_make.cpp \ + generators/makefiledeps.cpp generators/metamakefile.cpp generators/mac/pbuilder_pbx.cpp \ + generators/xmloutput.cpp generators/win32/borland_bmake.cpp \ + generators/win32/msvc_nmake.cpp generators/projectgenerator.cpp \ + generators/win32/msvc_vcproj.cpp \ + generators/win32/msvc_vcxproj.cpp \ + generators/win32/msvc_objectmodel.cpp generators/win32/msbuild_objectmodel.cpp \ + generators/symbian/symbiancommon.cpp \ + generators/symbian/symmake.cpp \ + generators/symbian/symmake_abld.cpp \ + generators/symbian/symmake_sbsv2.cpp \ + generators/symbian/initprojectdeploy_symbian.cpp \ + windows/registry.cpp \ + symbian/epocroot.cpp + +HEADERS += project.h property.h generators/makefile.h \ + generators/unix/unixmake.h meta.h option.h cachekeys.h \ + generators/win32/winmakefile.h generators/win32/mingw_make.h generators/projectgenerator.h \ + generators/makefiledeps.h generators/metamakefile.h generators/mac/pbuilder_pbx.h \ + generators/xmloutput.h generators/win32/borland_bmake.h generators/win32/msvc_nmake.h \ + generators/win32/msvc_vcproj.h \ + generators/win32/msvc_vcxproj.h \ + generators/win32/msvc_objectmodel.h generators/win32/msbuild_objectmodel.h \ + generators/symbian/symbiancommon.h \ + generators/symbian/symmake.h \ + generators/symbian/symmake_abld.h \ + generators/symbian/symmake_sbsv2.h \ + generators/symbian/initprojectdeploy_symbian.h \ + windows/registry_p.h \ + symbian/epocroot_p.h + +contains(QT_EDITION, OpenSource) { + DEFINES += QMAKE_OPENSOURCE_EDITION +} + +bootstrap { #Qt code + DEFINES+=QT_NODLL QT_NO_THREAD + SOURCES+= \ + qbitarray.cpp \ + qbuffer.cpp \ + qbytearray.cpp \ + qbytearraymatcher.cpp \ + qcryptographichash.cpp \ + qdatetime.cpp \ + qdir.cpp \ + qdiriterator.cpp \ + qfile.cpp \ + qabstractfileengine.cpp \ + qfileinfo.cpp \ + qfilesystementry.cpp \ + qfilesystemengine.cpp \ + qfsfileengine.cpp \ + qfsfileengine_iterator.cpp \ + qglobal.cpp \ + qnumeric.cpp \ + qhash.cpp \ + qiodevice.cpp \ + qlist.cpp \ + qlinkedlist.cpp \ + qlocale.cpp \ + qmalloc.cpp \ + qmap.cpp \ + qmetatype.cpp \ + qregexp.cpp \ + qtextcodec.cpp \ + qutfcodec.cpp \ + qstring.cpp \ + qstringlist.cpp \ + qtemporaryfile.cpp \ + qtextstream.cpp \ + qurl.cpp \ + quuid.cpp \ + qsettings.cpp \ + qlibraryinfo.cpp \ + qsystemerror.cpp \ + qvariant.cpp \ + qvector.cpp \ + qvsnprintf.cpp \ + qxmlstream.cpp \ + qxmlutils.cpp + + HEADERS+= \ + qbitarray.h \ + qbuffer.h \ + qbytearray.h \ + qbytearraymatcher.h \ + qchar.h \ + qcryptographichash.h \ + qdatetime.h \ + qdatetime_p.h \ + qdir.h \ + qdir_p.h \ + qdiriterator.h \ + qfile.h \ + qabstractfileengine.h \ + qfileinfo.h \ + qglobal.h \ + qnumeric.h \ + qhash.h \ + qiodevice.h \ + qlist.h \ + qlinkedlist.h \ + qlocale.h \ + qmalloc.h \ + qmap.h \ + qmetatype.h \ + qregexp.h \ + qtextcodec.h \ + qutfcodec.h \ + qstring.h \ + qstringlist.h \ + qstringmatcher.h \ + qsystemerror_p.h \ + qtemporaryfile.h \ + qtextstream.h \ + qurl.h \ + quuid.h \ + qvector.h \ + qxmlstream.h \ + qxmlutils.h + + unix { + SOURCES += qfilesystemengine_unix.cpp qfilesystemiterator_unix.cpp qfsfileengine_unix.cpp + mac { + SOURCES += qfilesystemengine_mac.cpp + SOURCES += qcore_mac.cpp qsettings_mac.cpp + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.4 #enables weak linking for 10.4 (exported) + LIBS += -framework ApplicationServices + } + } else:win32 { + SOURCES += qfilesystemengine_win.cpp qfsfileengine_win.cpp qfilesystemiterator_win.cpp qsettings_win.cpp \ + qsystemlibrary.cpp + win32-msvc*:LIBS += ole32.lib advapi32.lib + win32-g++*:LIBS += -lole32 -luuid -ladvapi32 -lkernel32 + } + + qnx { + CFLAGS += -fhonor-std + LFLAGS += -lcpp + } + DEFINES *= QT_NO_QOBJECT +} else { + CONFIG += qt + QT = core +} +*-g++:profiling { + QMAKE_CFLAGS = -pg + QMAKE_CXXFLAGS = -pg + QMAKE_LFLAGS = -pg +} + +PRECOMPILED_HEADER = qmake_pch.h diff --git a/qmake/qmake.pro b/qmake/qmake.pro new file mode 100644 index 0000000000..b602afa198 --- /dev/null +++ b/qmake/qmake.pro @@ -0,0 +1,36 @@ +#This is a project file for building qmake, of course it presents a problem - +# it is very hard to make qmake build this, when qmake is the thing it builds, +#once you are boot strapped though, the qmake.pro will offer better coverage of a +#platform than either of the generic makefiles + +CONFIG += console bootstrap +CONFIG -= qt shared app_bundle uic +DEFINES += QT_BUILD_QMAKE QT_BOOTSTRAPPED +DESTDIR = ../bin/ + +OBJECTS_DIR = . +MOC_DIR = . + +#guts +VPATH += $$QT_SOURCE_TREE/src/corelib/global \ + $$QT_SOURCE_TREE/src/corelib/tools \ + $$QT_SOURCE_TREE/src/corelib/kernel \ + $$QT_SOURCE_TREE/src/corelib/codecs \ + $$QT_SOURCE_TREE/src/corelib/plugin \ + $$QT_SOURCE_TREE/src/corelib/xml \ + $$QT_SOURCE_TREE/src/corelib/io +INCLUDEPATH += . \ + generators \ + generators/unix \ + generators/win32 \ + generators/mac \ + generators/symbian \ + $$QT_SOURCE_TREE/include \ + $$QT_SOURCE_TREE/include/QtCore \ + $$QT_SOURCE_TREE/qmake + +VPATH += $$QT_SOURCE_TREE/tools/shared +INCLUDEPATH += $$QT_SOURCE_TREE/tools/shared + +include(qmake.pri) + diff --git a/qmake/qmake_pch.h b/qmake/qmake_pch.h new file mode 100644 index 0000000000..25f27160a1 --- /dev/null +++ b/qmake/qmake_pch.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAKE_PCH_H +#define QMAKE_PCH_H +#include +#ifdef Q_WS_WIN +# define _POSIX_ +# include +# undef _POSIX_ +#endif + +#include +//#include "makefile.h" +//#include "meta.h" +#include +//#include "winmakefile.h" +//#include +//#include "project.h" +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +//#include +//#include "option.h" + +QT_END_NAMESPACE + +#endif -- cgit v1.2.3