diff options
author | Qt by Nokia <qt-info@nokia.com> | 2011-04-27 12:05:43 +0200 |
---|---|---|
committer | axis <qt-info@nokia.com> | 2011-04-27 12:05:43 +0200 |
commit | 38be0d13830efd2d98281c645c3a60afe05ffece (patch) | |
tree | 6ea73f3ec77f7d153333779883e8120f82820abe /qmake |
Diffstat (limited to 'qmake')
62 files changed, 33268 insertions, 0 deletions
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 <qstring.h> +#include <qstringlist.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qhash.h> + +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 <typename T> +inline void qmakeDeleteCacheClear(void *i) { delete reinterpret_cast<T*>(i); } + +inline void qmakeFreeCacheClear(void *i) { free(i); } + +typedef void (*qmakeCacheClearFunc)(void *); +void qmakeAddCacheClear(qmakeCacheClearFunc func, void **); +void qmakeClearCaches(); + +QT_END_NAMESPACE + +#endif // CACHEKEYS_H 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 <qdir.h> +#include <qregexp.h> +#include <qcryptographichash.h> +#include <qdebug.h> +#include <stdlib.h> +#include <time.h> +#ifdef Q_OS_UNIX +# include <sys/types.h> +# include <sys/stat.h> +#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 <projectname>_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 <qdir.h> +#include <qregexp.h> +#include <qcryptographichash.h> +#include <qdebug.h> +#include <stdlib.h> +#include <time.h> +#ifdef Q_OS_UNIX +# include <sys/types.h> +# include <sys/stat.h> +#endif +#ifdef Q_OS_DARWIN +#include <ApplicationServices/ApplicationServices.h> +#include <private/qcore_mac_p.h> +#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<ProjectBuilderSubDirs*> pb_subdirs; + pb_subdirs.append(new ProjectBuilderSubDirs(project, QString(), false)); + QString oldpwd = qmake_getpwd(); + QMap<QString, QStringList> 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<QString, QStringList> &vars = tmp_proj.variables(); + for(QMap<QString, QStringList>::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", "<absolute>") << ";" << "\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", "<group>") << ";" << "\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<QString, QStringList>::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<QString, QString> 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<QString, QString>::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<QString, QString>::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", "<group>") << ";" << "\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<QString, QStringList> groups; + QList<ProjectBuilderSources> 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<QString, QStringList>::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 << "<?xml version=" + << "\\\"1.0\\\" encoding=" << "\\\"UTF-8\\\"" << "?>" << "\n" + << "\t\t\t\t" << "<!DOCTYPE plist SYSTEM \\\"file://localhost/System/" + << "Library/DTDs/PropertyList.dtd\\\">" << "\n" + << "\t\t\t\t" << "<plist version=\\\"0.9\\\">" << "\n" + << "\t\t\t\t" << "<dict>" << "\n" + << "\t\t\t\t\t" << "<key>CFBundleDevelopmentRegion</key>" << "\n" + << "\t\t\t\t\t" << "<string>English</string>" << "\n" + << "\t\t\t\t\t" << "<key>CFBundleExecutable</key>" << "\n" + << "\t\t\t\t\t" << "<string>" << project->first("QMAKE_ORIG_TARGET") << "</string>" << "\n" + << "\t\t\t\t\t" << "<key>CFBundleIconFile</key>" << "\n" + << "\t\t\t\t\t" << "<string>" << var("ICON").section(Option::dir_sep, -1) << "</string>" << "\n" + << "\t\t\t\t\t" << "<key>CFBundleInfoDictionaryVersion</key>" << "\n" + << "\t\t\t\t\t" << "<string>6.0</string>" << "\n" + << "\t\t\t\t\t" << "<key>CFBundlePackageType</key>" << "\n" + << "\t\t\t\t\t" << "<string>APPL</string>" << "\n" + << "\t\t\t\t\t" << "<key>CFBundleSignature</key>" << "\n" + << "\t\t\t\t\t" << "<string>" + << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? QString::fromLatin1("????") : + project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << "</string>" << "\n" + << "\t\t\t\t\t" << "<key>CFBundleVersion</key>" << "\n" + << "\t\t\t\t\t" << "<string>0.1</string>" << "\n" + << "\t\t\t\t\t" << "<key>CSResourcesFileMapped</key>" << "\n" + << "\t\t\t\t\t" << "<true/>" << "\n" + << "\t\t\t\t" << "</dict>" << "\n" + << "\t\t\t\t" << "</plist>"; + } + 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", "<<ProjectDirectory>>") << ";" << "\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<QString, QString> 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<QString, QString>::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<QString, QString>::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<CFURLRef> cfurl; + OSStatus err = LSFindApplicationForInfo(0, CFSTR("com.apple.Xcode"), 0, 0, &cfurl); + if (err == noErr) { + QCFType<CFBundleRef> 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("^<key>(.*)</key>$"), stringreg("^<string>(.*)</string>$"); + while(!plist.atEnd()) { + QString line = plist.readLine().trimmed(); + if(line == "<dict>") + in_dict = true; + else if(line == "</dict>") + 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<QString, QString> 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 <qdir.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qregexp.h> +#include <qhash.h> +#include <qdebug.h> +#include <qbuffer.h> +#include <qsettings.h> +#include <qdatetime.h> +#if defined(Q_OS_UNIX) +#include <unistd.h> +#else +#include <io.h> +#endif +#include <qdebug.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +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<QString, QStringList> &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<QString, QStringList> &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<QString, QStringList> &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<QString, QStringList> &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<QString, QStringList> &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<int> 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<Compiler> 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<QMakeLocalFileName> 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<QString, QStringList> &vars = libinfo.variables(); + for(QMap<QString, QStringList>::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<QString, bool> 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<SubTarget*> 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<SubTarget*>::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<SubTarget*>::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<QStringList>() << 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<QStringList>() << 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<QStringList>() << 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>() << 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>() << 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<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths(); + for(QList<QMakeLocalFileName>::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<QMakeLocalFileName> depdirs = QMakeSourceFileInfo::dependencyPaths(); + for(QList<QMakeLocalFileName>::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<QString, QStringList> &vars = project->variables(); + QStringList &exports = project->values("QMAKE_EXTRA_VARIABLES"); + for(QMap<QString, QStringList>::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::SubTarget*> +MakefileGenerator::findSubDirsSubTargets() const +{ + QList<SubTarget*> 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<SubTarget*> 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<MakefileGenerator::SubTarget*> 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<SubTarget*>::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<QString> 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<FileInfoCacheKey, QFileInfo> *cache = 0; + static QFileInfo noInfo = QFileInfo(); + if(!cache) { + cache = new QHash<FileInfoCacheKey, QFileInfo>; + qmakeAddCacheClear(qmakeDeleteCacheClear<QHash<FileInfoCacheKey, QFileInfo> >, (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<QMakeLocalFileName> 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 <qtextstream.h> +#include <qlist.h> +#include <qhash.h> +#include <qfileinfo.h> + +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<QString, bool> init_compiler_already; + QString build_args(const QString &outdir=QString()); + void checkMultipleDefinition(const QString &, const QString &); + + //internal caches + mutable QHash<QString, QMakeLocalFileName> depHeuristicsCache; + mutable QHash<QString, QStringList> dependsCache; + mutable QHash<ReplaceExtraCompilerCacheKey, QString> 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<MakefileGenerator::SubTarget*> findSubDirsSubTargets() const; + void writeSubTargets(QTextStream &t, QList<SubTarget*> 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 <qdir.h> +#include <qdatetime.h> +#include <qfileinfo.h> +#include <qbuffer.h> +#include <qplatformdefs.h> +#if defined(Q_OS_UNIX) +# include <unistd.h> +#else +# include <io.h> +#endif +#include <qdebug.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#include <share.h> +#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<QMakeLocalFileName> &l) +{ + // Ensure that depdirs does not contain the same paths several times, to minimize the stats + QList<QMakeLocalFileName> 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; i<l.size(); ++i) + addSourceFile(l.at(i), seek, type); +} +void QMakeSourceFileInfo::addSourceFile(const QString &f, uchar seek, + QMakeSourceFileInfo::SourceFileType type) +{ + if(!files) + files = new SourceFiles; + + QMakeLocalFileName fn(f); + SourceFile *file = files->lookupFile(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<QMakeLocalFileName>::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<QString, QStringList> verify = getCacheVerification(); + stream << verify.count() << endl; + for(QMap<QString, QStringList>::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<QString, QStringList> 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<QString, QStringList> QMakeSourceFileInfo::getCacheVerification() +{ + return QMap<QString, QStringList>(); +} + +bool QMakeSourceFileInfo::verifyCache(const QMap<QString, QStringList> &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 <qstringlist.h> +#include <qfileinfo.h> + +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<QMakeLocalFileName> 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<QMakeLocalFileName> dependencyPaths() const { return depdirs; } + void setDependencyPaths(const QList<QMakeLocalFileName> &); + + enum DependencyMode { Recursive, NonRecursive }; + inline void setDependencyMode(DependencyMode mode) { dep_mode = mode; } + inline DependencyMode dependencyMode() const { return dep_mode; } + + 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<QString, QStringList> getCacheVerification(); + virtual bool verifyCache(const QMap<QString, QStringList> &); + 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<Build *> 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<QString, QStringList> &vars = project->variables(); + for(QMap<QString, QStringList>::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<QString, QStringList> 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<Subdir *> 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<UnixMakefileGenerator>; + } else if(gen == "SYMBIAN_MINGW") { + mkfile = new SymbianMakefileTemplate<MingwMakefileGenerator>; + } 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 <option.h> + +#include <qlist.h> +#include <qstring.h> + +QT_BEGIN_NAMESPACE + +class QMakeProject; +class MakefileGenerator; + +class MetaMakefileGenerator +{ +protected: + MetaMakefileGenerator(QMakeProject *p, const QString &n, bool op=true) : project(p), own_project(op), name(n) { } + QMakeProject *project; + bool own_project; + QString name; + +public: + + virtual ~MetaMakefileGenerator(); + + static MetaMakefileGenerator *createMetaGenerator(QMakeProject *proj, const QString &name, bool op=true, bool *success = 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 <qdatetime.h> +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qregexp.h> + +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<QString, QStringList> &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<QMakeLocalFileName> 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 <QDirIterator> +#include <project.h> +#include <qxmlstream.h> +#include <qsettings.h> +#include <qdebug.h> + +// Included from tools/shared +#include <symbian/epocroot_p.h> + +#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<QString, QString> 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<CopyItem> 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 <qstring.h> +#include <qstringlist.h> +#include <qdatetime.h> +#include <option.h> +#include <qdir.h> +#include <qfile.h> +#include <stdlib.h> + +#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<CopyItem> 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 T> +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<QString, QStringList> 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 <qdebug.h> +#include <qxmlstream.h> + +// Included from tools/shared +#include <symbian/epocroot_p.h> + +#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<QString, QStringList> &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 <appinfo.rh>" << 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 <appinfo.rh>" << 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<QString, + QStringList> &userRssRules) +{ + QMakeProject *project = generator->project; + for (QMap<QString, QStringList>::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<SymbianLocalization> 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 <project.h> +#include <makefile.h> +#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<SymbianLocalization> SymbianLocalizationList; +typedef QListIterator<SymbianLocalization> 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<QString, QStringList> &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<QString, QStringList> &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 <qstring.h> +#include <qhash.h> +#include <qstringlist.h> +#include <qdir.h> +#include <qdatetime.h> +#include <stdlib.h> +#include <qdebug.h> + +// Included from tools/shared +#include <symbian/epocroot_p.h> + +#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<QString, QStringList> 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<QString, QStringList>::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<QString, QStringList>::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<QString, QStringList> userBldInfRules; + for (QMap<QString, QStringList>::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<QString, QStringList>::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 <makefile.h> + +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<QString, QStringList> sources; + QMap<QString, QStringList> systeminclude; + QMap<QString, QStringList> library; + // (output file) (source , command) + QMap<QString, QStringList> 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 <qstring.h> +#include <qstringlist.h> +#include <qdir.h> +#include <qdatetime.h> +#include <qdebug.h> + +// Included from tools/shared +#include <symbian/epocroot_p.h> + +#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<QString, QStringList>::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<QString, QStringList>::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<QString, QStringList>::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<MakefileGenerator::SubTarget*> 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 <symmake.h> + +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 <qstring.h> +#include <qstringlist.h> +#include <qdir.h> +#include <qdatetime.h> +#include <qdebug.h> + +// Included from tools/shared +#include <symbian/epocroot_p.h> + +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<QString, QString> &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<QString, QString> 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<QString, QStringList>::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<MakefileGenerator::SubTarget*> 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<QString, QStringList>::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<QString, QString> 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<QString, QStringList>::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 <symmake.h> + +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 <qregexp.h> +#include <qfile.h> +#include <qhash.h> +#include <qdir.h> +#include <time.h> +#include <qdebug.h> + +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<QMakeLocalFileName> 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<QMakeLocalFileName>::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(e |