1 /*
2 	Copyright (C) 2010 Andres Cabrera
3 	mantaraya36@gmail.com
4 
5 	This file is part of CsoundQt.
6 
7 	CsoundQt is free software; you can redistribute it
8 	and/or modify it under the terms of the GNU Lesser General Public
9 	License as published by the Free Software Foundation; either
10 	version 2.1 of the License, or (at your option) any later version.
11 
12 	CsoundQt is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU Lesser General Public License for more details.
16 
17 	You should have received a copy of the GNU Lesser General Public
18 	License along with Csound; if not, write to the Free Software
19 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 	02111-1307 USA
21 */
22 
23 #include <QCheckBox>
24 #include <QGroupBox>
25 #include <QLabel>
26 #include <QLineEdit>
27 #include <QRadioButton>
28 #include <QVBoxLayout>
29 #include <QGridLayout>
30 #include <QListWidget>
31 #include <QDir>
32 #include <QFile>
33 #include <QMessageBox>
34 #include <QDebug>
35 #include <QProcess>
36 
37 
38 #include "appwizard.h"
39 #include "appdetailspage.h"
40 #include "additionalfilespage.h"
41 #include "pluginspage.h"
42 
43 
AppWizard(QWidget * parent,QString opcodeDir,QString csd,QString sdkDir)44 AppWizard::AppWizard(QWidget *parent,QString opcodeDir,
45 					 QString csd, QString sdkDir) :
46 	QWizard(parent), m_csd(csd), m_sdkDir(sdkDir)
47 {
48 	int appPage = addPage(new AppDetailsPage(this));
49 
50 	setField("opcodeDir", opcodeDir);
51 #ifndef Q_OS_MAC
52 	// No plugins page for Mac for now
53 	m_pluginsPage = addPage(new PluginsPage(this, field("opcodeDir").toString()));
54 	//  static_cast<PluginsPage *>(this->page(m_pluginsPage))->
55 	connect(static_cast<AppDetailsPage *>(this->page(appPage)), SIGNAL(opcodeDirChangedSignal()),
56 			static_cast<PluginsPage *>(this->page(m_pluginsPage)), SLOT(updateOpcodeDir()));
57 	connect(static_cast<AppDetailsPage *>(this->page(appPage)), SIGNAL(libDirChangedSignal()),
58 			static_cast<PluginsPage *>(this->page(m_pluginsPage)), SLOT(updateOpcodeDir()));
59 #endif
60 	m_additionalsPage = addPage(new AdditionalFilesPage(this));
61 
62 	m_dataFiles = processDataFiles();
63 	static_cast<AdditionalFilesPage *>(this->page(m_additionalsPage))->setFiles(m_dataFiles);
64 	//
65 	//  setPixmap(QWizard::BannerPixmap, QPixmap(":/images/banner.png"));
66 	//  setPixmap(QWizard::BackgroundPixmap, QPixmap(":/images/background.png"));
67 	setWindowTitle(tr("Standalone Application Generator"));
68 }
69 
makeApp()70 void AppWizard::makeApp()
71 {
72 	QString appName =  field("appName").toString();
73 	QString targetDir =  field("targetDir").toString();
74 	bool useSdk = field("useSdk").toInt() == 1;
75 
76     // bool autorun =  field("autorun").toBool();
77     // int runMode = field("runMode").toInt();
78     // bool saveState = field("saveState").toBool();
79     // bool newParser = field("newParser").toBool();
80 
81 	QString author = field("author").toString();
82 	QString version = field("version").toString();
83 	QString email = field("email").toString();
84 	QString website = field("website").toString();
85 	QString instructions = field("instructions").toString();
86 
87 	bool customPaths =  field("customPaths").toBool();
88 	QString libDir, opcodeDir, qtLibsDir;
89 	if (customPaths) {
90 		libDir =  field("libDir").toString();
91 		opcodeDir =  field("opcodeDir").toString();
92 		qtLibsDir = field("qtLibsDir").toString();
93 	} else {
94 		libDir = "";
95 		opcodeDir = "";
96 		qtLibsDir = "";
97 	}
98 
99 #ifdef Q_OS_MAC
100 	QStringList plugins;
101 #else
102 	QStringList plugins = this->page(m_pluginsPage)->property("plugins").toStringList();
103 #endif
104 	QStringList dataFiles = this->page(m_additionalsPage)->property("dataFiles").toStringList();
105 
106 	if (!QFile::exists(targetDir)) {
107 		QMessageBox::critical(this, tr("CsoundQt App Creator"),
108 							  tr("The destination directory does not exist!\n"
109 								 "Aborting."));
110 		return;
111 	}
112 
113     qDebug()  << targetDir;
114 	if (useSdk) {
115 		createLinuxApp(appName, targetDir, dataFiles, plugins, m_sdkDir,
116 					   "", "", "");
117 		// TODO create two Mac Apps (one for 32 bit plaforms to support PPC)
118 		createMacApp(appName, targetDir, dataFiles, plugins, m_sdkDir,
119 					 "", "", "");
120 		createWinApp(appName, targetDir, dataFiles, plugins, m_sdkDir,
121 					 "", "", "");
122 	} else {
123 #ifdef USE_DOUBLE
124 #ifdef Q_OS_LINUX
125 		createLinuxApp(appName, targetDir, dataFiles, plugins, "",
126 					   libDir, opcodeDir, qtLibsDir);
127 #endif
128 #ifdef Q_OS_MAC
129 		createMacApp(appName, targetDir, dataFiles, plugins, "",
130 					 libDir, opcodeDir, qtLibsDir);
131 #endif
132 #ifdef Q_OS_WIN32
133 		createWinApp(appName, targetDir, dataFiles, plugins, "",
134 					 libDir, opcodeDir, qtLibsDir);
135 #endif
136 #else // If not doubles version, can't build for local platform
137 		QMessageBox::critical(this, tr("Can't build"),
138 							  tr("Can't build for local platform for single presicion. Aborted."));
139 		return;
140 #endif
141 	}
142 	QMessageBox::information(this, tr("Done"), tr("App Creation finished!"));
143 }
144 
processDataFiles()145 QStringList AppWizard::processDataFiles()
146 {
147 	QFile csdFile(m_csd);
148 	if (!csdFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
149 		return QStringList();
150 	}
151 	QStringList list;
152 	QTextStream in(&csdFile);
153 	while (!in.atEnd()) {
154 		QString line = in.readLine();
155 		if (line.count("\"") == 2 && !line.contains("invalue") && !line.contains("outvalue")
156 				&& !line.contains("chnget") && !line.contains("chnset") ) {
157 			int startIndex = line.indexOf("\"");
158 			if ((line.contains(";") && (line.indexOf(";") > startIndex)) ||
159 					!line.contains(";")) { // Quotes are not part of a comment
160 				if (!line.contains("outvalue") && !line.contains("invalue") &&
161 						!line.contains("if") && !line.contains("=") &&
162 						!line.contains("elseif") && !line.contains("=") ) { // exclude opcodes which use strings which are not filenames
163 					int endIndex = line.lastIndexOf("\"");
164 					QString dataName = line.mid(startIndex + 1, endIndex-startIndex - 1);
165 					QString newDataName = dataName;
166 #ifdef Q_OS_WIN32
167 					if (newDataName.indexOf(QRegExp("\\w\\:")) == 0) { // Absolute path
168 #else
169 					if (newDataName.startsWith("/")) { // Absolute path
170 #endif
171 						newDataName = "data/" + newDataName.right(newDataName.lastIndexOf("/") + 1);
172 					}
173 					else {
174 						newDataName += "data/";
175 					}
176 					if (QFile::exists(m_csd.left(m_csd.lastIndexOf(QDir::separator()) + 1) + dataName)
177 							|| QFile::exists(m_csd)) {
178 						list << dataName;
179 						line.replace(dataName,newDataName);
180 					}
181 				}
182 			}
183 		}
184 		m_fullText += line + "\n";
185 		if (line.trimmed().startsWith("</CsoundSynthesizer>")) {
186 			break;
187 		}
188 	}
189 	return list;
190 }
191 
192 void AppWizard::copyFolder(QString sourceFolder, QString destFolder)
193 {
194 	QDir sourceDir(sourceFolder);
195 	if(!sourceDir.exists()) {
196         qDebug()  << "source dir does not exist";
197 		return;
198 	}
199 	QDir destDir(destFolder);
200 	if(!destDir.exists()) {
201 		destDir.mkdir(destFolder);
202 	}
203 	QStringList files = sourceDir.entryList();
204 	for(int i = 0; i< files.count(); i++) {
205 		QString srcName = sourceFolder + QDir::separator() + files[i];
206 		QString destName = destFolder + QDir::separator() + files[i];
207 		QString symLinkTarget = QFile::symLinkTarget(srcName);
208 		if (symLinkTarget.isEmpty()) {
209 			QFile::copy(srcName, destName);
210 		} else {
211 			QFile::link(symLinkTarget, destName);
212 		}
213 	}
214 	files.clear();
215 	files = sourceDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
216 	for(int i = 0; i< files.count(); i++) {
217 		QString srcName = sourceFolder + QDir::separator() + files[i];
218 		QString destName = destFolder + QDir::separator() + files[i];
219 		QString symLinkTarget = QFile::symLinkTarget(srcName);
220 		if (symLinkTarget.isEmpty()) {
221 			copyFolder(srcName, destName);
222 		} else {
223 			QFile::link(symLinkTarget, destName);
224 		}
225 	}
226 }
227 
228 
229 void AppWizard::createWinApp(QString appName, QString appDir, QStringList dataFiles,
230 							 QStringList plugins, QString sdkDir,  QString libDir,
231 							 QString opcodeDir, QString qtLibsDir)
232 {
233     (void) appName;
234     (void) appDir;
235     (void) dataFiles;
236     (void) plugins;
237     (void) sdkDir;
238     (void) libDir;
239     (void) opcodeDir;
240     (void) qtLibsDir;
241 	//  QDir dir(appDir);
242 	//    if (dir.mkdir(appName)) {
243 	//      dir.cd(appName);
244 	//      dir.mkdir("lib");
245 	//      dir.mkdir("data");
246 	//      dir.cd("data");
247 	//      QMessageBox::critical(this, tr("CsoundQt App Creator"),
248 	//                            tr("Plugins:") + dataFiles.join("\n"));
249 	//      foreach(QString file, dataFiles) {
250 	//        QString destName = dir.absolutePath() + QDir::separator() + file.mid(file.lastIndexOf(QDir::separator()) + 1);
251 	//        QFile::copy(file, destName);
252     //        qDebug()  << destName;
253 	//      }
254 	//      dir.cd("../lib");
255 	//      foreach(QString file, plugins) {
256 	//        QString destName = dir.absolutePath() + QDir::separator() + file.mid(file.lastIndexOf(QDir::separator()) + 1);
257 	//        QFile::copy(file, destName);
258     //        qDebug()  << destName;
259 	//      }
260 	//    }
261 	//    else {
262 	//      QMessageBox::critical(this, tr("CsoundQt App Creator"),
263 	//                            tr("Error creating app directory."));
264 	//    }
265 	//  }
266 }
267 
268 void AppWizard::createMacApp(QString appName, QString appDir, QStringList dataFiles,
269 							 QStringList plugins, QString sdkDir,  QString libDir,
270 							 QString opcodeDir, QString qtLibsDir)
271 {
272     (void) appName;
273     (void) appDir;
274     (void) dataFiles;
275     (void) plugins;
276     (void) sdkDir;
277     (void) libDir;
278     (void) opcodeDir;
279     (void) qtLibsDir;
280     qDebug() ;
281 	QDir dir(appDir);
282 	QList<QPair<QString, QString> > copyList;
283 	if (dir.exists(appName + QDir::separator() + "osx")) {
284 		int ret = QProcess::execute("rm -fR " + dir.absolutePath() + QDir::separator() + appName + QDir::separator() + "osx");
285         qDebug()  << "deleted directory";
286 		if (ret != 0) {
287 			QMessageBox::critical(this, tr("Error"), tr("Could not delete old application directory! Aborted."));
288 		}
289 	}
290 	if (dir.mkpath(appName + QDir::separator() + "osx")) {
291 		dir.cd(appName + QDir::separator() + "osx");
292 		// Copy csd and binaries
293 		if (sdkDir.isEmpty()) {
294 			copyList << QPair<QString, QString>(QCoreApplication::applicationDirPath() + QDir::separator() + "../Resources/QuteApp_d.app",
295 												dir.absolutePath() + QDir::separator() + appName + ".app");
296 		} else {
297 			copyList << QPair<QString, QString>(sdkDir + QDir::separator() + "osx/QuteApp_d.app",
298 												dir.absolutePath() + QDir::separator() + appName + ".app");
299 		}
300 		// Data files
301 		copyList << QPair<QString, QString>
302 				(m_csd,
303 				 dir.absolutePath() + QDir::separator() + appName + ".app" + QDir::separator() + "Contents/Resources" + QDir::separator() + "quteapp.csd");
304 
305 		foreach(QString file, dataFiles) {
306 			QString destName = dir.absolutePath() + QDir::separator() + appName + ".app" +  QDir::separator() + "Contents/Resources" + QDir::separator() + file.mid(file.lastIndexOf(QDir::separator()) + 1);
307 			if (file.startsWith("/")) { // Absolute path
308 				copyList << QPair<QString, QString>(file, destName);
309 			}
310 			else { // Relative path
311 				copyList <<  QPair<QString, QString>(m_csd.left(m_csd.lastIndexOf("/")), destName);
312 			}
313 		}
314 		// No need to copy Qt libraries as they should already be deployed in the QuteApp
315 		// Copy lib files and plugins only if libDir is not given
316 		if (!libDir.isEmpty()) {
317 			QStringList libFiles;
318 			libFiles << "LibCsound64.framework";
319 			libFiles << "libportaudio.so" << "libportmidi.so";
320 			QStringList defaultLibDirs;
321 			defaultLibDirs << "/Library/Frameworks" << "/usr/lib" << "/usr/local/lib";
322 			QStringList libSearchDirs;
323 			libSearchDirs << libDir << defaultLibDirs;
324 			//FIXME this is not really working yet... You need to use the frameworks and libs inside the QuteApp template bundle.
325             qDebug()  << "Error! libDir not implemented!";
326 			dir.cd("../Frameworks");
327 			foreach(QString file, libFiles) {
328 				QString destName = dir.absolutePath() + QDir::separator() + file.mid(file.lastIndexOf(QDir::separator()) + 1);
329 				for (int i = 0 ; i < libSearchDirs.size(); i++) {
330 					QString libName = libSearchDirs[i] + QDir::separator() + file;
331 					if (QFile::exists(libName)) {
332 						copyList << QPair<QString, QString>(libName,destName);
333 						break;
334 					}
335 				}
336 			}
337 		}
338 		if (!opcodeDir.isEmpty()) {
339 			foreach(QString file, plugins) {
340 				QString destName = dir.absolutePath() + QDir::separator() + file.mid(file.lastIndexOf(QDir::separator()) + 1);
341 				copyList << QPair<QString, QString>(opcodeDir + QDir::separator() + file, destName);
342 			}
343 		}
344 		QList<QPair<QString,QString> >::const_iterator i;
345 		for (i = copyList.constBegin(); i != copyList.constEnd(); ++i) {
346 			QFileInfo finfo((*i).first);
347 			if (finfo.isDir()) {
348 				copyFolder((*i).first, (*i).second);
349                 qDebug()  << "dir copied:" << (*i).first;
350 			} else {
351 				if (QFile::copy((*i).first, (*i).second)) {
352                     qDebug()  << "copied:" << (*i).first;
353 				} else {
354                     qDebug()  << "error copying: " << (*i).first << (*i).second;
355 				}
356 			}
357 		}
358 
359 		dir.cdUp();
360 		QFile f3(dir.absolutePath() + QDir::separator() + "quteapp.csd");
361 		f3.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
362 						  | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
363 						  | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther);
364 	}
365 	else {
366 		QMessageBox::critical(this, tr("CsoundQt App Creator"),
367 							  tr("Error creating app directory."));
368 	}
369 }
370 
371 void AppWizard::createLinuxApp(QString appName, QString appDir, QStringList dataFiles,
372 							   QStringList plugins, QString sdkDir, QString libDir,
373 							   QString opcodeDir, QString qtLibsDir)
374 {
375     qDebug() ;
376 	QDir dir(appDir);
377 	QList<QPair<QString, QString> > copyList;
378 	if (dir.exists(appName + QDir::separator() + "linux")) {
379 		int ret = QProcess::execute("rm -fR " + dir.absolutePath() + QDir::separator() + appName + QDir::separator() + "linux");
380 		qDebug() << "Deleted directory";
381 		if (ret != 0) {
382 			QMessageBox::critical(this, tr("Error"), tr("Could not delete old application directory! Aborted."));
383 		}
384 	}
385 	if (dir.mkpath(appName + QDir::separator() + "linux")) {
386 		dir.cd(appName + QDir::separator() + "linux");
387 		// Create directories
388 		dir.mkdir("lib");
389 		dir.mkdir("data");
390 		// Copy csd and binaries
391 		if (sdkDir.isEmpty()) {
392 			copyList << QPair<QString, QString>(":/res/linux/QuteApp_d",
393 												dir.absolutePath() + QDir::separator() +"lib/QuteApp");
394 			copyList << QPair<QString, QString>(":/res/linux/launch.sh",
395 												dir.absolutePath() + QDir::separator() + appName + ".sh");
396 		} else {
397 			copyList << QPair<QString, QString>(sdkDir + QDir::separator() + "linux/lib/QuteApp_d",
398 												dir.absolutePath() + QDir::separator() +"lib/QuteApp");
399 			copyList << QPair<QString, QString>(sdkDir + QDir::separator() + "linux/lib/launch.sh",
400 												dir.absolutePath() + QDir::separator() + appName + ".sh");
401 		}
402 		// Data files
403 		dir.cd("data");
404 		copyList << QPair<QString, QString>(m_csd,
405 											dir.absolutePath() + QDir::separator() + "quteapp.csd");
406 
407 		foreach(QString file, dataFiles) {
408 			QString destName = dir.absolutePath() + QDir::separator() + file.mid(file.lastIndexOf(QDir::separator()) + 1);
409 			if (file.startsWith("/")) { // Absolute path
410 				copyList << QPair<QString, QString>(file, destName);
411 			}
412 			else { // Relative path
413 				copyList <<  QPair<QString, QString>(m_csd.left(m_csd.lastIndexOf("/")), destName);
414 			}
415 		}
416 		// Copy lib files and plugins
417 		QStringList libFiles;
418 		libFiles << "libcsound64.so" << "libcsound64.so.5.2";
419 		libFiles << "libportaudio.so" << "libportmidi.so";
420 		QStringList defaultLibDirs;
421 		defaultLibDirs << "/usr/lib" << "/usr/local/lib";
422 		QStringList libSearchDirs;
423 		if (sdkDir.isEmpty()) { // If sdk dir is not given, it means that local installation is used
424 			libSearchDirs << defaultLibDirs;
425 		} else {
426 			libSearchDirs << libDir;
427 		}
428 
429 		dir.cd("../lib");
430 		foreach(QString file, libFiles) {
431 			QString destName = dir.absolutePath() + QDir::separator() + file.mid(file.lastIndexOf(QDir::separator()) + 1);
432 			for (int i = 0 ; i < libSearchDirs.size(); i++) {
433 				QString libName = libSearchDirs[i] + QDir::separator() + file;
434 				if (QFile::exists(libName)) {
435 					copyList << QPair<QString, QString>(libName,destName);
436 					break;
437 				}
438 			}
439 		}
440 		// Qt Libs
441 		QStringList qtLibFiles;
442 		qtLibFiles << "libQtCore.4.so" << "libQtGui.4.so" << "libQtXml.4.so";
443 		QStringList defaultQtLibDirs;
444 		defaultQtLibDirs<< "/usr/lib" << "/usr/local/lib";
445 		QStringList qtLibSearchDirs;
446 		if (qtLibsDir.isEmpty()) {
447 			qtLibSearchDirs << defaultQtLibDirs;
448 		} else {
449 			qtLibSearchDirs << qtLibsDir;
450 		}
451 		foreach(QString file, qtLibFiles) {
452 			QString destName = dir.absolutePath() + QDir::separator() + file.mid(file.lastIndexOf(QDir::separator()) + 1);
453 			for (int i = 0 ; i < qtLibSearchDirs.size(); i++) {
454 				QString libName = qtLibSearchDirs[i] + QDir::separator() + file;
455 				if (QFile::exists(libName)) {
456 					copyList << QPair<QString, QString>(libName,destName);
457 					break;
458 				}
459 			}
460 		}
461 		foreach(QString file, plugins) {
462 			QString destName = dir.absolutePath() + QDir::separator() + file.mid(file.lastIndexOf(QDir::separator()) + 1);
463 			copyList << QPair<QString, QString>(opcodeDir + QDir::separator() + file, destName);
464 		}
465 		QList<QPair<QString,QString> >::const_iterator i;
466 		for (i = copyList.constBegin(); i != copyList.constEnd(); ++i) {
467 			if (QFile::copy((*i).first, (*i).second)) {
468                 qDebug()  << "copied:" << (*i).first;
469 			} else {
470                 qDebug()  << "error copying: " << (*i).first << (*i).second;
471 			}
472 		}
473 		dir.cdUp();
474 		QFile f(dir.absolutePath() + QDir::separator() +"lib/QuteApp");
475 		f.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
476 						 | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
477 						 | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther);
478 		QFile f2(dir.absolutePath() + QDir::separator() + appName + ".sh");
479 		f2.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
480 						  | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
481 						  | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther);
482 		QFile f3(dir.absolutePath() + QDir::separator() + "quteapp.csd");
483 		f3.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
484 						  | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
485 						  | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther);
486 	}
487 	else {
488 		QMessageBox::critical(this, tr("CsoundQt App Creator"),
489 							  tr("Error creating app directory."));
490 	}
491 }
492 
493 void AppWizard::getLinuxLibDeps(QString libname)
494 {
495     (void) libname;
496 	QProcess ldd;
497 	//    ldd.start("ldd", QStringList() << "-c");
498 	//    if (!ldd.waitForStarted())
499 	//        return false;
500 	//    ldd.write("Qt rocks!");
501 	//    ldd.closeWriteChannel();
502 	//    if (!ldd.waitForFinished())
503 	//        return false;
504 	//    QByteArray result = ldd.readAll();
505 }
506