unifiy initialization of QMAKE_LIBS{,_PRIVATE} among windows generators
[profile/ivi/qtbase.git] / qmake / option.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the qmake application of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "option.h"
43 #include "cachekeys.h"
44 #include <qdir.h>
45 #include <qregexp.h>
46 #include <qhash.h>
47 #include <qdebug.h>
48 #include <qsettings.h>
49 #include <stdlib.h>
50 #include <stdarg.h>
51
52 QT_BEGIN_NAMESPACE
53
54 //convenience
55 const char *Option::application_argv0 = 0;
56 QString Option::prf_ext;
57 QString Option::prl_ext;
58 QString Option::libtool_ext;
59 QString Option::pkgcfg_ext;
60 QString Option::ui_ext;
61 QStringList Option::h_ext;
62 QString Option::cpp_moc_ext;
63 QStringList Option::cpp_ext;
64 QStringList Option::c_ext;
65 QString Option::obj_ext;
66 QString Option::lex_ext;
67 QString Option::yacc_ext;
68 QString Option::pro_ext;
69 QString Option::dir_sep;
70 QString Option::dirlist_sep;
71 QString Option::h_moc_mod;
72 QString Option::yacc_mod;
73 QString Option::lex_mod;
74 QString Option::res_ext;
75 char Option::field_sep;
76
77 //mode
78 Option::QMAKE_MODE Option::qmake_mode = Option::QMAKE_GENERATE_NOTHING;
79
80 //all modes
81 QString Option::qmake_abslocation;
82 int Option::warn_level = WarnLogic | WarnDeprecated;
83 int Option::debug_level = 0;
84 QFile Option::output;
85 QString Option::output_dir;
86 bool Option::recursive = false;
87 QStringList Option::before_user_vars;
88 QStringList Option::after_user_vars;
89 QString Option::user_template;
90 QString Option::user_template_prefix;
91
92 //QMAKE_*_PROPERTY stuff
93 QStringList Option::prop::properties;
94
95 //QMAKE_GENERATE_PROJECT stuff
96 bool Option::projfile::do_pwd = true;
97 QStringList Option::projfile::project_dirs;
98
99 //QMAKE_GENERATE_MAKEFILE stuff
100 QString Option::mkfile::qmakespec;
101 QString Option::mkfile::xqmakespec;
102 int Option::mkfile::cachefile_depth = -1;
103 bool Option::mkfile::do_deps = true;
104 bool Option::mkfile::do_mocs = true;
105 bool Option::mkfile::do_dep_heuristics = true;
106 bool Option::mkfile::do_preprocess = false;
107 bool Option::mkfile::do_stub_makefile = false;
108 bool Option::mkfile::do_cache = true;
109 QString Option::mkfile::source_root;
110 QString Option::mkfile::build_root;
111 QString Option::mkfile::cachefile;
112 QStringList Option::mkfile::project_files;
113 QString Option::mkfile::qmakespec_commandline;
114 QString Option::mkfile::xqmakespec_commandline;
115
116 static Option::QMAKE_MODE default_mode(QString progname)
117 {
118     int s = progname.lastIndexOf(QDir::separator());
119     if(s != -1)
120         progname = progname.right(progname.length() - (s + 1));
121     if(progname == "qmakegen")
122         return Option::QMAKE_GENERATE_PROJECT;
123     else if(progname == "qt-config")
124         return Option::QMAKE_QUERY_PROPERTY;
125     return Option::QMAKE_GENERATE_MAKEFILE;
126 }
127
128 static QString detectProjectFile(const QString &path)
129 {
130     QString ret;
131     QDir dir(path);
132     if(dir.exists(dir.dirName() + Option::pro_ext)) {
133         ret = dir.filePath(dir.dirName()) + Option::pro_ext;
134     } else { //last try..
135         QStringList profiles = dir.entryList(QStringList("*" + Option::pro_ext));
136         if(profiles.count() == 1)
137             ret = dir.filePath(profiles.at(0));
138     }
139     return ret;
140 }
141
142 static QString cleanSpec(const QString &spec)
143 {
144     QString ret = QDir::cleanPath(spec);
145     if (ret.contains('/')) {
146         const QFileInfo specDirInfo(ret);
147         if (specDirInfo.exists() && specDirInfo.isDir())
148             ret = QDir::cleanPath(specDirInfo.absoluteFilePath());
149     }
150     return ret;
151 }
152
153 QString project_builtin_regx();
154 bool usage(const char *a0)
155 {
156     fprintf(stdout, "Usage: %s [mode] [options] [files]\n"
157             "\n"
158             "QMake has two modes, one mode for generating project files based on\n"
159             "some heuristics, and the other for generating makefiles. Normally you\n"
160             "shouldn't need to specify a mode, as makefile generation is the default\n"
161             "mode for qmake, but you may use this to test qmake on an existing project\n"
162             "\n"
163             "Mode:\n"
164             "  -project       Put qmake into project file generation mode%s\n"
165             "                 In this mode qmake interprets files as files to\n"
166             "                 be built,\n"
167             "                 defaults to %s\n"
168             "                 Note: The created .pro file probably will \n"
169             "                 need to be edited. For example add the QT variable to \n"
170             "                 specify what modules are required.\n"
171             "  -makefile      Put qmake into makefile generation mode%s\n"
172             "                 In this mode qmake interprets files as project files to\n"
173             "                 be processed, if skipped qmake will try to find a project\n"
174             "                 file in your current working directory\n"
175             "\n"
176             "Warnings Options:\n"
177             "  -Wnone         Turn off all warnings; specific ones may be re-enabled by\n"
178             "                 later -W options\n"
179             "  -Wall          Turn on all warnings\n"
180             "  -Wparser       Turn on parser warnings\n"
181             "  -Wlogic        Turn on logic warnings (on by default)\n"
182             "  -Wdeprecated   Turn on deprecation warnings (on by default)\n"
183             "\n"
184             "Options:\n"
185             "   * You can place any variable assignment in options and it will be     *\n"
186             "   * processed as if it was in [files]. These assignments will be parsed *\n"
187             "   * before [files].                                                     *\n"
188             "  -o file        Write output to file\n"
189             "  -d             Increase debug level\n"
190             "  -t templ       Overrides TEMPLATE as templ\n"
191             "  -tp prefix     Overrides TEMPLATE so that prefix is prefixed into the value\n"
192             "  -help          This help\n"
193             "  -v             Version information\n"
194             "  -after         All variable assignments after this will be\n"
195             "                 parsed after [files]\n"
196             "  -norecursive   Don't do a recursive search\n"
197             "  -recursive     Do a recursive search\n"
198             "  -set <prop> <value> Set persistent property\n"
199             "  -unset <prop>  Unset persistent property\n"
200             "  -query <prop>  Query persistent property. Show all if <prop> is empty.\n"
201             "  -cache file    Use file as cache           [makefile mode only]\n"
202             "  -spec spec     Use spec as QMAKESPEC       [makefile mode only]\n"
203             "  -nocache       Don't use a cache file      [makefile mode only]\n"
204             "  -nodepend      Don't generate dependencies [makefile mode only]\n"
205             "  -nomoc         Don't generate moc targets  [makefile mode only]\n"
206             "  -nopwd         Don't look for files in pwd [project mode only]\n"
207             ,a0,
208             default_mode(a0) == Option::QMAKE_GENERATE_PROJECT  ? " (default)" : "", project_builtin_regx().toLatin1().constData(),
209             default_mode(a0) == Option::QMAKE_GENERATE_MAKEFILE ? " (default)" : ""
210         );
211     return false;
212 }
213
214 int
215 Option::parseCommandLine(int argc, char **argv, int skip)
216 {
217     QStringList user_configs;
218
219     bool before = true;
220     for(int x = skip; x < argc; x++) {
221         if(*argv[x] == '-' && strlen(argv[x]) > 1) { /* options */
222             QString opt = argv[x] + 1;
223
224             //first param is a mode, or we default
225             if(x == 1) {
226                 bool specified = true;
227                 if(opt == "project") {
228                     Option::recursive = true;
229                     Option::qmake_mode = Option::QMAKE_GENERATE_PROJECT;
230                 } else if(opt == "prl") {
231                     Option::mkfile::do_deps = false;
232                     Option::mkfile::do_mocs = false;
233                     Option::qmake_mode = Option::QMAKE_GENERATE_PRL;
234                 } else if(opt == "set") {
235                     Option::qmake_mode = Option::QMAKE_SET_PROPERTY;
236                 } else if(opt == "unset") {
237                     Option::qmake_mode = Option::QMAKE_UNSET_PROPERTY;
238                 } else if(opt == "query") {
239                     Option::qmake_mode = Option::QMAKE_QUERY_PROPERTY;
240                 } else if(opt == "makefile") {
241                     Option::qmake_mode = Option::QMAKE_GENERATE_MAKEFILE;
242                 } else {
243                     specified = false;
244                 }
245                 if(specified)
246                     continue;
247             }
248             //all modes
249             if(opt == "o" || opt == "output") {
250                 Option::output.setFileName(argv[++x]);
251             } else if(opt == "after") {
252                 before = false;
253             } else if(opt == "t" || opt == "template") {
254                 Option::user_template = argv[++x];
255             } else if(opt == "tp" || opt == "template_prefix") {
256                 Option::user_template_prefix = argv[++x];
257             } else if(opt == "unix") {
258                 Option::dir_sep = "/";
259             } else if(opt == "win32") {
260                 Option::dir_sep = "\\";
261             } else if(opt == "d") {
262                 Option::debug_level++;
263             } else if(opt == "version" || opt == "v" || opt == "-version") {
264                 fprintf(stdout,
265                         "QMake version %s\n"
266                         "Using Qt version %s in %s\n",
267                         qmake_version(), QT_VERSION_STR,
268                         QLibraryInfo::location(QLibraryInfo::LibrariesPath).toLatin1().constData());
269 #ifdef QMAKE_OPENSOURCE_VERSION
270                 fprintf(stdout, "QMake is Open Source software from Nokia Corporation and/or its subsidiary(-ies).\n");
271 #endif
272                 return Option::QMAKE_CMDLINE_BAIL;
273             } else if(opt == "h" || opt == "help") {
274                 return Option::QMAKE_CMDLINE_SHOW_USAGE;
275             } else if(opt == "Wall") {
276                 Option::warn_level |= WarnAll;
277             } else if(opt == "Wparser") {
278                 Option::warn_level |= WarnParser;
279             } else if(opt == "Wlogic") {
280                 Option::warn_level |= WarnLogic;
281             } else if(opt == "Wdeprecated") {
282                 Option::warn_level |= WarnDeprecated;
283             } else if(opt == "Wnone") {
284                 Option::warn_level = WarnNone;
285             } else if(opt == "r" || opt == "recursive") {
286                 Option::recursive = true;
287             } else if(opt == "nr" || opt == "norecursive") {
288                 Option::recursive = false;
289             } else if(opt == "config") {
290                 user_configs += argv[++x];
291             } else {
292                 if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
293                    Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
294                     if(opt == "nodepend" || opt == "nodepends") {
295                         Option::mkfile::do_deps = false;
296                     } else if(opt == "nomoc") {
297                         Option::mkfile::do_mocs = false;
298                     } else if(opt == "nocache") {
299                         Option::mkfile::do_cache = false;
300                     } else if(opt == "createstub") {
301                         Option::mkfile::do_stub_makefile = true;
302                     } else if(opt == "nodependheuristics") {
303                         Option::mkfile::do_dep_heuristics = false;
304                     } else if(opt == "E") {
305                         Option::mkfile::do_preprocess = true;
306                     } else if(opt == "cache") {
307                         Option::mkfile::cachefile = argv[++x];
308                     } else if(opt == "platform" || opt == "spec") {
309                         Option::mkfile::qmakespec = cleanSpec(argv[++x]);
310                         Option::mkfile::qmakespec_commandline = argv[x];
311                     } else if (opt == "xplatform" || opt == "xspec") {
312                         Option::mkfile::xqmakespec = cleanSpec(argv[++x]);
313                         Option::mkfile::xqmakespec_commandline = argv[x];
314                     } else {
315                         fprintf(stderr, "***Unknown option -%s\n", opt.toLatin1().constData());
316                         return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
317                     }
318                 } else if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
319                     if(opt == "nopwd") {
320                         Option::projfile::do_pwd = false;
321                     } else {
322                         fprintf(stderr, "***Unknown option -%s\n", opt.toLatin1().constData());
323                         return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
324                     }
325                 }
326             }
327         } else {
328             QString arg = argv[x];
329             if(arg.indexOf('=') != -1) {
330                 if(before)
331                     Option::before_user_vars.append(arg);
332                 else
333                     Option::after_user_vars.append(arg);
334             } else {
335                 bool handled = true;
336                 if(Option::qmake_mode == Option::QMAKE_QUERY_PROPERTY ||
337                     Option::qmake_mode == Option::QMAKE_SET_PROPERTY ||
338                     Option::qmake_mode == Option::QMAKE_UNSET_PROPERTY) {
339                     Option::prop::properties.append(arg);
340                 } else {
341                     QFileInfo fi(arg);
342                     if(!fi.makeAbsolute()) //strange
343                         arg = fi.filePath();
344                     if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
345                        Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
346                         if(fi.isDir()) {
347                             QString proj = detectProjectFile(arg);
348                             if (!proj.isNull())
349                                 arg = proj;
350                         }
351                         Option::mkfile::project_files.append(arg);
352                     } else if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) {
353                         Option::projfile::project_dirs.append(arg);
354                     } else {
355                         handled = false;
356                     }
357                 }
358                 if(!handled) {
359                     return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
360                 }
361             }
362         }
363     }
364
365     if (!user_configs.isEmpty())
366         Option::before_user_vars += "CONFIG += " + user_configs.join(" ");
367
368     if (Option::mkfile::xqmakespec.isEmpty())
369         Option::mkfile::xqmakespec = Option::mkfile::qmakespec;
370
371     return Option::QMAKE_CMDLINE_SUCCESS;
372 }
373
374 int
375 Option::init(int argc, char **argv)
376 {
377     Option::application_argv0 = 0;
378     Option::prf_ext = ".prf";
379     Option::pro_ext = ".pro";
380 #ifdef Q_OS_WIN
381     Option::dir_sep = "\\";
382     Option::dirlist_sep = ";";
383 #else
384     Option::dir_sep = "/";
385     Option::dirlist_sep = ":";
386 #endif
387     Option::field_sep = ' ';
388
389     if(argc && argv) {
390         Option::application_argv0 = argv[0];
391         QString argv0 = argv[0];
392         if(Option::qmake_mode == Option::QMAKE_GENERATE_NOTHING)
393             Option::qmake_mode = default_mode(argv0);
394         if(!argv0.isEmpty() && !QFileInfo(argv0).isRelative()) {
395             Option::qmake_abslocation = argv0;
396         } else if (argv0.contains(QLatin1Char('/'))
397 #ifdef Q_OS_WIN
398                    || argv0.contains(QLatin1Char('\\'))
399 #endif
400             ) { //relative PWD
401             Option::qmake_abslocation = QDir::current().absoluteFilePath(argv0);
402         } else { //in the PATH
403             QByteArray pEnv = qgetenv("PATH");
404             QDir currentDir = QDir::current();
405 #ifdef Q_OS_WIN
406             QStringList paths = QString::fromLocal8Bit(pEnv).split(QLatin1String(";"));
407 #else
408             QStringList paths = QString::fromLocal8Bit(pEnv).split(QLatin1String(":"));
409 #endif
410             for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) {
411                 if ((*p).isEmpty())
412                     continue;
413                 QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + argv0);
414 #ifdef Q_OS_WIN
415                 candidate += ".exe";
416 #endif
417                 if (QFile::exists(candidate)) {
418                     Option::qmake_abslocation = candidate;
419                     break;
420                 }
421             }
422         }
423         if(!Option::qmake_abslocation.isNull())
424             Option::qmake_abslocation = QDir::cleanPath(Option::qmake_abslocation);
425         else // This is rather unlikely to ever happen on a modern system ...
426             Option::qmake_abslocation = QLibraryInfo::rawLocation(QLibraryInfo::HostBinariesPath,
427                                                                   QLibraryInfo::EffectivePaths) +
428 #ifdef Q_OS_WIN
429                     "/qmake.exe";
430 #else
431                     "/qmake";
432 #endif
433     } else {
434         Option::qmake_mode = Option::QMAKE_GENERATE_MAKEFILE;
435     }
436
437     const QByteArray envflags = qgetenv("QMAKEFLAGS");
438     if (!envflags.isNull()) {
439         int env_argc = 0, env_size = 0, currlen=0;
440         char quote = 0, **env_argv = NULL;
441         for (int i = 0; i < envflags.size(); ++i) {
442             if (!quote && (envflags.at(i) == '\'' || envflags.at(i) == '"')) {
443                 quote = envflags.at(i);
444             } else if (envflags.at(i) == quote) {
445                 quote = 0;
446             } else if (!quote && envflags.at(i) == ' ') {
447                 if (currlen && env_argv && env_argv[env_argc]) {
448                     env_argv[env_argc][currlen] = '\0';
449                     currlen = 0;
450                     env_argc++;
451                 }
452             } else {
453                 if(!env_argv || env_argc > env_size) {
454                     env_argv = (char **)realloc(env_argv, sizeof(char *)*(env_size+=10));
455                     for(int i2 = env_argc; i2 < env_size; i2++)
456                         env_argv[i2] = NULL;
457                 }
458                 if(!env_argv[env_argc]) {
459                     currlen = 0;
460                     env_argv[env_argc] = (char*)malloc(255);
461                 }
462                 if(currlen < 255)
463                     env_argv[env_argc][currlen++] = envflags.at(i);
464             }
465         }
466         if(env_argv) {
467             if(env_argv[env_argc]) {
468                 env_argv[env_argc][currlen] = '\0';
469                 currlen = 0;
470                 env_argc++;
471             }
472             parseCommandLine(env_argc, env_argv);
473             for(int i2 = 0; i2 < env_size; i2++) {
474                 if(env_argv[i2])
475                     free(env_argv[i2]);
476             }
477             free(env_argv);
478         }
479     }
480     if(argc && argv) {
481         int ret = parseCommandLine(argc, argv, 1);
482         if(ret != Option::QMAKE_CMDLINE_SUCCESS) {
483             if ((ret & Option::QMAKE_CMDLINE_SHOW_USAGE) != 0)
484                 usage(argv[0]);
485             return ret;
486             //return ret == QMAKE_CMDLINE_SHOW_USAGE ? usage(argv[0]) : false;
487         }
488     }
489
490     //last chance for defaults
491     if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE ||
492         Option::qmake_mode == Option::QMAKE_GENERATE_PRL) {
493         if (Option::mkfile::xqmakespec.isEmpty())
494             Option::mkfile::xqmakespec = QString::fromLocal8Bit(qgetenv("XQMAKESPEC").constData());
495         if (Option::mkfile::qmakespec.isEmpty()) {
496             Option::mkfile::qmakespec = QString::fromLocal8Bit(qgetenv("QMAKESPEC").constData());
497             if (Option::mkfile::xqmakespec.isEmpty())
498                 Option::mkfile::xqmakespec = Option::mkfile::qmakespec;
499         }
500
501         //try REALLY hard to do it for them, lazy..
502         if(Option::mkfile::project_files.isEmpty()) {
503             QString proj = detectProjectFile(qmake_getpwd());
504             if(!proj.isNull())
505                 Option::mkfile::project_files.append(proj);
506 #ifndef QT_BUILD_QMAKE_LIBRARY
507             if(Option::mkfile::project_files.isEmpty()) {
508                 usage(argv[0]);
509                 return Option::QMAKE_CMDLINE_ERROR;
510             }
511 #endif
512         }
513     }
514
515     return QMAKE_CMDLINE_SUCCESS;
516 }
517
518 void Option::prepareProject(const QString &pfile)
519 {
520     QString srcpath = (pfile != "-")
521             ? QDir::cleanPath(QFileInfo(pfile).absolutePath()) : qmake_getpwd();
522     if (srcpath != output_dir) {
523         if (!srcpath.endsWith(QLatin1Char('/')))
524             srcpath += QLatin1Char('/');
525         QString dstpath = output_dir;
526         if (!dstpath.endsWith(QLatin1Char('/')))
527             dstpath += QLatin1Char('/');
528         int srcLen = srcpath.length();
529         int dstLen = dstpath.length();
530         int lastSl = -1;
531         while (++lastSl, srcpath.at(--srcLen) == dstpath.at(--dstLen))
532             if (srcpath.at(srcLen) == QLatin1Char('/'))
533                 lastSl = 0;
534         mkfile::source_root = srcpath.left(srcLen + lastSl);
535         mkfile::build_root = dstpath.left(dstLen + lastSl);
536     } else {
537         mkfile::source_root.clear();
538     }
539 }
540
541 bool Option::postProcessProject(QMakeProject *project)
542 {
543     Option::cpp_ext = project->values("QMAKE_EXT_CPP");
544     Option::h_ext = project->values("QMAKE_EXT_H");
545     Option::c_ext = project->values("QMAKE_EXT_C");
546     Option::res_ext = project->first("QMAKE_EXT_RES");
547     Option::pkgcfg_ext = project->first("QMAKE_EXT_PKGCONFIG");
548     Option::libtool_ext = project->first("QMAKE_EXT_LIBTOOL");
549     Option::prl_ext = project->first("QMAKE_EXT_PRL");
550     Option::ui_ext = project->first("QMAKE_EXT_UI");
551     Option::cpp_moc_ext = project->first("QMAKE_EXT_CPP_MOC");
552     Option::lex_ext = project->first("QMAKE_EXT_LEX");
553     Option::yacc_ext = project->first("QMAKE_EXT_YACC");
554     Option::obj_ext = project->first("QMAKE_EXT_OBJ");
555     Option::h_moc_mod = project->first("QMAKE_H_MOD_MOC");
556     Option::lex_mod = project->first("QMAKE_MOD_LEX");
557     Option::yacc_mod = project->first("QMAKE_MOD_YACC");
558     Option::dir_sep = project->first("QMAKE_DIR_SEP");
559
560     if (Option::output_dir.startsWith(project->buildRoot()))
561         Option::mkfile::cachefile_depth =
562                 Option::output_dir.mid(project->buildRoot().length()).count('/');
563
564     return true;
565 }
566
567 QString
568 Option::fixString(QString string, uchar flags)
569 {
570     //const QString orig_string = string;
571     static QHash<FixStringCacheKey, QString> *cache = 0;
572     if(!cache) {
573         cache = new QHash<FixStringCacheKey, QString>;
574         qmakeAddCacheClear(qmakeDeleteCacheClear<QHash<FixStringCacheKey, QString> >, (void**)&cache);
575     }
576     FixStringCacheKey cacheKey(string, flags);
577
578     QHash<FixStringCacheKey, QString>::const_iterator it = cache->constFind(cacheKey);
579
580     if (it != cache->constEnd()) {
581         //qDebug() << "Fix (cached) " << orig_string << "->" << it.value();
582         return it.value();
583     }
584
585     //fix the environment variables
586     if(flags & Option::FixEnvVars) {
587         int rep;
588         static QRegExp reg_var("\\$\\(.*\\)");
589         reg_var.setMinimal(true);
590         while((rep = reg_var.indexIn(string)) != -1)
591             string.replace(rep, reg_var.matchedLength(),
592                            QString::fromLocal8Bit(qgetenv(string.mid(rep + 2, reg_var.matchedLength() - 3).toLatin1().constData()).constData()));
593     }
594
595     //canonicalize it (and treat as a path)
596     if(flags & Option::FixPathCanonicalize) {
597 #if 0
598         string = QFileInfo(string).canonicalFilePath();
599 #endif
600         string = QDir::cleanPath(string);
601     }
602
603     if(string.length() > 2 && string[0].isLetter() && string[1] == QLatin1Char(':'))
604         string[0] = string[0].toLower();
605
606     bool localSep = (flags & Option::FixPathToLocalSeparators) != 0;
607     bool targetSep = (flags & Option::FixPathToTargetSeparators) != 0;
608     bool normalSep = (flags & Option::FixPathToNormalSeparators) != 0;
609
610     // either none or only one active flag
611     Q_ASSERT(localSep + targetSep + normalSep <= 1);
612     //fix separators
613     if (flags & Option::FixPathToNormalSeparators) {
614         string = string.replace('\\', '/');
615     } else if (flags & Option::FixPathToLocalSeparators) {
616 #if defined(Q_OS_WIN32)
617         string = string.replace('/', '\\');
618 #else
619         string = string.replace('\\', '/');
620 #endif
621     } else if(flags & Option::FixPathToTargetSeparators) {
622         string = string.replace('/', Option::dir_sep).replace('\\', Option::dir_sep);
623     }
624
625     if ((string.startsWith("\"") && string.endsWith("\"")) ||
626         (string.startsWith("\'") && string.endsWith("\'")))
627         string = string.mid(1, string.length()-2);
628
629     //cache
630     //qDebug() << "Fix" << orig_string << "->" << string;
631     cache->insert(cacheKey, string);
632     return string;
633 }
634
635 const char *qmake_version()
636 {
637     static char *ret = NULL;
638     if(ret)
639         return ret;
640     ret = (char *)malloc(15);
641     qmakeAddCacheClear(qmakeFreeCacheClear, (void**)&ret);
642 #if defined(_MSC_VER) && _MSC_VER >= 1400
643     sprintf_s(ret, 15, "%d.%02d%c", QMAKE_VERSION_MAJOR, QMAKE_VERSION_MINOR, 'a' + QMAKE_VERSION_PATCH);
644 #else
645     sprintf(ret, "%d.%02d%c", QMAKE_VERSION_MAJOR, QMAKE_VERSION_MINOR, 'a' + QMAKE_VERSION_PATCH);
646 #endif
647     return ret;
648 }
649
650 void debug_msg_internal(int level, const char *fmt, ...)
651 {
652     if(Option::debug_level < level)
653         return;
654     fprintf(stderr, "DEBUG %d: ", level);
655     {
656         va_list ap;
657         va_start(ap, fmt);
658         vfprintf(stderr, fmt, ap);
659         va_end(ap);
660     }
661     fprintf(stderr, "\n");
662 }
663
664 void warn_msg(QMakeWarn type, const char *fmt, ...)
665 {
666     if(!(Option::warn_level & type))
667         return;
668     fprintf(stderr, "WARNING: ");
669     {
670         va_list ap;
671         va_start(ap, fmt);
672         vfprintf(stderr, fmt, ap);
673         va_end(ap);
674     }
675     fprintf(stderr, "\n");
676 }
677
678 class QMakeCacheClearItem {
679 private:
680     qmakeCacheClearFunc func;
681     void **data;
682 public:
683     QMakeCacheClearItem(qmakeCacheClearFunc f, void **d) : func(f), data(d) { }
684     ~QMakeCacheClearItem() {
685         (*func)(*data);
686         *data = 0;
687     }
688 };
689 static QList<QMakeCacheClearItem*> cache_items;
690
691 void
692 qmakeClearCaches()
693 {
694     qDeleteAll(cache_items);
695     cache_items.clear();
696 }
697
698 void
699 qmakeAddCacheClear(qmakeCacheClearFunc func, void **data)
700 {
701     cache_items.append(new QMakeCacheClearItem(func, data));
702 }
703
704 QString qmake_libraryInfoFile()
705 {
706     if(!Option::qmake_abslocation.isEmpty())
707         return QDir(QFileInfo(Option::qmake_abslocation).absolutePath()).filePath("qt.conf");
708     return QString();
709 }
710
711 QT_END_NAMESPACE