/****************************************************************************
** $Id$
**
** Definition of ________ class.
**
** Created : 970521
**
** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
**
** This file is part of the network module of the Qt GUI Toolkit.
**
** This file may be distributed under the terms of the Q Public License
** as defined by Trolltech AS of Norway and appearing in the file
** LICENSE.QPL included in the packaging of this file.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid Qt Enterprise Edition licenses may use this
** file in accordance with the Qt Commercial License Agreement provided
** with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
**   information about Qt Commercial License Agreements.
** See http://www.trolltech.com/qpl/ for QPL licensing information.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

#include "makefile.h"
#include "option.h"
#include <qdir.h>
#include <qfile.h>
#include <qtextstream.h>
#include <qregexp.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#if defined(Q_OS_UNIX)
#include <unistd.h>
#else
#include <io.h>
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

MakefileGenerator::MakefileGenerator(QMakeProject *p) : init_already(FALSE), moc_aware(FALSE), project(p)
{
}

static char *gimme_buffer(off_t s)
{
    static char *big_buffer = NULL;
    static int big_buffer_size = 0;
    if(!big_buffer || big_buffer_size < s)
 	big_buffer = (char *)realloc(big_buffer, s);
    return big_buffer;
}

bool
MakefileGenerator::generateMocList(QString fn_target)
{
    if(!findMocDestination(fn_target).isEmpty())
	return TRUE;
    QString fn_local = Option::fixPathToLocalOS(fn_target);

    int file = open(fn_local.latin1(), O_RDONLY);
    if(file == -1)
	return FALSE;

    struct stat fst;
    if(fstat(file, &fst))
	return FALSE; //shouldn't happen
    char *big_buffer = gimme_buffer(fst.st_size);

    int total_size_read;
    for(int have_read = total_size_read = 0;
	(have_read = read(file, big_buffer + total_size_read,
			  fst.st_size - total_size_read));
	total_size_read += have_read);
    close(file);

    bool ignore_qobject = FALSE;
    int line_count = 0;
#define COMP_LEN 8 //strlen("Q_OBJECT")
#define OBJ_LEN 8 //strlen("Q_OBJECT")
#define DIS_LEN 10 //strlen("Q_DISPATCH")
    for(int x = 0; x < (total_size_read-COMP_LEN); x++) {
	if(*(big_buffer + x) == '/') {
	    x++;
	    if(total_size_read >= x) {
		if(*(big_buffer + x) == '/') { //c++ style comment
		    for( ;x < total_size_read && *(big_buffer + x) != '\n'; x++);
		    line_count++;
		} else if(*(big_buffer + x) == '*') { //c style comment
		    for( ;x < total_size_read; x++) {
			if(*(big_buffer + x) == 't' || *(big_buffer + x) == 'q') { //ignore
			    if(total_size_read >= (x + 20)) {
				if(!strncmp(big_buffer + x + 1, "make ignore Q_OBJECT", 20)) {
				    debug_msg(2, "Mocgen: %s:%d Found \"qmake ignore Q_OBJECT\"",
					      fn_target.latin1(), line_count);
				    x += 20;
				    ignore_qobject = TRUE;
				}
			    }
			} else if(*(big_buffer + x) == '*') {
			    if(total_size_read >= (x+1) && *(big_buffer + (x+1)) == '/') {
				x += 2;
				break;
			    }
			} else if(*(big_buffer + x) == '\n') {
			    line_count++;
			}
		    }
		}
	    }
	}
#define SYMBOL_CHAR(x) ((x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') || \
			(x <= '0' && x >= '9') || x == '_')
	bool interesting = *(big_buffer+x) == 'Q' && (!strncmp(big_buffer+x, "Q_OBJECT", OBJ_LEN) ||
						      !strncmp(big_buffer+x, "Q_DISPATCH", DIS_LEN));
	if(interesting) {
	    int len = 0;
	    if(!strncmp(big_buffer+x, "Q_OBJECT", OBJ_LEN)) {
		if(ignore_qobject) {
		    debug_msg(2, "Mocgen: %s:%d Ignoring Q_OBJECT", fn_target.latin1(), line_count);
		    interesting = FALSE;
		}
		len=OBJ_LEN;
	    } else if(!strncmp(big_buffer+x, "Q_DISPATH", DIS_LEN)) {
		len=DIS_LEN;
	    }
	    if(SYMBOL_CHAR(*(big_buffer+x+len)))
		interesting = FALSE;
	    if(interesting) {
		*(big_buffer+x+len) = '\0';
		debug_msg(2, "Mocgen: %s:%d Found MOC symbol %s", fn_target.latin1(), line_count, big_buffer+x);

		int ext_pos = fn_target.findRev('.');
		int ext_len = fn_target.length() - ext_pos;
		int dir_pos =  fn_target.findRev(Option::dir_sep, ext_pos);
		QString mocFile;
		if(!project->variables()["MOC_DIR"].isEmpty())
		    mocFile = project->first("MOC_DIR");
		else if(dir_pos != -1)
		    mocFile = fn_target.left(dir_pos+1);

		if(fn_target.right(ext_len) == Option::cpp_ext) {
		    mocFile += fn_target.mid(dir_pos+1, ext_pos - dir_pos-1) + Option::moc_ext;
		    findDependencies(fn_target).append(mocFile);
		    project->variables()["_SRCMOC"].append(mocFile);
		} else if(fn_target.right(ext_len) == Option::h_ext &&
			  project->variables()["HEADERS"].findIndex(fn_target) != -1) {
		    mocFile += Option::moc_mod + fn_target.mid(dir_pos+1, ext_pos - dir_pos-1) + Option::cpp_ext;
		    logicWarn(mocFile, "SOURCES");
		    project->variables()["_HDRMOC"].append(mocFile);
		}

		if(!mocFile.isEmpty()) {
		    mocFile = Option::fixPathToTargetOS(mocFile);
		    mocablesToMOC[cleanFilePath(fn_target)] = mocFile;
		    mocablesFromMOC[cleanFilePath(mocFile)] = fn_target;
		}
		break;
	    }
	}
	    while(x < total_size_read && SYMBOL_CHAR(*(big_buffer+x)))
		x++;
	if(*(big_buffer+x) == '\n')
	    line_count++;
    }
#undef OBJ_LEN
#undef DIS_LEN
    return TRUE;
}

bool
MakefileGenerator::generateDependencies(QPtrList<MakefileDependDir> &dirs, QString fn, bool recurse)
{
    fileFixify(fn);
    QStringList &fndeps = findDependencies(fn);
    if(!fndeps.isEmpty())
	return TRUE;

    fn = Option::fixPathToLocalOS(fn, FALSE);

    QString fndir;
    int dl = fn.findRev(Option::dir_sep);
    if(dl != -1)
	fndir = fn.left(dl+1);

    int file = open(fn.latin1(), O_RDONLY);
    if(file == -1)
	return FALSE;

    struct stat fst;
    if(fstat(file, &fst))
	return FALSE; //shouldn't happen
    char *big_buffer = gimme_buffer(fst.st_size);

    int total_size_read;
    for(int have_read = total_size_read = 0;
	(have_read = read(file, big_buffer + total_size_read,
			  fst.st_size - total_size_read));
	total_size_read += have_read);
    close(file);

    enum { UI_FILE, C_FILE } ftype;
    if(fn.right(3) == ".ui")
	ftype = UI_FILE;
    else
	ftype = C_FILE;


    for(int x = 0; x < total_size_read; x++) {
	QString inc;
	if(ftype == C_FILE) {
	    if(*(big_buffer + x) == '/') {
		x++;
		if(total_size_read >= x) {
		    if(*(big_buffer + x) == '/') { //c++ style comment
			for( ; x < total_size_read && *(big_buffer + x) != '\n'; x++);
		    } else if(*(big_buffer + x) == '*') { //c style comment
			for( ; x < total_size_read; x++) {
			    if(*(big_buffer + x) == '*') {
				if(total_size_read >= (x+1) && *(big_buffer + (x+1)) == '/') {
				    x += 2;
				    break;
				}
			    }
			}
		    }
		}
	    }
	    if(*(big_buffer + x) == '#') {
		x++;
		// skip spaces and tabs between the hash and text, so we can handle
		// #  include
		while(x < total_size_read &&
		      (*(big_buffer+x) == ' ' || *(big_buffer+x) == '\t'))
		    x++;
		if(total_size_read >= x + 8 && !strncmp(big_buffer + x, "include ", 8)) {
		    x += 8;
		    char term = *(big_buffer + x);
		    if(term == '"');
		    else if(term == '<')
			term = '>';
		    else
			continue; //wtf?
		    x++;

		    int inc_len;
		    for(inc_len = 0; *(big_buffer + x + inc_len) != term; inc_len++);
		    *(big_buffer + x + inc_len) = '\0';

		    inc = big_buffer + x;
		}
	    }
	} else if(ftype == UI_FILE) {
	    // skip whitespaces
	    while(x < total_size_read &&
		  (*(big_buffer+x) == ' ' || *(big_buffer+x) == '\t'))
		x++;
	    if(*(big_buffer + x) == '<') {
		x++;
		if(total_size_read >= x + 8 && !strncmp(big_buffer + x, "include ", 8)) {
		    x += 8;
		    while(*(big_buffer + (x++)) != '>');
		    int inc_len = 0;
		    for( ; *(big_buffer + x + inc_len) != '<'; inc_len++);
		    *(big_buffer + x + inc_len) = '\0';

		    inc = big_buffer + x;
		}
	    }
	}

	if(!inc.isEmpty()) {
	    QString fqn;
	    if(project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH") && !stat(fndir + inc, &fst)) {
		fqn = fndir + inc;
	    } else {
		if((Option::target_mode == Option::TARG_MAC9_MODE && inc.find(':')) ||
		   (Option::target_mode == Option::TARG_WIN_MODE && inc[1] != ':') ||
		   ((Option::target_mode == Option::TARG_UNIX_MODE || Option::target_mode == Option::TARG_MACX_MODE) &&
		    inc[0] != '/')) {
		    for(MakefileDependDir *mdd = dirs.first(); mdd; mdd = dirs.next() ) {
			if(QFile::exists(mdd->local_dir + QDir::separator() + inc))
			    fqn = mdd->real_dir + QDir::separator() + inc;
		    }
		}
	    }
	    if(fqn.isEmpty()) {
		//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) { //some heuristics..
		    //is it a file from a .ui?
		    int extn = inc.findRev('.');
		    if(extn != -1 && inc.findRev(Option::dir_sep) < extn) {
			QString uip = inc.left(extn) + Option::ui_ext + "$";
			QStringList uil = project->variables()["FORMS"];
			for(QStringList::Iterator it = uil.begin(); it != uil.end(); ++it) {
			    QString s = (*it);
			    if(!project->isEmpty("UI_DIR")) {
				int d = s.findRev(Option::dir_sep) + 1;
				s = project->first("UI_DIR") + s.right(s.length()-d);
			    }
			    if(s.find(QRegExp(uip)) != -1) {
				fqn = s.left(s.length()-3) + inc.right(inc.length()-extn);
				break;
			    }
			}
		    }
		}
		if(!Option::mkfile::do_dep_heuristics || fqn.isEmpty()) //I give up
		    continue;
	    }

	    fqn = Option::fixPathToTargetOS(fqn, FALSE);
	    fileFixify(fqn);

	    if(fndeps.findIndex(fqn) == -1)
		fndeps.append(fqn);
	}
	//read past new line now..
	for( ; x < total_size_read && (*(big_buffer + x) != '\n'); x++);
    }

    if(recurse) {
	for(QStringList::Iterator fnit = fndeps.begin(); fnit != fndeps.end(); ++fnit) {
	    generateDependencies(dirs, (*fnit), recurse);
	    QStringList &deplist = findDependencies((*fnit));
	    for(QStringList::Iterator it = deplist.begin(); it != deplist.end(); ++it)
		if(fndeps.findIndex((*it)) == -1)
		    fndeps.append((*it));
	}
    }
    debug_msg(2, "Dependancies: %s -> %s", fn.latin1(), fndeps.join(" :: ").latin1());
    return TRUE;
}

void
MakefileGenerator::init()
{
    if(init_already)
	return;
    init_already = TRUE;

    QMap<QString, QStringList> &v = project->variables();
    { //opaths
	if ( !v["QMAKE_ABSOLUTE_SOURCE_PATH"].isEmpty() ) {
	    QString &asp = v["QMAKE_ABSOLUTE_SOURCE_PATH"].first();
	    asp = Option::fixPathToTargetOS( asp );
	    if(asp.isEmpty() || asp == Option::output_dir) //if they're the same, why bother?
		v["QMAKE_ABSOLUTE_SOURCE_PATH"].clear();
	}
	QString currentDir = QDir::currentDirPath();
	QString dirs[] = { QString("OBJECTS_DIR"), QString("MOC_DIR"), QString("UI_DIR"), QString("DESTDIR"), QString::null };
	for(int x = 0; dirs[x] != QString::null; x++) {
	    if ( !v[dirs[x]].isEmpty() ) {
		{
		    QString &path = v[dirs[x]].first();
		    if(QDir::isRelativePath(path) && !v["QMAKE_ABSOLUTE_SOURCE_PATH"].isEmpty())
			path.prepend(Option::output_dir + Option::dir_sep);
		    fileFixify(path);
		    if(path.right(Option::dir_sep.length()) != Option::dir_sep)
			path += Option::dir_sep;
		}
		QString path = project->first(dirs[x]); //not to be changed any further
		path = Option::fixPathToTargetOS(path);

		QDir d;
		if ( !QDir::isRelativePath( path ) )
		    d.cd( path.left( 2 ) );
		if(path.left(1) == Option::dir_sep)
		    d.cd(Option::dir_sep);

		QStringList subs = QStringList::split(Option::dir_sep, path);
		for(QStringList::Iterator subit = subs.begin(); subit != subs.end(); ++subit) {
		    if(!d.cd(*subit)) {
			d.mkdir((*subit));
			d.cd((*subit));
		    }
		}
	    }
	}
	QDir::current().cd( currentDir );
    }

    QString paths[] = { QString("SOURCES"), QString("FORMS"), QString("YACCSOURCES"), QString("INCLUDEPATH"),
			    QString("HEADERS"), QString("HEADERS_ORIG"),
			    QString("LEXSOURCES"), QString("QMAKE_INTERNAL_INCLUDED_FILES"), QString::null };
    for(int y = 0; paths[y] != QString::null; y++) {
	QStringList &l = v[paths[y]];
	if(!l.isEmpty())
	    fileFixify(l);
    }

    /* get deps and mocables */
    {
	QPtrList<MakefileDependDir> deplist;
	deplist.setAutoDelete(TRUE);
	if(doDepends()) {
	    QStringList incDirs;
	    QString dirs[] = { QString("QMAKE_ABSOLUTE_SOURCE_PATH"),
				   QString("INCLUDEPATH"), QString("DEPENDPATH"), QString::null };
	    for(int y = 0; dirs[y] != QString::null; y++) {
		QStringList &l = v[dirs[y]];
		for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it)
		{
		    //apparently tmake used colon separation...
		    QStringList damn = QStringList::split(':', (*val_it));
		    if(!damn.isEmpty())
			incDirs += damn;
		    else
			incDirs.append((*val_it));
		}
	    }
	    for(QStringList::Iterator it = incDirs.begin(); it != incDirs.end(); ++it) {
		QString r = (*it), l = Option::fixPathToLocalOS((*it));
		deplist.append(new MakefileDependDir(r, l));
	    }
	    debug_msg(1, "Dependancy Directories: %s", incDirs.join(" :: ").latin1());
	}

	QString sources[] = { QString("LEXSOURCES"), QString("YACCSOURCES"),
				  QString("HEADERS"), QString("SOURCES"), QString("FORMS"), QString::null };
	for(int x = 0; sources[x] != QString::null; x++) {
	    QStringList &l = v[sources[x]];
	    for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
		if(!(*val_it).isEmpty()) {
		    generateDependencies(deplist, (*val_it), doDepends());
		    if(mocAware() && !generateMocList((*val_it)))
			warn_msg(WarnLogic, "Failure to open: %s", (*val_it).latin1());
		}
	    }
	}
    }

    /* init variables */
    v["OBJECTS"].clear();
    v["OBJECTS"] += createObjectList("SOURCES");

    //UI files
    {
	if(!project->variables()["UI_DIR"].isEmpty())
	    project->variables()["INCLUDEPATH"].append(project->first("UI_DIR"));
	QStringList &decls = v["UICDECLS"], &impls = v["UICIMPLS"];
	QStringList &l = v["FORMS"];
	for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
	    QFileInfo fi((*it));
	    QString impl, decl;
	    if ( !project->isEmpty("UI_DIR") ) {
		impl = project->first("UI_DIR") + fi.baseName() + Option::cpp_ext;
		decl = project->first("UI_DIR") + fi.baseName() + Option::h_ext;

		QString d = fi.dirPath();
		if( d == ".")
		    d = QDir::currentDirPath();
		fileFixify(d);
		if( !project->variables()["INCLUDEPATH"].contains(d))
		    project->variables()["INCLUDEPATH"].append(d);
	    } else if ( fi.dirPath() == "." ) {
	    	impl = fi.baseName() + Option::cpp_ext;
                decl = fi.baseName() + Option::h_ext;
	    } else {
	    	impl = fi.dirPath() + Option::dir_sep + fi.baseName() + Option::cpp_ext;
                decl = fi.dirPath() + Option::dir_sep + fi.baseName() + Option::h_ext;
	    }
	    logicWarn(impl, "SOURCES");
	    logicWarn(decl, "HEADERS");
	    decls.append(decl);
	    impls.append(impl);
	    findDependencies(impl).append(decl);

	    QString mocable = Option::moc_mod + fi.baseName() + Option::cpp_ext;
	    if(!v["MOC_DIR"].isEmpty())
		mocable.prepend(v["MOC_DIR"].first());
	    else if(fi.dirPath() != ".")
		mocable.prepend(fi.dirPath() + Option::dir_sep);
	    logicWarn(mocable, "SOURCES");
	    if((*it).left(8) == "editbook") 
		qDebug("generating %s %s", mocable.latin1(), decl.latin1());
	    mocablesToMOC[cleanFilePath(decl)] = mocable;
	    mocablesFromMOC[cleanFilePath(mocable)] = decl;
	    v["_UIMOC"].append(mocable);
	}
	v["OBJECTS"] += (v["UICOBJECTS"] = createObjectList("UICDECLS"));
    }

    //Image files
    if(!project->isEmpty("IMAGES")) {
	if(project->isEmpty("QMAKE_IMAGE_COLLECTION"))
	    v["QMAKE_IMAGE_COLLECTION"].append("qmake_image_collection.cpp");
	QString imgfile = project->first("QMAKE_IMAGE_COLLECTION");
	Option::fixPathToTargetOS(imgfile);
	if(!project->isEmpty("UI_DIR")) {
	    if(imgfile.find(Option::dir_sep) != -1)
		imgfile = imgfile.right(imgfile.findRev(Option::dir_sep) + 1);
	    imgfile.prepend(project->first("UI_DIR"));
	    v["QMAKE_IMAGE_COLLECTION"] = QStringList(imgfile);
	}
	logicWarn(imgfile, "SOURCES");
	QStringList &l = v["IMAGES"];
	for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
	    QString f = (*it);
	    fileFixify(f);
	    if(!QFile::exists(f)) {
		warn_msg(WarnLogic, "Failure to open: %s", f.latin1());
		continue;
	    }
	    findDependencies(imgfile).append(f);
	}
	v["OBJECTS"] += (v["IMAGEOBJECTS"] = createObjectList("QMAKE_IMAGE_COLLECTION"));
    }

    //lex files
    {
	QStringList &impls = v["LEXIMPLS"];
	QStringList &l = v["LEXSOURCES"];
	for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
	    QFileInfo fi((*it));
	    QString impl = fi.dirPath() + Option::dir_sep + fi.baseName() + Option::lex_mod + Option::cpp_ext;
	    logicWarn(impl, "SOURCES");
	    impls.append(impl);
	    if( ! project->isActiveConfig("lex_included")) {
		v["SOURCES"].append(impl);
		// attribute deps of lex file to impl file
		QStringList &lexdeps = findDependencies((*it));
		QStringList &impldeps = findDependencies(impl);
		for(QStringList::ConstIterator d = lexdeps.begin(); d != lexdeps.end(); ++d) {
		    if(!impldeps.contains(*d))
			impldeps.append(*d);
		}
		lexdeps.clear();
	    }
	}
	if( ! project->isActiveConfig("lex_included"))
	    v["OBJECTS"] += (v["LEXOBJECTS"] = createObjectList("LEXIMPLS"));
    }
    //yacc files
    {
	QStringList &decls = v["YACCCDECLS"], &impls = v["YACCIMPLS"];
	QStringList &l = v["YACCSOURCES"];
	for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
	    QFileInfo fi((*it));
	    QString impl = fi.dirPath() + Option::dir_sep + fi.baseName() + Option::yacc_mod + Option::cpp_ext;
	    logicWarn(impl, "SOURCES");
	    QString decl = fi.dirPath() + Option::dir_sep + fi.baseName() + Option::yacc_mod + Option::h_ext;
	    logicWarn(decl, "HEADERS");

	    decls.append(decl);
	    impls.append(impl);
	    v["SOURCES"].append(impl);
	    QStringList &impldeps = findDependencies(impl);
 	    impldeps.append(decl);
	    // attribute deps of yacc file to impl file
	    QStringList &yaccdeps = findDependencies((*it));
	    for(QStringList::ConstIterator d = yaccdeps.begin(); d != yaccdeps.end(); ++d) {
		if(!impldeps.contains(*d))
		    impldeps.append(*d);
	    }
	    if( project->isActiveConfig("lex_included")) {
		// is there a matching lex file ? Transfer its dependencies.
		QString lexsrc = fi.baseName() + Option::lex_ext;
		if(fi.dirPath() != ".")
		    lexsrc.prepend(fi.dirPath() + Option::dir_sep);
		if(v["LEXSOURCES"].findIndex(lexsrc) != -1) {
		    QString trg = fi.dirPath() + Option::dir_sep + fi.baseName() + Option::lex_mod + Option::cpp_ext;
		    impldeps.append(trg);
		    impldeps += findDependencies(lexsrc);
		    depends[lexsrc].clear();
		}
	    }
	    yaccdeps.clear();
	}
	v["OBJECTS"] += (v["YACCOBJECTS"] = createObjectList("YACCIMPLS"));
    }

    //moc files
    if ( mocAware() ) {
	if(!project->variables()["MOC_DIR"].isEmpty())
	    project->variables()["INCLUDEPATH"].append(project->first("MOC_DIR"));
	v["OBJMOC"] = createObjectList("_HDRMOC") + createObjectList("_UIMOC");

	QStringList &l = v["SRCMOC"];
	l = v["_HDRMOC"] + v["_SRCMOC"] + v["_UIMOC"];
	for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it)
	    if(!(*val_it).isEmpty())
		(*val_it) = Option::fixPathToTargetOS((*val_it), FALSE);
    }
}

bool
MakefileGenerator::write()
{
#if 0
    //create my prl file?
    if(project->isActiveConfig("create_prl")) {
	QString foo = var("DESTDIR") + Option::dir_sep + var("TARGET") + Option::prl_ext;
	QFile ft(foo);
	if(ft.open(IO_WriteOnly)) {
	    QTextStream t(&ft);
	    QStringList exprt;
	    if(project->first("TEMPLATE") == "app")
		fprintf(stderr, "Cannot create a prl file for an application!");
	    else if ( project->isActiveConfig("staticlib") )
		exprt << "LIBS" << "DEFINES" << "CONFIG";
	    else
		exprt << "DEFINES" << "CONFIG";
	    for(QStringList::Iterator it = exprt.begin(); it != exprt.end(); ++it)
		t << (*it) << " += " << project->variables()[(*it)].join(" ") << endl;
	    ft.close();
	}
    }
    //read in any prl fiels included..
    QStringList &libs = project->variables()["LIBS"];
    for(QStringList::Iterator it = libs.begin(); it != libs.end(); ) {
	bool remove_it = FALSE;
	if((*it).right(Option::prl_ext.length()) == Option::prl_ext) {
	    project->read((*it), project->variables());
	    remove_it = TRUE;
	}
	if(remove_it)
	    it = libs.remove(it);
	else
	    ++it;
    }
#endif
    init();

    QTextStream t(&Option::output);
    writeMakefile(t);
    return TRUE;
}

void
MakefileGenerator::writeObj(QTextStream &t, const QString &obj, const QString &src)
{
    QStringList &objl = project->variables()[obj];
    QStringList &srcl = project->variables()[src];

    QStringList::Iterator oit = objl.begin();
    QStringList::Iterator sit = srcl.begin();
    QRegExp regexpSrc("\\$src");
    QRegExp regexpObj("\\$obj");
    for( ;sit != srcl.end() && oit != objl.end(); oit++, sit++) {
	if((*sit).isEmpty())
	    continue;

	if(!doDepends()) {
	    QString sdep, odep = (*sit) + " ";
	    QStringList deps = findDependencies((*sit));
	    for(QStringList::Iterator dit = deps.begin(); dit != deps.end(); dit++) {
		if((*dit).right(Option::moc_ext.length()) == Option::moc_ext)
		    odep += (*dit) + " ";
		else
		    sdep += (*dit) + " ";
	    }
	    t << (*sit) << ": " << sdep << endl
	      << (*oit) << ": " << odep ;
	} else {
	    t << (*oit) << ": " << (*sit) << " " << findDependencies((*sit)).join(" \\\n\t\t");
	}

	QString comp, cimp;
	if((*sit).right(qstrlen(Option::cpp_ext)) == Option::cpp_ext || 
	    (*sit).right(qstrlen(Option::cc_ext)) == Option::cc_ext ||
	    (*sit).right(qstrlen(Option::cxx_ext)) == Option::cxx_ext)  {
	    comp = "QMAKE_RUN_CXX";
	    cimp = "QMAKE_RUN_CXX_IMP";
	} else {
	    comp = "QMAKE_RUN_CC";
	    cimp = "QMAKE_RUN_CC_IMP";
	}
	if ( !project->variables()["OBJECTS_DIR"].isEmpty() || project->variables()[cimp].isEmpty()) {
	    QString p = var(comp);
	    p.replace(regexpSrc, (*sit));
	    p.replace(regexpObj, (*oit));
	    t << "\n\t" << p;
	}
	t << endl << endl;
    }
}


void
MakefileGenerator::writeUicSrc(QTextStream &t, const QString &ui)
{
    QStringList &uil = project->variables()[ui];
    for(QStringList::Iterator it = uil.begin(); it != uil.end(); it++) {
	QString deps = findDependencies((*it)).join(" \\\n\t\t"), decl, impl;
	{
	    QString tmp = (*it);
	    decl = tmp.replace(QRegExp("\\.ui$"), Option::h_ext);
	    tmp = (*it);
	    impl = tmp.replace(QRegExp("\\.ui$"), Option::cpp_ext);
	    if(!project->isEmpty("UI_DIR")) {
		int dlen = (*it).findRev(Option::dir_sep) + 1;
		decl = project->first("UI_DIR") + decl.right(decl.length() - dlen);
		impl = project->first("UI_DIR") + impl.right(impl.length() - dlen);
	    }
	}
	t << decl << ": " << (*it) << " " << deps << "\n\t"
	  << "$(UIC) " << (*it) << " -o " << decl << endl << endl;

	QString mildDecl = decl;
	int k = mildDecl.findRev( Option::dir_sep );
	if ( k != -1 )
	    mildDecl = mildDecl.mid( k + 1 );

	t << impl << ": " << decl << " " << (*it) << " " << deps << "\n\t"
	  << "$(UIC) " << (*it) << " -i " << mildDecl << " -o " << impl << endl << endl;
    }
}


void
MakefileGenerator::writeMocObj(QTextStream &t, const QString &obj)
{
    QStringList &objl = project->variables()[obj];
    QStringList::Iterator oit = objl.begin();
    QRegExp regexpSrc("\\$src");
    QRegExp regexpObj("\\$obj");

    QString mocdir;
    if(!project->variables()["MOC_DIR"].isEmpty())
	mocdir = project->first("MOC_DIR");

    for( ;oit != objl.end(); oit++) {
	QFileInfo fi(Option::fixPathToLocalOS((*oit)));
	QString dirName;
	if( !mocdir.isEmpty() )
	    dirName = mocdir;
	else if(!fi.dirPath().isEmpty() && fi.dirPath() != ".")
	    dirName = Option::fixPathToTargetOS(fi.dirPath(), FALSE) + Option::dir_sep;
	QString src(dirName + fi.baseName() + Option::cpp_ext );

	QString hdr = findMocSource(src);
	t << (*oit) << ": " << src << " "
	  << hdr << " " << findDependencies(hdr).join(" \\\n\t\t");
	if ( !project->variables()["OBJECTS_DIR"].isEmpty() ||
	     !project->variables()["MOC_DIR"].isEmpty() ||
	     project->variables()["QMAKE_RUN_CXX_IMP"].isEmpty()) {
	    QString p = var("QMAKE_RUN_CXX");
	    p.replace( regexpSrc, src);
	    p.replace( regexpObj, (*oit));
	    t << "\n\t" << p;
	}
	t << endl << endl;
    }
}


void
MakefileGenerator::writeMocSrc(QTextStream &t, const QString &src)
{
    QStringList &l = project->variables()[src];
    for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
	QString m = findMocDestination(*it);
	if ( !m.isEmpty())
	    t << m << ": $(MOC) " << (*it) << "\n\t"
	      << "$(MOC) " << (*it) << " -o " << m << endl << endl;
    }
}

void
MakefileGenerator::writeYaccSrc(QTextStream &t, const QString &src)
{
    QStringList &l = project->variables()[src];
    if(project->isActiveConfig("yacc_no_name_mangle") && l.count() > 1)
	warn_msg(WarnLogic, "yacc_no_name_mangle specified, but multiple parsers expected."
		 "This can lead to link problems.\n");
    for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
	QFileInfo fi((*it));
	QString impl = fi.dirPath() + Option::dir_sep + fi.baseName() + Option::yacc_mod + Option::cpp_ext;
	QString decl = fi.dirPath() + Option::dir_sep + fi.baseName() + Option::yacc_mod + Option::h_ext;

	QString yaccflags = "$(YACCFLAGS)";
	if(!project->isActiveConfig("yacc_no_name_mangle"))
	    yaccflags += " -p " + fi.baseName();
	t << impl << ": " << (*it) << "\n\t"
	  << ( "$(YACC) " + yaccflags + " " ) << (*it) << "\n\t"
	  << "-$(DEL) " << impl << " " << decl << "\n\t"
	  << "-$(MOVE) y.tab.h " << decl << "\n\t"
	  << "-$(MOVE) y.tab.c " << impl << endl << endl;
	t << decl << ": " << impl << endl << endl;
    }
}

void
MakefileGenerator::writeLexSrc(QTextStream &t, const QString &src)
{
    QStringList &l = project->variables()[src];
    if(project->isActiveConfig("yacc_no_name_mangle") && l.count() > 1)
	warn_msg(WarnLogic, "yacc_no_name_mangle specified, but multiple parsers expected.\n"
		 "This can lead to link problems.\n");
    for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
	QFileInfo fi((*it));
	QString impl = fi.dirPath() + Option::dir_sep + fi.baseName() + Option::lex_mod + Option::cpp_ext;

	QString lexflags = "$(LEXFLAGS)", stub="yy";
	if(!project->isActiveConfig("yacc_no_name_mangle")) {
	    stub = fi.baseName();
	    lexflags += " -P" + stub;
	}
	t << impl << ": " << (*it) << " " << findDependencies((*it)).join(" \\\n\t\t") << "\n\t"
	  << ( "$(LEX) " + lexflags + " " ) << (*it) << "\n\t"
	  << "-$(DEL) " << impl << " " << "\n\t"
	  << "-$(MOVE) lex." << stub << ".c " << impl << endl << endl;
    }
}

void
MakefileGenerator::writeImageObj(QTextStream &t, const QString &obj)
{
    QStringList &objl = project->variables()[obj];
    QRegExp regexpSrc("\\$src");
    QRegExp regexpObj("\\$obj");

    QString uidir;
    for(QStringList::Iterator oit = objl.begin(); oit != objl.end(); oit++) {
        QString src(project->first("QMAKE_IMAGE_COLLECTION"));
	t << (*oit) << ": " << src;
	if ( !project->variables()["OBJECTS_DIR"].isEmpty() ||
	     !project->variables()["UI_DIR"].isEmpty() ||
	     project->variables()["QMAKE_RUN_CXX_IMP"].isEmpty()) {
	    QString p = var("QMAKE_RUN_CXX");
	    p.replace( regexpSrc, src);
	    p.replace( regexpObj, (*oit));
	    t << "\n\t" << p;
	}
	t << endl << endl;
    }
}


void
MakefileGenerator::writeImageSrc(QTextStream &t, const QString &src)
{
    QStringList &l = project->variables()[src];
    for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
	t << (*it) << ": " << findDependencies((*it)).join(" \\\n\t\t") << "\n\t"
	  << "$(UIC) " << " -embed " << project->first("QMAKE_ORIG_TARGET")
	  << " " << findDependencies((*it)).join(" ") << " -o " << (*it) << endl << endl;
    }
}

void
MakefileGenerator::writeInstalls(QTextStream &t, const QString &installs)
{
    QString all_installs;
    QStringList &l = project->variables()[installs];
    for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
	QString pvar = (*it) + ".path";
	if(project->variables()[pvar].isEmpty()) {
	    warn_msg(WarnLogic, "%s is not defined: install target not created\n", pvar.latin1());
	    continue;
	}

	bool do_default = TRUE;
	QString target, dst=project->variables()[pvar].first();
 	QStringList tmp;
 	//masks
 	tmp = project->variables()[(*it) + ".files"];
 	if(!tmp.isEmpty()) {
 	    if(Option::target_mode == Option::TARG_WIN_MODE || Option::target_mode == Option::TARG_MAC9_MODE) {
 	    } else if(Option::target_mode == Option::TARG_UNIX_MODE || Option::target_mode == Option::TARG_MACX_MODE) {
		do_default = FALSE;
		for(QStringList::Iterator wild_it = tmp.begin(); wild_it != tmp.end(); ++wild_it) {
		    QString dirstr = QDir::currentDirPath(), f = (*wild_it);
		    int slsh = f.findRev(Option::dir_sep);
		    if(slsh != -1) {
			dirstr = f.left(slsh+1);
			f = f.right(f.length() - slsh - 1);
		    }
		    if(dirstr.right(Option::dir_sep.length()) != Option::dir_sep)
			dirstr += Option::dir_sep;
		    QDir dir(dirstr, f);
		    for(uint x = 0; x < dir.count(); x++) {
			QString file = dir[x];
			if(file == "." || file == "..") //blah
			    continue;
			file = dirstr + file;
			fileFixify(file);
			QFileInfo fi(file);
			target += QString(fi.isDir() ? "$(COPY_DIR)" : "$(COPY_FILE)") +
				  " " + fi.filePath() + " " + dst + "\n\t";
			if(fi.isExecutable() && !project->isEmpty("QMAKE_STRIP"))
			    target += var("QMAKE_STRIP") + " " + dst + "\n\t";
		    }
		}
 	    }
 	}
 	//other
 	tmp = project->variables()[(*it) + ".extra"];
 	if(!tmp.isEmpty()) {
	    do_default = FALSE;
	    if(!target.isEmpty())
 		target += "\n\t";
 	    target += tmp.join(" ");
 	}
 	//default?
	if(do_default)
	    target = defaultInstall((*it));

	if(!target.isEmpty()) {
	    t << "install_" << (*it) << ": " << "\n\t"
	      << "[ -d " << project->variables()[pvar].first() << " ] || mkdir -p "
	      << project->variables()[pvar].first() << "\n\t"
	      << target << endl << endl;
	    all_installs += QString("install_") + (*it) + " ";
	}   else {
	    debug_msg(1, "no definition for install %s: install target not created",(*it).latin1());
	}
    }
    t << "install: all " << all_installs << "\n\n";
}

QString
MakefileGenerator::var(const QString &var)
{
    return varGlue(var, "", " ", "");
}


QString
MakefileGenerator::varGlue(const QString &var, const QString &before, const QString &glue, const QString &after)
{
    QString ret;
    QStringList &l = project->variables()[var];
    for(QStringList::Iterator it = l.begin(); it != l.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 varGlue(var, "", " \\\n\t\t", "");
}


QStringList
MakefileGenerator::createObjectList(const QString &var)
{
    QStringList ret;
    QStringList &l = project->variables()[var];
    QString dir, file;
    if(!project->variables()["OBJECTS_DIR"].isEmpty())
	dir = project->first("OBJECTS_DIR");
    for(QStringList::Iterator it = l.begin(); it != l.end(); ++it) {
	QFileInfo fi(Option::fixPathToLocalOS((*it)));
	QString dirName;
	if ( dir.isEmpty() )
	    dirName = Option::fixPathToTargetOS(fi.dirPath(), FALSE) + Option::dir_sep;
	else
	    dirName = dir;

	file = dirName + fi.baseName() + Option::obj_ext;
	fileFixify(file);
	ret.append( file );
    }
    return ret;
}

bool
MakefileGenerator::writeMakefile(QTextStream &t)
{
    t << "####### Compile" << endl << endl;
    writeObj(t, "OBJECTS", "SOURCES");
    writeUicSrc(t, "FORMS");
    writeObj(t, "UICOBJECTS", "UICIMPLS");
    writeMocObj(t, "OBJMOC" );
    writeMocSrc(t, "HEADERS");
    writeMocSrc(t, "SOURCES");
    writeMocSrc(t, "UICDECLS");
    writeYaccSrc(t, "YACCSOURCES");
    writeLexSrc(t, "LEXSOURCES");
    writeImageObj(t, "IMAGEOBJECTS");
    writeImageSrc(t, "QMAKE_IMAGE_COLLECTION");

    t << "####### Install" << endl << endl;
    writeInstalls(t, "INSTALLS");
    return TRUE;
}

//get store argv, but then it would have more options than are probably necesary
//this will try to guess the bare minimum..
QString MakefileGenerator::build_args()
{
    static QString ret;
    if(ret.isEmpty()) {
	ret = "$(QMAKE)";

	//special variables
	if(!project->variables()["QMAKE_ABSOLUTE_SOURCE_PATH"].isEmpty())
	    ret += " QMAKE_ABSOLUTE_SOURCE_PATH=\"" + project->first("QMAKE_ABSOLUTE_SOURCE_PATH") + "\"";

	//output
	QString ofile = Option::fixPathToTargetOS(Option::output.name());
	if(ofile.findRev(Option::dir_sep) != -1)
	    ofile = ofile.right(ofile.length() - ofile.findRev(Option::dir_sep) -1);
	if (!ofile.isEmpty() && ofile != project->first("QMAKE_MAKEFILE"))
	    ret += " -o " + ofile;
	//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::mkfile::do_cache)
	    ret += " -nocache";
	if(!Option::mkfile::do_deps)
	    ret += " -nodepend";
	if(!Option::mkfile::do_dep_heuristics)
	    ret += " -nodependheuristics";

	//arguments
	for(QStringList::Iterator it = Option::user_vars.begin(); it != Option::user_vars.end(); ++it) {
	    if((*it).left(qstrlen("QMAKE_ABSOLUTE_SOURCE_PATH")) != "QMAKE_ABSOLUTE_SOURCE_PATH")
		ret += " \"" + (*it) + "\"";
	}

	//inputs
	QStringList files = Option::mkfile::project_files;
	fileFixify(files);
	ret += " " + files.join(" ");
    }
    return ret;
}

bool
MakefileGenerator::writeHeader(QTextStream &t)
{
    time_t foo = time(NULL);
    t << "#############################################################################" << endl;
    t << "# Makefile for building: " << var("TARGET") << endl;
    t << "# Generated by qmake on: " << ctime(&foo);
    t << "# Project:  " << project->projectFile() << endl;
    t << "# Template: " << var("TEMPLATE") << endl;
    t << "# Command: " << build_args() << endl;
    t << "#############################################################################" << endl;
    t << endl;
	return TRUE;
}


//makes my life easier..
bool
MakefileGenerator::writeMakeQmake(QTextStream &t)
{
    QString ofile = Option::output.name();
    if(ofile.findRev(Option::dir_sep) != -1)
	ofile = ofile.right(ofile.length() - ofile.findRev(Option::dir_sep) -1);
    ofile = Option::fixPathToTargetOS(ofile);

    QString pfile = project->projectFile();
    if(pfile != "(stdin)") {
	QString qmake = build_args();
	fileFixify(pfile);
	if(!ofile.isEmpty()) {
	    t << ofile << ": " << pfile << " ";
	    if(Option::mkfile::do_cache) {
		QString s = Option::mkfile::cachefile;
		fileFixify(s);
		t << s << " ";
	    }
	    t << project->variables()["QMAKE_INTERNAL_INCLUDED_FILES"].join(" \\\n\t\t") << "\n\t"
	      << qmake <<endl;
	}
	t << "qmake: " << project->variables()["QMAKE_INTERNAL_QMAKE_DEPS"].join(" \\\n\t\t") << "\n\t"
	  << "@" << qmake << endl << endl;
    }
    return TRUE;
}

bool
MakefileGenerator::fileFixify(QStringList &files) const
{
    if(files.isEmpty())
	return FALSE;
    int ret = 0;
    for(QStringList::Iterator it = files.begin(); it != files.end(); ++it)
	if(!(*it).isEmpty())
	    ret += (int)fileFixify((*it));
    return ret != 0;
}

bool
MakefileGenerator::fileFixify(QString &file) const
{
    if(file.isEmpty())
	return FALSE;
    int depth = 4;
    if(Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE) {
	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;
    }

    QString orig_file = file;
    if(project->variables()["QMAKE_ABSOLUTE_SOURCE_PATH"].isEmpty()) { //relative
	file = Option::fixPathToTargetOS(file, FALSE);
	if(QDir::isRelativePath(file))
	    return FALSE;
	QString match_dir = Option::output_dir;
	if(file.left(match_dir.length()) == match_dir &&
	   file.mid(match_dir.length(), Option::dir_sep.length()) == Option::dir_sep) {
	    file = file.right(file.length() - (match_dir.length() + 1));
	} else {
	    for(int i = 1; i <= depth; i++) {
		int sl = match_dir.findRev(Option::dir_sep);
		if(sl == -1)
		    break;
		match_dir = match_dir.left(sl);
		if(match_dir.isEmpty())
		    break;
		if(file.left(match_dir.length()) == match_dir &&
		   file.mid(match_dir.length(), Option::dir_sep.length()) == Option::dir_sep) {
		    //concat
		    int remlen = file.length() - (match_dir.length() + 1);
		    if (remlen < 0)
			remlen = 0;
		    file = file.right(remlen);
		    //prepend
		    for(int o = 0; o < i; o++)
			file.prepend(".." + Option::dir_sep);
		}
	    }
	}
    } else { //absoluteify it
	file = Option::fixPathToTargetOS(file);
	if(!QDir::isRelativePath(file)) //already absolute
	    return FALSE;
	QFileInfo fi(file);
	if(fi.convertToAbs()) //strange
	    return FALSE;
	file = fi.filePath();
    }
    debug_msg(3, "Fixed %s :: to :: %s (%d)", orig_file.latin1(), file.latin1(), depth);
    return TRUE;
}

QString
MakefileGenerator::cleanFilePath(const QString &file) const
{
    QString ret = Option::fixPathToTargetOS(file);
    fileFixify(ret);
    return ret;
}

void MakefileGenerator::logicWarn(const QString &f, const QString &w)
{
    if(!(Option::warn_level & WarnLogic))
	return;
    QString file = f;
    int slsh = f.findRev(Option::dir_sep);
    if(slsh != -1)
	file = file.right(file.length() - slsh - 1);
    QStringList &l = project->variables()[w];
    for(QStringList::Iterator val_it = l.begin(); val_it != l.end(); ++val_it) {
	if((*val_it).right(file.length()) == file) {
	    warn_msg(WarnLogic, "Found potential symbol conflict of %s in %s",
		     file.latin1(), w.latin1());
	    break;
	}
    }
}

QStringList
&MakefileGenerator::findDependencies(const QString &file)
{
    QString key = file;
    Option::fixPathToTargetOS(key);
    if(key.find(Option::dir_sep))
	key = key.right(key.length() - key.findRev(Option::dir_sep) - 1);
    return depends[key];
}
