|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the qmake application of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "msvc_nmake.h" |
|
43 #include "option.h" |
|
44 #include <qregexp.h> |
|
45 #include <qhash.h> |
|
46 #include <qdir.h> |
|
47 #include <time.h> |
|
48 |
|
49 QT_BEGIN_NAMESPACE |
|
50 |
|
51 NmakeMakefileGenerator::NmakeMakefileGenerator() : Win32MakefileGenerator(), init_flag(false) |
|
52 { |
|
53 |
|
54 } |
|
55 |
|
56 bool |
|
57 NmakeMakefileGenerator::writeMakefile(QTextStream &t) |
|
58 { |
|
59 writeHeader(t); |
|
60 if(!project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()) { |
|
61 QStringList &qut = project->values("QMAKE_EXTRA_TARGETS"); |
|
62 for(QStringList::ConstIterator it = qut.begin(); it != qut.end(); ++it) |
|
63 t << *it << " "; |
|
64 t << "all first clean:" << "\n\t" |
|
65 << "@echo \"Some of the required modules (" |
|
66 << var("QMAKE_FAILED_REQUIREMENTS") << ") are not available.\"" << "\n\t" |
|
67 << "@echo \"Skipped.\"" << endl << endl; |
|
68 writeMakeQmake(t); |
|
69 return true; |
|
70 } |
|
71 |
|
72 if(project->first("TEMPLATE") == "app" || |
|
73 project->first("TEMPLATE") == "lib") { |
|
74 #if 0 |
|
75 if(Option::mkfile::do_stub_makefile) |
|
76 return MakefileGenerator::writeStubMakefile(t); |
|
77 #endif |
|
78 writeNmakeParts(t); |
|
79 return MakefileGenerator::writeMakefile(t); |
|
80 } |
|
81 else if(project->first("TEMPLATE") == "subdirs") { |
|
82 writeSubDirs(t); |
|
83 return true; |
|
84 } |
|
85 return false; |
|
86 } |
|
87 |
|
88 QStringList &NmakeMakefileGenerator::findDependencies(const QString &file) |
|
89 { |
|
90 QStringList &aList = MakefileGenerator::findDependencies(file); |
|
91 // Note: The QMAKE_IMAGE_COLLECTION file have all images |
|
92 // as dependency, so don't add precompiled header then |
|
93 if (file == project->first("QMAKE_IMAGE_COLLECTION")) |
|
94 return aList; |
|
95 for(QStringList::Iterator it = Option::cpp_ext.begin(); it != Option::cpp_ext.end(); ++it) { |
|
96 if(file.endsWith(*it)) { |
|
97 if(!precompObj.isEmpty() && !aList.contains(precompObj)) |
|
98 aList += precompObj; |
|
99 break; |
|
100 } |
|
101 } |
|
102 return aList; |
|
103 } |
|
104 |
|
105 void NmakeMakefileGenerator::writeNmakeParts(QTextStream &t) |
|
106 { |
|
107 writeStandardParts(t); |
|
108 |
|
109 // precompiled header |
|
110 if(usePCH) { |
|
111 QString precompRule = QString("-c -Yc -Fp%1 -Fo%2").arg(precompPch).arg(precompObj); |
|
112 t << precompObj << ": " << precompH << " " << findDependencies(precompH).join(" \\\n\t\t") |
|
113 << "\n\t" << "$(CXX) " + precompRule +" $(CXXFLAGS) $(INCPATH) -TP " << precompH << endl << endl; |
|
114 } |
|
115 } |
|
116 |
|
117 QString NmakeMakefileGenerator::var(const QString &value) |
|
118 { |
|
119 if (usePCH) { |
|
120 if ((value == "QMAKE_RUN_CXX_IMP_BATCH" |
|
121 || value == "QMAKE_RUN_CXX_IMP" |
|
122 || value == "QMAKE_RUN_CXX")) { |
|
123 QFileInfo precompHInfo(fileInfo(precompH)); |
|
124 QString precompRule = QString("-c -FI%1 -Yu%2 -Fp%3") |
|
125 .arg(precompHInfo.fileName()) |
|
126 .arg(precompHInfo.fileName()) |
|
127 .arg(precompPch); |
|
128 QString p = MakefileGenerator::var(value); |
|
129 p.replace("-c", precompRule); |
|
130 // Cannot use -Gm with -FI & -Yu, as this gives an |
|
131 // internal compiler error, on the newer compilers |
|
132 p.remove("-Gm"); |
|
133 return p; |
|
134 } else if (value == "QMAKE_CXXFLAGS") { |
|
135 // Remove internal compiler error option |
|
136 return MakefileGenerator::var(value).remove("-Gm"); |
|
137 } |
|
138 } |
|
139 |
|
140 // Normal val |
|
141 return MakefileGenerator::var(value); |
|
142 } |
|
143 |
|
144 void NmakeMakefileGenerator::init() |
|
145 { |
|
146 if(init_flag) |
|
147 return; |
|
148 init_flag = true; |
|
149 |
|
150 /* this should probably not be here, but I'm using it to wrap the .t files */ |
|
151 if(project->first("TEMPLATE") == "app") |
|
152 project->values("QMAKE_APP_FLAG").append("1"); |
|
153 else if(project->first("TEMPLATE") == "lib") |
|
154 project->values("QMAKE_LIB_FLAG").append("1"); |
|
155 else if(project->first("TEMPLATE") == "subdirs") { |
|
156 MakefileGenerator::init(); |
|
157 if(project->values("MAKEFILE").isEmpty()) |
|
158 project->values("MAKEFILE").append("Makefile"); |
|
159 if(project->values("QMAKE_QMAKE").isEmpty()) |
|
160 project->values("QMAKE_QMAKE").append("qmake"); |
|
161 if(project->isEmpty("QMAKE_COPY_FILE")) |
|
162 project->values("QMAKE_COPY_FILE").append("$(COPY)"); |
|
163 if(project->isEmpty("QMAKE_COPY_DIR")) |
|
164 project->values("QMAKE_COPY_DIR").append("xcopy /s /q /y /i"); |
|
165 if(project->isEmpty("QMAKE_INSTALL_FILE")) |
|
166 project->values("QMAKE_INSTALL_FILE").append("$(COPY_FILE)"); |
|
167 if(project->isEmpty("QMAKE_INSTALL_PROGRAM")) |
|
168 project->values("QMAKE_INSTALL_PROGRAM").append("$(COPY_FILE)"); |
|
169 if(project->isEmpty("QMAKE_INSTALL_DIR")) |
|
170 project->values("QMAKE_INSTALL_DIR").append("$(COPY_DIR)"); |
|
171 return; |
|
172 } |
|
173 |
|
174 project->values("QMAKE_LIBS") += escapeFilePaths(project->values("LIBS")); |
|
175 project->values("QMAKE_LIBS_PRIVATE") += escapeFilePaths(project->values("LIBS_PRIVATE")); |
|
176 processVars(); |
|
177 |
|
178 if (!project->values("RES_FILE").isEmpty()) { |
|
179 project->values("QMAKE_LIBS") += escapeFilePaths(project->values("RES_FILE")); |
|
180 } |
|
181 |
|
182 if(!project->values("DEF_FILE").isEmpty()) |
|
183 project->values("QMAKE_LFLAGS").append(QString("/DEF:") + escapeFilePath(project->first("DEF_FILE"))); |
|
184 |
|
185 if(!project->values("VERSION").isEmpty()) { |
|
186 QString version = project->values("VERSION")[0]; |
|
187 int firstDot = version.indexOf("."); |
|
188 QString major = version.left(firstDot); |
|
189 QString minor = version.right(version.length() - firstDot - 1); |
|
190 minor.replace(".", ""); |
|
191 project->values("QMAKE_LFLAGS").append("/VERSION:" + major + "." + minor); |
|
192 } |
|
193 |
|
194 // Base class init! |
|
195 MakefileGenerator::init(); |
|
196 |
|
197 // Setup PCH variables |
|
198 precompH = project->first("PRECOMPILED_HEADER"); |
|
199 usePCH = !precompH.isEmpty() && project->isActiveConfig("precompile_header"); |
|
200 if (usePCH) { |
|
201 // Created files |
|
202 precompObj = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch" + Option::obj_ext; |
|
203 precompPch = var("PRECOMPILED_DIR") + project->first("TARGET") + "_pch.pch"; |
|
204 // Add linking of precompObj (required for whole precompiled classes) |
|
205 project->values("OBJECTS") += precompObj; |
|
206 // Add pch file to cleanup |
|
207 project->values("QMAKE_CLEAN") += precompPch; |
|
208 // Return to variable pool |
|
209 project->values("PRECOMPILED_OBJECT") = QStringList(precompObj); |
|
210 project->values("PRECOMPILED_PCH") = QStringList(precompPch); |
|
211 } |
|
212 |
|
213 QString version = project->first("TARGET_VERSION_EXT"); |
|
214 if(project->isActiveConfig("shared")) { |
|
215 project->values("QMAKE_CLEAN").append(project->first("DESTDIR") + project->first("TARGET") + version + ".exp"); |
|
216 } |
|
217 if(project->isActiveConfig("debug")) { |
|
218 project->values("QMAKE_CLEAN").append(project->first("DESTDIR") + project->first("TARGET") + version + ".pdb"); |
|
219 project->values("QMAKE_CLEAN").append(project->first("DESTDIR") + project->first("TARGET") + version + ".ilk"); |
|
220 project->values("QMAKE_CLEAN").append("vc*.pdb"); |
|
221 project->values("QMAKE_CLEAN").append("vc*.idb"); |
|
222 } |
|
223 } |
|
224 |
|
225 void NmakeMakefileGenerator::writeLibDirPart(QTextStream &t) |
|
226 { |
|
227 QStringList libDirs = project->values("QMAKE_LIBDIR"); |
|
228 for (int i = 0; i < libDirs.size(); ++i) |
|
229 libDirs[i].remove("\""); |
|
230 t << valGlue(libDirs,"/LIBPATH:\"","\" /LIBPATH:\"","\"") << " "; |
|
231 } |
|
232 |
|
233 void NmakeMakefileGenerator::writeImplicitRulesPart(QTextStream &t) |
|
234 { |
|
235 t << ".SUFFIXES:"; |
|
236 for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit) |
|
237 t << " " << (*cit); |
|
238 for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) |
|
239 t << " " << (*cppit); |
|
240 t << endl << endl; |
|
241 |
|
242 if(!project->isActiveConfig("no_batch")) { |
|
243 // Batchmode doesn't use the non implicit rules QMAKE_RUN_CXX & QMAKE_RUN_CC |
|
244 project->variables().remove("QMAKE_RUN_CXX"); |
|
245 project->variables().remove("QMAKE_RUN_CC"); |
|
246 |
|
247 QHash<QString, void*> source_directories; |
|
248 source_directories.insert(".", (void*)1); |
|
249 QString directories[] = { QString("UI_SOURCES_DIR"), QString("UI_DIR"), QString() }; |
|
250 for(int y = 0; !directories[y].isNull(); y++) { |
|
251 QString dirTemp = project->first(directories[y]); |
|
252 if (dirTemp.endsWith("\\")) |
|
253 dirTemp.truncate(dirTemp.length()-1); |
|
254 if(!dirTemp.isEmpty()) |
|
255 source_directories.insert(dirTemp, (void*)1); |
|
256 } |
|
257 QString srcs[] = { QString("SOURCES"), QString("GENERATED_SOURCES"), QString() }; |
|
258 for(int x = 0; !srcs[x].isNull(); x++) { |
|
259 QStringList &l = project->values(srcs[x]); |
|
260 for(QStringList::Iterator sit = l.begin(); sit != l.end(); ++sit) { |
|
261 QString sep = "\\"; |
|
262 if((*sit).indexOf(sep) == -1) |
|
263 sep = "/"; |
|
264 QString dir = (*sit).section(sep, 0, -2); |
|
265 if(!dir.isEmpty() && !source_directories[dir]) |
|
266 source_directories.insert(dir, (void*)1); |
|
267 } |
|
268 } |
|
269 |
|
270 for(QHash<QString, void*>::Iterator it(source_directories.begin()); it != source_directories.end(); ++it) { |
|
271 if(it.key().isEmpty()) |
|
272 continue; |
|
273 QString objDir = var("OBJECTS_DIR"); |
|
274 if (objDir == ".\\") |
|
275 objDir = ""; |
|
276 for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) |
|
277 t << "{" << it.key() << "}" << (*cppit) << "{" << objDir << "}" << Option::obj_ext << "::\n\t" |
|
278 << var("QMAKE_RUN_CXX_IMP_BATCH").replace(QRegExp("\\$@"), var("OBJECTS_DIR")) << endl << "\t$<" << endl << "<<" << endl << endl; |
|
279 for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit) |
|
280 t << "{" << it.key() << "}" << (*cit) << "{" << objDir << "}" << Option::obj_ext << "::\n\t" |
|
281 << var("QMAKE_RUN_CC_IMP_BATCH").replace(QRegExp("\\$@"), var("OBJECTS_DIR")) << endl << "\t$<" << endl << "<<" << endl << endl; |
|
282 } |
|
283 } else { |
|
284 for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) |
|
285 t << (*cppit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CXX_IMP") << endl << endl; |
|
286 for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit) |
|
287 t << (*cit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CC_IMP") << endl << endl; |
|
288 } |
|
289 |
|
290 } |
|
291 |
|
292 void NmakeMakefileGenerator::writeBuildRulesPart(QTextStream &t) |
|
293 { |
|
294 t << "first: all" << endl; |
|
295 t << "all: " << fileFixify(Option::output.fileName()) << " " << varGlue("ALL_DEPS"," "," "," ") << "$(DESTDIR_TARGET)" << endl << endl; |
|
296 t << "$(DESTDIR_TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) " << var("POST_TARGETDEPS"); |
|
297 |
|
298 if(!project->isEmpty("QMAKE_PRE_LINK")) |
|
299 t << "\n\t" <<var("QMAKE_PRE_LINK"); |
|
300 if(project->isActiveConfig("staticlib")) { |
|
301 t << "\n\t" << "$(LIBAPP) $(LIBFLAGS) /OUT:$(DESTDIR_TARGET) @<<" << "\n\t " |
|
302 << "$(OBJECTS)"; |
|
303 } else { |
|
304 t << "\n\t" << "$(LINK) $(LFLAGS) /OUT:$(DESTDIR_TARGET) @<< " << "\n\t " |
|
305 << "$(OBJECTS) $(LIBS)"; |
|
306 } |
|
307 t << endl << "<<"; |
|
308 QString signature = !project->isEmpty("SIGNATURE_FILE") ? var("SIGNATURE_FILE") : var("DEFAULT_SIGNATURE"); |
|
309 bool useSignature = !signature.isEmpty() && !project->isActiveConfig("staticlib") && |
|
310 !project->isEmpty("CE_SDK") && !project->isEmpty("CE_ARCH"); |
|
311 if(useSignature) { |
|
312 t << "\n\tsigntool sign /F " << signature << " $(DESTDIR_TARGET)"; |
|
313 } |
|
314 if(!project->isEmpty("QMAKE_POST_LINK")) { |
|
315 if (useSignature) |
|
316 t << " && " << var("QMAKE_POST_LINK"); |
|
317 else |
|
318 t << "\n\t" << var("QMAKE_POST_LINK"); |
|
319 } |
|
320 t << endl; |
|
321 } |
|
322 |
|
323 QT_END_NAMESPACE |