main.cpp 73 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
  4. ** Contact: http://www.qt-project.org/legal
  5. **
  6. ** This file is part of the tools applications of the Qt Toolkit.
  7. **
  8. ** $QT_BEGIN_LICENSE:LGPL21$
  9. ** Commercial License Usage
  10. ** Licensees holding valid commercial Qt licenses may use this file in
  11. ** accordance with the commercial license agreement provided with the
  12. ** Software or, alternatively, in accordance with the terms contained in
  13. ** a written agreement between you and Digia. For licensing terms and
  14. ** conditions see http://qt.digia.com/licensing. For further information
  15. ** use the contact form at http://qt.digia.com/contact-us.
  16. **
  17. ** GNU Lesser General Public License Usage
  18. ** Alternatively, this file may be used under the terms of the GNU Lesser
  19. ** General Public License version 2.1 or version 3 as published by the Free
  20. ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
  21. ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
  22. ** following information to ensure the GNU Lesser General Public License
  23. ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
  24. ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  25. **
  26. ** In addition, as a special exception, Digia gives you certain additional
  27. ** rights. These rights are described in the Digia Qt LGPL Exception
  28. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  29. **
  30. ** $QT_END_LICENSE$
  31. **
  32. ****************************************************************************/
  33. #include <QCoreApplication>
  34. #include <QStringList>
  35. #include <QDir>
  36. #include <QJsonDocument>
  37. #include <QJsonObject>
  38. #include <QJsonArray>
  39. #include <QJsonValue>
  40. #include <QSet>
  41. #include <QXmlStreamReader>
  42. #include <QDateTime>
  43. #include <QStandardPaths>
  44. #include <QUuid>
  45. #include <QDirIterator>
  46. #include <algorithm>
  47. static const bool mustReadOutputAnyway = true; // pclose seems to return the wrong error code unless we read the output
  48. void deleteRecursively(const QString &dirName)
  49. {
  50. QDir dir(dirName);
  51. if (!dir.exists())
  52. return;
  53. QFileInfoList entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
  54. foreach (QFileInfo entry, entries) {
  55. if (entry.isDir())
  56. deleteRecursively(entry.absoluteFilePath());
  57. else
  58. QFile::remove(entry.absoluteFilePath());
  59. }
  60. QDir().rmdir(dirName);
  61. }
  62. FILE *openProcess(const QString &command)
  63. {
  64. #if defined(Q_OS_WIN32)
  65. QString processedCommand = QLatin1Char('\"') + command + QLatin1Char('\"');
  66. #else
  67. QString processedCommand = command;
  68. #endif
  69. return popen(processedCommand.toLocal8Bit().constData(), "r");
  70. }
  71. struct QtDependency
  72. {
  73. QtDependency(QString rpath, QString apath) : relativePath(rpath), absolutePath(apath) {}
  74. bool operator==(const QtDependency &other) const
  75. {
  76. return relativePath == other.relativePath && absolutePath == other.absolutePath;
  77. }
  78. QString relativePath;
  79. QString absolutePath;
  80. };
  81. struct Options
  82. {
  83. Options()
  84. : helpRequested(false)
  85. , verbose(false)
  86. , timing(false)
  87. , build(true)
  88. , deploymentMechanism(Bundled)
  89. , releasePackage(false)
  90. , authorPass("")
  91. , tizenDistributor2SignerFile("")
  92. , tizenDistributor2SignerPassword("")
  93. , tizenDistributor2CaCerFile("")
  94. , tizenDistributor2RootPath("")
  95. , installTpk(false)
  96. , unInstallTpk(false)
  97. , fetchedRemoteModificationDates(false)
  98. {}
  99. ~Options()
  100. {
  101. if (!temporaryDirectoryName.isEmpty())
  102. deleteRecursively(temporaryDirectoryName);
  103. }
  104. enum DeploymentMechanism
  105. {
  106. Bundled,
  107. Target,
  108. Debug
  109. };
  110. bool helpRequested;
  111. bool verbose;
  112. bool timing;
  113. bool build;
  114. QTime timer;
  115. // External tools
  116. QString tizenSdkDirectory;
  117. QString sdbBinary;
  118. QString nativeSigningBinary;
  119. // Build paths
  120. QString qtInstallDirectory;
  121. QString qtInstallLibDirectory;
  122. QString qtInstallBinDirectory;
  123. QString outputDirectory;
  124. QString applicationBinary;
  125. QString rootPath;
  126. QStringList qmlImportPaths;
  127. // Build information
  128. QString tizenPlatform;
  129. QString architecture;
  130. QString toolchainBinDirectory;
  131. QString toolchainVersion;
  132. QString toolchainPrefix;
  133. QString qtPlatformPluginName;
  134. // Package information
  135. DeploymentMechanism deploymentMechanism;
  136. QString packageName;
  137. QString packageVersion;
  138. QStringList extraLibs;
  139. QStringList extraPlugins;
  140. // Signing information
  141. bool releasePackage;
  142. QString authorKey;
  143. QString authorPass;
  144. QString tizenDeveloperCaCerFile;
  145. QString tizenDistributorSignerFile;
  146. QString tizenDistributorSignerPassword;
  147. QString tizenDistributorCaCerFile;
  148. QString tizenDistributor2SignerFile;
  149. QString tizenDistributor2SignerPassword;
  150. QString tizenDistributor2CaCerFile;
  151. QString tizenDistributor2RootPath;
  152. // Installation information
  153. bool installTpk;
  154. bool unInstallTpk;
  155. QString installLocation;
  156. // Collected information
  157. typedef QPair<QString, QString> BundledFile;
  158. QList<BundledFile> bundledFiles;
  159. QList<QtDependency> qtDependencies;
  160. QStringList localLibs;
  161. QString temporaryDirectoryName;
  162. bool fetchedRemoteModificationDates;
  163. QDateTime remoteModificationDate;
  164. QStringList permissions;
  165. QStringList features;
  166. };
  167. // Copy-pasted from qmake/library/ioutil.cpp
  168. inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
  169. {
  170. for (int x = arg.length() - 1; x >= 0; --x) {
  171. ushort c = arg.unicode()[x].unicode();
  172. if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
  173. return true;
  174. }
  175. return false;
  176. }
  177. static QString shellQuoteUnix(const QString &arg)
  178. {
  179. // Chars that should be quoted (TM). This includes:
  180. static const uchar iqm[] = {
  181. 0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
  182. 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
  183. }; // 0-32 \'"$`<>|;&(){}*?#!~[]
  184. if (!arg.length())
  185. return QString::fromLatin1("\"\"");
  186. QString ret(arg);
  187. if (hasSpecialChars(ret, iqm)) {
  188. ret.replace(QLatin1Char('\''), QLatin1String("'\\''"));
  189. ret.prepend(QLatin1Char('\''));
  190. ret.append(QLatin1Char('\''));
  191. }
  192. return ret;
  193. }
  194. static QString shellQuoteWin(const QString &arg)
  195. {
  196. // Chars that should be quoted (TM). This includes:
  197. // - control chars & space
  198. // - the shell meta chars "&()<>^|
  199. // - the potential separators ,;=
  200. static const uchar iqm[] = {
  201. 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
  202. 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
  203. };
  204. if (!arg.length())
  205. return QString::fromLatin1("\"\"");
  206. QString ret(arg);
  207. if (hasSpecialChars(ret, iqm)) {
  208. // Quotes are escaped and their preceding backslashes are doubled.
  209. // It's impossible to escape anything inside a quoted string on cmd
  210. // level, so the outer quoting must be "suspended".
  211. ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\"\\1\\1\\^\"\""));
  212. // The argument must not end with a \ since this would be interpreted
  213. // as escaping the quote -- rather put the \ behind the quote: e.g.
  214. // rather use "foo"\ than "foo\"
  215. int i = ret.length();
  216. while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
  217. --i;
  218. ret.insert(i, QLatin1Char('"'));
  219. ret.prepend(QLatin1Char('"'));
  220. }
  221. return ret;
  222. }
  223. static QString shellQuote(const QString &arg)
  224. {
  225. if (QDir::separator() == QLatin1Char('\\'))
  226. return shellQuoteWin(arg);
  227. else
  228. return shellQuoteUnix(arg);
  229. }
  230. void deleteMissingFiles(const Options &options, const QDir &srcDir, const QDir &dstDir)
  231. {
  232. if (options.verbose)
  233. fprintf(stdout, "Delete missing files %s %s\n", qPrintable(srcDir.absolutePath()), qPrintable(dstDir.absolutePath()));
  234. QFileInfoList srcEntries = srcDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
  235. QFileInfoList dstEntries = dstDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
  236. foreach (const QFileInfo &dst, dstEntries) {
  237. bool found = false;
  238. foreach (const QFileInfo &src, srcEntries)
  239. if (dst.fileName() == src.fileName()) {
  240. if (dst.isDir())
  241. deleteMissingFiles(options, src.absoluteFilePath(), dst.absoluteFilePath());
  242. found = true;
  243. break;
  244. }
  245. if (!found) {
  246. if (options.verbose)
  247. fprintf(stdout, "%s not found in %s, removing it.\n", qPrintable(dst.fileName()), qPrintable(srcDir.absolutePath()));
  248. if (dst.isDir())
  249. deleteRecursively(dst.absolutePath());
  250. else
  251. QFile::remove(dst.absoluteFilePath());
  252. }
  253. }
  254. fflush(stdout);
  255. }
  256. QString cleanPackageName(QString packageName)
  257. {
  258. QRegExp legalChars(QLatin1String("[a-zA-Z0-9_\\.]"));
  259. packageName.replace(QLatin1Char('.'),QLatin1String("_"));
  260. static QStringList keywords;
  261. if (keywords.isEmpty()) {
  262. keywords << QLatin1String("abstract") << QLatin1String("continue") << QLatin1String("for")
  263. << QLatin1String("new") << QLatin1String("switch") << QLatin1String("assert")
  264. << QLatin1String("default") << QLatin1String("if") << QLatin1String("package")
  265. << QLatin1String("synchronized") << QLatin1String("boolean") << QLatin1String("do")
  266. << QLatin1String("goto") << QLatin1String("private") << QLatin1String("this")
  267. << QLatin1String("break") << QLatin1String("double") << QLatin1String("implements")
  268. << QLatin1String("protected") << QLatin1String("throw") << QLatin1String("byte")
  269. << QLatin1String("else") << QLatin1String("import") << QLatin1String("public")
  270. << QLatin1String("throws") << QLatin1String("case") << QLatin1String("enum")
  271. << QLatin1String("instanceof") << QLatin1String("return") << QLatin1String("transient")
  272. << QLatin1String("catch") << QLatin1String("extends") << QLatin1String("int")
  273. << QLatin1String("short") << QLatin1String("try") << QLatin1String("char")
  274. << QLatin1String("final") << QLatin1String("interface") << QLatin1String("static")
  275. << QLatin1String("void") << QLatin1String("class") << QLatin1String("finally")
  276. << QLatin1String("long") << QLatin1String("strictfp") << QLatin1String("volatile")
  277. << QLatin1String("const") << QLatin1String("float") << QLatin1String("native")
  278. << QLatin1String("super") << QLatin1String("while");
  279. }
  280. // No keywords
  281. int index = -1;
  282. while (index < packageName.length()) {
  283. int next = packageName.indexOf(QLatin1Char('.'), index + 1);
  284. if (next == -1)
  285. next = packageName.length();
  286. QString word = packageName.mid(index + 1, next - index - 1);
  287. if (!word.isEmpty()) {
  288. QChar c = word[0];
  289. if ((c >= QChar(QLatin1Char('0')) && c<= QChar(QLatin1Char('9')))
  290. || c == QLatin1Char('_')) {
  291. packageName.insert(index + 1, QLatin1Char('a'));
  292. index = next + 1;
  293. continue;
  294. }
  295. }
  296. if (keywords.contains(word)) {
  297. packageName.insert(next, QLatin1String("_"));
  298. index = next + 1;
  299. } else {
  300. index = next;
  301. }
  302. }
  303. return packageName;
  304. }
  305. Options parseOptions()
  306. {
  307. Options options;
  308. QStringList arguments = QCoreApplication::arguments();
  309. for (int i=0; i<arguments.size(); ++i) {
  310. const QString &argument = arguments.at(i);
  311. if (argument.compare(QLatin1String("--output"), Qt::CaseInsensitive) == 0) {
  312. if (i + 1 == arguments.size())
  313. options.helpRequested = true;
  314. else
  315. options.outputDirectory = arguments.at(++i).trimmed();
  316. } else if (argument.compare(QLatin1String("--no-build"), Qt::CaseInsensitive) == 0) {
  317. options.build = false;
  318. } else if (argument.compare(QLatin1String("--install"), Qt::CaseInsensitive) == 0) {
  319. options.installTpk = true;
  320. options.unInstallTpk = true;
  321. } else if (argument.compare(QLatin1String("--reinstall"), Qt::CaseInsensitive) == 0) {
  322. options.installTpk = true;
  323. options.unInstallTpk = false;
  324. } else if (argument.compare(QLatin1String("--help"), Qt::CaseInsensitive) == 0) {
  325. options.helpRequested = true;
  326. } else if (argument.compare(QLatin1String("--verbose"), Qt::CaseInsensitive) == 0) {
  327. options.verbose = true;
  328. } else if (argument.compare(QLatin1String("--deployment"), Qt::CaseInsensitive) == 0) {
  329. if (i + 1 == arguments.size()) {
  330. options.helpRequested = true;
  331. } else {
  332. QString deploymentMechanism = arguments.at(++i);
  333. if (deploymentMechanism.compare(QLatin1String("target"), Qt::CaseInsensitive) == 0) {
  334. options.deploymentMechanism = Options::Target;
  335. } else if (deploymentMechanism.compare(QLatin1String("debug"), Qt::CaseInsensitive) == 0) {
  336. options.deploymentMechanism = Options::Debug;
  337. QString temporaryDirectoryName = QStandardPaths::standardLocations(QStandardPaths::TempLocation).at(0)
  338. + QLatin1String("/tizen-build-")
  339. + QUuid::createUuid().toString();
  340. if (QFileInfo(temporaryDirectoryName).exists()) {
  341. fprintf(stderr, "Temporary directory '%s' already exists. Bailing out.\n",
  342. qPrintable(temporaryDirectoryName));
  343. options.helpRequested = true;
  344. } else {
  345. QDir().mkpath(temporaryDirectoryName);
  346. options.temporaryDirectoryName = temporaryDirectoryName;
  347. }
  348. } else if (deploymentMechanism.compare(QLatin1String("bundled"), Qt::CaseInsensitive) == 0) {
  349. options.deploymentMechanism = Options::Bundled;
  350. } else {
  351. fprintf(stderr, "Unrecognized deployment mechanism: %s\n", qPrintable(deploymentMechanism));
  352. options.helpRequested = true;
  353. }
  354. }
  355. } else if (argument.compare(QLatin1String("--device"), Qt::CaseInsensitive) == 0) {
  356. if (i + 1 == arguments.size())
  357. options.helpRequested = true;
  358. else
  359. options.installLocation = arguments.at(++i);
  360. } else if (argument.compare(QLatin1String("--release"), Qt::CaseInsensitive) == 0) {
  361. options.releasePackage = true;
  362. } else if (argument.compare(QLatin1String("--tizen-sdk-directory"), Qt::CaseInsensitive) == 0) {
  363. if (i + 1 == arguments.size())
  364. options.helpRequested = true;
  365. else
  366. options.tizenSdkDirectory = arguments.at(++i);
  367. } else if (argument.compare(QLatin1String("--author-password"), Qt::CaseInsensitive) == 0) {
  368. if (i + 1 == arguments.size())
  369. options.helpRequested = true;
  370. else
  371. options.authorPass = arguments.at(++i);
  372. } else if (argument.compare(QLatin1String("--author-key"), Qt::CaseInsensitive) == 0) {
  373. if (i + 1 == arguments.size())
  374. options.helpRequested = true;
  375. else
  376. options.authorKey = arguments.at(++i);
  377. } else if (argument.compare(QLatin1String("--distributor-password"), Qt::CaseInsensitive) == 0) {
  378. if (i + 1 == arguments.size())
  379. options.helpRequested = true;
  380. else
  381. options.tizenDistributorSignerPassword= arguments.at(++i);
  382. } else if (argument.compare(QLatin1String("--distributor-key"), Qt::CaseInsensitive) == 0) {
  383. if (i + 1 == arguments.size())
  384. options.helpRequested = true;
  385. else
  386. options.tizenDistributorSignerFile= arguments.at(++i);
  387. } else if (argument.compare(QLatin1String("--qt-install-directory"), Qt::CaseInsensitive) == 0) {
  388. if (i + 1 == arguments.size())
  389. options.helpRequested = true;
  390. else
  391. options.qtInstallDirectory = arguments.at(++i);
  392. } else if (argument.compare(QLatin1String("--qt-install-lib-directory"), Qt::CaseInsensitive) == 0) {
  393. if (i + 1 == arguments.size())
  394. options.helpRequested = true;
  395. else
  396. options.qtInstallLibDirectory = arguments.at(++i);
  397. } else if (argument.compare(QLatin1String("--qt-install-bin-directory"), Qt::CaseInsensitive) == 0) {
  398. if (i + 1 == arguments.size())
  399. options.helpRequested = true;
  400. else
  401. options.qtInstallBinDirectory = arguments.at(++i);
  402. } else if (argument.compare(QLatin1String("--toolchain-bin-directory"), Qt::CaseInsensitive) == 0) {
  403. if (i + 1 == arguments.size())
  404. options.helpRequested = true;
  405. else
  406. options.toolchainBinDirectory = arguments.at(++i);
  407. } else if (argument.compare(QLatin1String("--toolchain-prefix"), Qt::CaseInsensitive) == 0) {
  408. if (i + 1 == arguments.size())
  409. options.helpRequested = true;
  410. else
  411. options.toolchainPrefix = arguments.at(++i);
  412. } else if (argument.compare(QLatin1String("--sdb-binary"), Qt::CaseInsensitive) == 0) {
  413. if (i + 1 == arguments.size())
  414. options.helpRequested = true;
  415. else
  416. options.sdbBinary = arguments.at(++i);
  417. } else if (argument.compare(QLatin1String("--application-binary"), Qt::CaseInsensitive) == 0) {
  418. if (i + 1 == arguments.size())
  419. options.helpRequested = true;
  420. else
  421. options.applicationBinary = arguments.at(++i);
  422. } else if (argument.compare(QLatin1String("--package-name"), Qt::CaseInsensitive) == 0) {
  423. if (i + 1 == arguments.size())
  424. options.helpRequested = true;
  425. else
  426. options.packageName= arguments.at(++i);
  427. } else if (argument.compare(QLatin1String("--package-version"), Qt::CaseInsensitive) == 0) {
  428. if (i + 1 == arguments.size())
  429. options.helpRequested = true;
  430. else
  431. options.packageVersion= arguments.at(++i);
  432. } else if (argument.compare(QLatin1String("--architecture"), Qt::CaseInsensitive) == 0) {
  433. if (i + 1 == arguments.size())
  434. options.helpRequested = true;
  435. else
  436. options.architecture = arguments.at(++i);
  437. } else if (argument.compare(QLatin1String("--qml-root-path"), Qt::CaseInsensitive) == 0) {
  438. if (i + 1 == arguments.size())
  439. options.helpRequested = true;
  440. else
  441. options.rootPath = arguments.at(++i);
  442. } else if (argument.compare(QLatin1String("--qt-platform-plugin-name"), Qt::CaseInsensitive) == 0) {
  443. if (i + 1 == arguments.size())
  444. options.helpRequested = true;
  445. else
  446. options.qtPlatformPluginName = arguments.at(++i);
  447. }
  448. }
  449. if (options.architecture.isEmpty()) {
  450. fprintf(stderr, "Error no architecture set.\n");
  451. options.helpRequested = true;
  452. }
  453. if (options.packageName.isEmpty()) {
  454. options.packageName = QString("org.qtproject.example.%1").arg(QFileInfo(options.applicationBinary).fileName());
  455. }
  456. if (options.packageVersion.isEmpty()) {
  457. options.packageVersion = QLatin1Literal("1.0.0");
  458. }
  459. if (options.toolchainBinDirectory.isEmpty()) {
  460. fprintf(stderr, "Error no toolchain-bin-directory.\n");
  461. options.helpRequested = true;
  462. }
  463. if (options.toolchainPrefix.isEmpty()) {
  464. fprintf(stderr, "Error no toolchain-prefix\n");
  465. options.helpRequested = true;
  466. }
  467. if (options.applicationBinary.isEmpty()) {
  468. fprintf(stderr, "Error application-binary was not set\n");
  469. options.helpRequested = true;
  470. }
  471. if (!options.authorKey.isEmpty())
  472. options.build = true;
  473. if (options.qtInstallLibDirectory.isEmpty())
  474. options.qtInstallLibDirectory = QDir(options.qtInstallDirectory).absoluteFilePath("lib");
  475. if (options.qtInstallBinDirectory.isEmpty())
  476. options.qtInstallBinDirectory = QDir(options.qtInstallDirectory).absoluteFilePath("bin");
  477. if (options.tizenSdkDirectory.isEmpty())
  478. options.tizenSdkDirectory = QDir::home().absoluteFilePath("tizen-sdk");
  479. if (options.sdbBinary.isEmpty())
  480. options.sdbBinary = QDir(options.tizenSdkDirectory).absoluteFilePath("tools/sdb");
  481. if (options.nativeSigningBinary.isEmpty())
  482. options.nativeSigningBinary = QDir(options.tizenSdkDirectory).absoluteFilePath("tools/ide/bin/native-signing");
  483. if (options.tizenDistributorSignerFile.isEmpty())
  484. options.tizenDistributorSignerFile = QDir(options.tizenSdkDirectory).absoluteFilePath("tools/certificate-generator/certificates/distributor/tizen-distributor-signer.p12");
  485. if (options.tizenDistributorSignerPassword.isEmpty())
  486. options.tizenDistributorSignerPassword = "tizenpkcs12passfordsigner";
  487. if (options.qtPlatformPluginName.isEmpty())
  488. options.qtPlatformPluginName = "xcb";
  489. options.timing = qEnvironmentVariableIsSet("TIZENDEPLOYQT_TIMING_OUTPUT");
  490. if (!QDir::current().mkpath(options.outputDirectory)) {
  491. fprintf(stderr, "Invalid output directory: %s\n", qPrintable(options.outputDirectory));
  492. options.outputDirectory.clear();
  493. } else {
  494. options.outputDirectory = QFileInfo(options.outputDirectory).canonicalFilePath();
  495. if (!options.outputDirectory.endsWith(QLatin1Char('/')))
  496. options.outputDirectory += QLatin1Char('/');
  497. }
  498. return options;
  499. }
  500. void printHelp()
  501. {// "012345678901234567890123456789012345678901234567890123456789012345678901"
  502. fprintf(stderr, "Syntax: %s PARAMETERS [options]\n"
  503. "\n"
  504. " Creates an Tizen package in the build directory <destination> and\n"
  505. " builds it into an .tpk file.\n\n"
  506. " PARAMETERS:\n"
  507. " --output <destination>"
  508. " --application-binary <path/to/application/binary>"
  509. " Optional arguments:\n"
  510. " --deployment <mechanism>: Supported deployment mechanisms:\n"
  511. " bundled (default): Include Qt files in stand-alone package.\n"
  512. " ministro: Use the Ministro service to manage Qt files.\n"
  513. " debug: Copy Qt files to device for quick debugging.\n"
  514. " --no-build: Do not build the package, it is useful to just install\n"
  515. " a package previously built.\n"
  516. " --install: Installs apk to device/emulator. By default this step is\n"
  517. " not taken. If the application has previously been installed on\n"
  518. " the device, it will be uninstalled first.\n"
  519. " --reinstall: Installs apk to device/emulator. By default this step\n"
  520. " is not taken. If the application has previously been installed on\n"
  521. " the device, it will be overwritten, but its data will be left\n"
  522. " intact.\n"
  523. " --device [device ID]: Use specified device for deployment. Default\n"
  524. " is the device selected by default by adb.\n"
  525. " --release: Builds a package ready for release. By default, the\n"
  526. " package will be signed with a debug key.\n"
  527. " --sdb-binary <path/to/sdb/binary/sdb> By default it is set to\n"
  528. " <tizen0sdk-directory>/tools/sdb\n"
  529. " --author-key <paths/to/author.p12> Also implies the\n"
  530. " --release option.\n"
  531. " Optional arguments for use with signing:\n"
  532. " --author-password <password>: author key password.\n"
  533. " --distributor-key <path/to/distributor.p12>\n"
  534. " Optional arguments for use with signing:\n"
  535. " --distributor-password <password>: distributor key password.\n"
  536. " --tizen-sdk-directory <path/to/tizensdk> Also implies the\n"
  537. " --qt-install-bin-directory <path>: path to host bin directory. By\n"
  538. " default it is set to <qt-install-directory>/bin\n"
  539. " --qt-install-lib-directory <path>: path to target lib directory.\n"
  540. " By default it is set to <qt-install-directory>/lib\n"
  541. " --qt-install-directory <path>: path to target qt install directory.\n"
  542. " By default it is set to <qt-install-directory>/lib\n"
  543. " --qml-root-path <path>: path to target qml directory.\n"
  544. " By default it is set to <qt-install-directory>/lib\n"
  545. " --verbose: Prints out information during processing.\n"
  546. " --help: Displays this information.\n\n",
  547. qPrintable(QCoreApplication::arguments().at(0))
  548. );
  549. }
  550. // Since strings compared will all start with the same letters,
  551. // sorting by length and then alphabetically within each length
  552. // gives the natural order.
  553. bool quasiLexicographicalReverseLessThan(const QFileInfo &fi1, const QFileInfo &fi2)
  554. {
  555. QString s1 = fi1.baseName();
  556. QString s2 = fi2.baseName();
  557. if (s1.length() == s2.length())
  558. return s1 > s2;
  559. else
  560. return s1.length() > s2.length();
  561. }
  562. // Files which contain templates that need to be overwritten by build data should be overwritten every
  563. // time.
  564. bool alwaysOverwritableFile(const QString &/*fileName*/)
  565. {
  566. return false;
  567. }
  568. bool copyFileIfNewer(const QString &sourceFileName,
  569. const QString &destinationFileName,
  570. bool verbose,
  571. bool forceOverwrite = false)
  572. {
  573. if (QFile::exists(destinationFileName)) {
  574. QFileInfo destinationFileInfo(destinationFileName);
  575. QFileInfo sourceFileInfo(sourceFileName);
  576. if (!forceOverwrite
  577. && sourceFileInfo.lastModified() <= destinationFileInfo.lastModified()
  578. && !alwaysOverwritableFile(destinationFileName)) {
  579. if (verbose)
  580. fprintf(stdout, " -- Skipping file %s. Same or newer file already in place.\n", qPrintable(sourceFileName));
  581. return true;
  582. } else {
  583. if (!QFile(destinationFileName).remove()) {
  584. fprintf(stderr, "Can't remove old file: %s\n", qPrintable(destinationFileName));
  585. return false;
  586. }
  587. }
  588. }
  589. if (!QDir().mkpath(QFileInfo(destinationFileName).path())) {
  590. fprintf(stderr, "Cannot make output directory for %s.\n", qPrintable(destinationFileName));
  591. return false;
  592. }
  593. if (!QFile::exists(destinationFileName) && !QFile::copy(sourceFileName, destinationFileName)) {
  594. fprintf(stderr, "Failed to copy %s to %s.\n", qPrintable(sourceFileName), qPrintable(destinationFileName));
  595. return false;
  596. } else {
  597. if (verbose) {
  598. fprintf(stdout, " -- Copied %s\n", qPrintable(destinationFileName));
  599. fflush(stdout);
  600. }
  601. }
  602. return true;
  603. }
  604. bool copyFiles(const QDir &sourceDirectory, const QDir &destinationDirectory, bool verbose, bool forceOverwrite = false)
  605. {
  606. QFileInfoList entries = sourceDirectory.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
  607. foreach (QFileInfo entry, entries) {
  608. if (entry.isDir()) {
  609. QDir dir(entry.absoluteFilePath());
  610. if (!destinationDirectory.mkpath(dir.dirName())) {
  611. fprintf(stderr, "Cannot make directory %s in %s\n", qPrintable(dir.dirName()), qPrintable(destinationDirectory.path()));
  612. return false;
  613. }
  614. if (!copyFiles(dir, QDir(destinationDirectory.path() + QLatin1String("/") + dir.dirName()), verbose, forceOverwrite))
  615. return false;
  616. } else {
  617. QString destination = destinationDirectory.absoluteFilePath(entry.fileName());
  618. if (!copyFileIfNewer(entry.absoluteFilePath(), destination, verbose, forceOverwrite))
  619. return false;
  620. }
  621. }
  622. return true;
  623. }
  624. void cleanTopFolders(const Options &options, const QDir &srcDir, const QString &dstDir)
  625. {
  626. foreach (const QFileInfo &dir, srcDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs)) {
  627. if (dir.fileName() != QLatin1String("libs"))
  628. deleteMissingFiles(options, dir.absoluteFilePath(), dstDir + dir.fileName());
  629. }
  630. }
  631. QStringList allFilesInside(const QDir& current, const QDir& rootDir)
  632. {
  633. QStringList files;
  634. foreach (QString dir, current.entryList(QDir::Dirs|QDir::NoDotAndDotDot)) {
  635. files += allFilesInside(QDir(current.filePath(dir)), rootDir);
  636. }
  637. foreach (QString file, current.entryList(QDir::Files)) {
  638. files += rootDir.relativeFilePath(current.filePath(file));
  639. }
  640. return files;
  641. }
  642. bool updateFile(const QString &fileName, const QHash<QString, QString> &replacements)
  643. {
  644. QFile inputFile(fileName);
  645. if (!inputFile.open(QIODevice::ReadOnly)) {
  646. fprintf(stderr, "Cannot open %s for reading.\n", qPrintable(fileName));
  647. return false;
  648. }
  649. // All the files we are doing substitutes in are quite small. If this
  650. // ever changes, this code should be updated to be more conservative.
  651. QByteArray contents = inputFile.readAll();
  652. bool hasReplacements = false;
  653. QHash<QString, QString>::const_iterator it;
  654. for (it = replacements.constBegin(); it != replacements.constEnd(); ++it) {
  655. forever {
  656. int index = contents.indexOf(it.key().toUtf8());
  657. if (index >= 0) {
  658. contents.replace(index, it.key().length(), it.value().toUtf8());
  659. hasReplacements = true;
  660. } else {
  661. break;
  662. }
  663. }
  664. }
  665. if (hasReplacements) {
  666. inputFile.close();
  667. if (!inputFile.open(QIODevice::WriteOnly)) {
  668. fprintf(stderr, "Cannot open %s for writing.\n", qPrintable(fileName));
  669. return false;
  670. }
  671. inputFile.write(contents);
  672. }
  673. return true;
  674. }
  675. bool updateStringsXml(const Options &options)
  676. {
  677. if (options.verbose)
  678. fprintf(stdout, " -- res/values/strings.xml\n");
  679. QHash<QString, QString> replacements;
  680. replacements["<!-- %%INSERT_APP_NAME%% -->"] = QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1);
  681. QString fileName = options.outputDirectory + QLatin1String("/res/values/strings.xml");
  682. if (!QFile::exists(fileName)) {
  683. if (options.verbose)
  684. fprintf(stdout, " -- Create strings.xml since it's missing.\n");
  685. QFile file(fileName);
  686. if (!file.open(QIODevice::WriteOnly)) {
  687. fprintf(stderr, "Can't open %s for writing.\n", qPrintable(fileName));
  688. return false;
  689. }
  690. file.write(QByteArray("<?xml version='1.0' encoding='utf-8'?><resources><string name=\"app_name\">")
  691. .append(QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1).toLatin1())
  692. .append("</string></resources>\n"));
  693. return true;
  694. }
  695. if (!updateFile(fileName, replacements))
  696. return false;
  697. // ant can't (easily) build multiple res folders,
  698. // so we need to replace the "<!-- %%INSERT_STRINGS -->" placeholder
  699. // from the main res folder
  700. QFile stringsXml(fileName);
  701. if (!stringsXml.open(QIODevice::ReadOnly)) {
  702. fprintf(stderr, "Cannot open %s for reading.\n", qPrintable(fileName));
  703. return false;
  704. }
  705. QXmlStreamReader reader(&stringsXml);
  706. while (!reader.atEnd()) {
  707. reader.readNext();
  708. if (reader.isStartElement() &&
  709. reader.name() == QLatin1String("string") &&
  710. reader.attributes().hasAttribute(QLatin1String("name")) &&
  711. reader.attributes().value(QLatin1String("name")) == QLatin1String("app_name")) {
  712. return true;
  713. }
  714. }
  715. replacements.clear();
  716. replacements["<!-- %%INSERT_STRINGS -->"] = QString(QLatin1String("<string name=\"app_name\">%1</string>\n"))
  717. .arg(QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1));
  718. if (!updateFile(fileName, replacements))
  719. return false;
  720. return true;
  721. }
  722. QList<QtDependency> findFilesRecursively(const Options &options, const QFileInfo &info, const QString &rootPath)
  723. {
  724. if (!info.exists())
  725. return QList<QtDependency>();
  726. if (info.isDir()) {
  727. QList<QtDependency> ret;
  728. QDir dir(info.filePath());
  729. QStringList entries = dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
  730. foreach (QString entry, entries) {
  731. QString s = info.absoluteFilePath() + QLatin1Char('/') + entry;
  732. ret += findFilesRecursively(options, s, rootPath);
  733. }
  734. return ret;
  735. } else {
  736. return QList<QtDependency>() << QtDependency(info.absoluteFilePath().mid(rootPath.length()), info.absoluteFilePath());
  737. }
  738. }
  739. QList<QtDependency> findFilesRecursively(const Options &options, const QString &fileName)
  740. {
  741. QFileInfo info(options.qtInstallDirectory + QLatin1Char('/') + fileName);
  742. return findFilesRecursively(options, info, options.qtInstallDirectory + QLatin1Char('/'));
  743. }
  744. QStringList getQtLibsFromElf(const Options &options, const QString &fileName)
  745. {
  746. QString readElf = QDir(options.toolchainBinDirectory)
  747. .absoluteFilePath(options.toolchainPrefix)
  748. + QLatin1String("-readelf");
  749. #if defined(Q_OS_WIN32)
  750. readElf += QLatin1String(".exe");
  751. #endif
  752. if (!QFile::exists(readElf)) {
  753. fprintf(stderr, "Command does not exist: %s\n", qPrintable(readElf));
  754. return QStringList();
  755. }
  756. readElf = QString::fromLatin1("%1 -d -W %2").arg(shellQuote(readElf)).arg(shellQuote(fileName));
  757. FILE *readElfCommand = openProcess(readElf);
  758. if (readElfCommand == 0) {
  759. fprintf(stderr, "Cannot execute command %s", qPrintable(readElf));
  760. return QStringList();
  761. }
  762. QStringList ret;
  763. char buffer[512];
  764. while (fgets(buffer, sizeof(buffer), readElfCommand) != 0) {
  765. QByteArray line = QByteArray::fromRawData(buffer, qstrlen(buffer));
  766. if (line.contains("(NEEDED)") && line.contains("Shared library:") ) {
  767. const int pos = line.lastIndexOf('[') + 1;
  768. QString libraryName = QString::fromLatin1(line.mid(pos, line.length() - pos - 2));
  769. if (! libraryName.startsWith("libQt"))
  770. continue;
  771. if (QFile::exists(options.qtInstallLibDirectory + QLatin1Char('/') + libraryName)) {
  772. ret += QString("lib/") + libraryName;
  773. }
  774. }
  775. }
  776. pclose(readElfCommand);
  777. return ret;
  778. }
  779. bool readDependenciesFromElf(Options *options,
  780. const QString &fileName,
  781. QSet<QString> *usedDependencies,
  782. QSet<QString> *remainingDependencies)
  783. {
  784. // Get dependencies on libraries in $QTDIR/lib
  785. QStringList dependencies = getQtLibsFromElf(*options, fileName);
  786. if (options->verbose) {
  787. fprintf(stdout, "Reading dependencies from %s\n", qPrintable(fileName));
  788. foreach (QString dep, dependencies)
  789. fprintf(stdout, " %s\n", qPrintable(dep));
  790. }
  791. // Recursively add dependencies from ELF and supplementary XML information
  792. foreach (QString dependency, dependencies) {
  793. if (usedDependencies->contains(dependency))
  794. continue;
  795. QString absoluteDependencyPath(options->qtInstallLibDirectory + QLatin1Char('/') + QString(dependency).remove("lib/"));
  796. usedDependencies->insert(dependency);
  797. if (!readDependenciesFromElf(options,
  798. absoluteDependencyPath,
  799. usedDependencies,
  800. remainingDependencies)) {
  801. return false;
  802. }
  803. options->qtDependencies.append(QtDependency(dependency, absoluteDependencyPath));
  804. if (options->verbose)
  805. fprintf(stdout, "Appending dependency: %s\n", qPrintable(dependency));
  806. }
  807. return true;
  808. }
  809. bool goodToCopy(const Options *options, const QString &file, QStringList *unmetDependencies);
  810. bool scanImports(Options *options, QSet<QString> *usedDependencies)
  811. {
  812. if (options->verbose)
  813. fprintf(stdout, "Scanning for QML imports.\n");
  814. QString qmlImportScanner = QCoreApplication::applicationDirPath() + QLatin1String("/qmlimportscanner");
  815. #if defined(Q_OS_WIN32)
  816. qmlImportScanner += QLatin1String(".exe");
  817. #endif
  818. if (!QFile::exists(qmlImportScanner)) {
  819. fprintf(stderr, "qmlimportscanner not found: %s\n", qPrintable(qmlImportScanner));
  820. return false;
  821. }
  822. QString rootPath = options->rootPath;
  823. QStringList importPaths;
  824. importPaths += shellQuote(options->qtInstallDirectory + QLatin1String("/qml"));
  825. importPaths += QFileInfo(rootPath).absoluteFilePath();
  826. foreach (QString qmlImportPath, options->qmlImportPaths)
  827. importPaths += shellQuote(qmlImportPath);
  828. qmlImportScanner += QString::fromLatin1(" -rootPath %1 -importPath %2")
  829. .arg(shellQuote(rootPath))
  830. .arg(importPaths.join(QLatin1Char(' ')));
  831. if (options->verbose) {
  832. fprintf(stdout, "Running qmlimportscanner: %s.\n", qPrintable(qmlImportScanner));
  833. }
  834. FILE *qmlImportScannerCommand = popen(qmlImportScanner.toLocal8Bit().constData(), "r");
  835. if (qmlImportScannerCommand == 0) {
  836. fprintf(stderr, "Couldn't run qmlimportscanner.\n");
  837. return false;
  838. }
  839. QByteArray output;
  840. char buffer[512];
  841. while (fgets(buffer, sizeof(buffer), qmlImportScannerCommand) != 0)
  842. output += QByteArray(buffer, qstrlen(buffer));
  843. QJsonDocument jsonDocument = QJsonDocument::fromJson(output);
  844. if (jsonDocument.isNull()) {
  845. fprintf(stderr, "Invalid json output from qmlimportscanner.\n");
  846. return false;
  847. }
  848. QString absoluteOutputPath = QFileInfo(options->outputDirectory).absoluteFilePath();
  849. QJsonArray jsonArray = jsonDocument.array();
  850. for (int i=0; i<jsonArray.count(); ++i) {
  851. QJsonValue value = jsonArray.at(i);
  852. if (!value.isObject()) {
  853. fprintf(stderr, "Invalid format of qmlimportscanner output.\n");
  854. return false;
  855. }
  856. QJsonObject object = value.toObject();
  857. QString path = object.value(QLatin1String("path")).toString();
  858. if (path.isEmpty()) {
  859. fprintf(stderr, "Warning: QML import could not be resolved in any of the import paths: %s\n",
  860. qPrintable(object.value(QLatin1String("name")).toString()));
  861. } else {
  862. if (options->verbose)
  863. fprintf(stdout, " -- Adding '%s' as QML dependency\n", path.toLocal8Bit().constData());
  864. QFileInfo info(path);
  865. // The qmlimportscanner sometimes outputs paths that do not exist.
  866. if (!info.exists()) {
  867. if (options->verbose)
  868. fprintf(stdout, " -- Skipping because file does not exist.\n");
  869. continue;
  870. }
  871. QString importPathOfThisImport;
  872. foreach (QString importPath, importPaths) {
  873. #if defined(Q_OS_WIN32)
  874. Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
  875. #else
  876. Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
  877. #endif
  878. QString cleanImportPath = QDir::cleanPath(importPath);
  879. if (info.absoluteFilePath().startsWith(cleanImportPath, caseSensitivity)) {
  880. importPathOfThisImport = importPath;
  881. break;
  882. }
  883. }
  884. if (importPathOfThisImport.isEmpty()) {
  885. fprintf(stderr, "Import found outside of import paths: %s.\n", qPrintable(info.absoluteFilePath()));
  886. return false;
  887. }
  888. QDir dir(importPathOfThisImport);
  889. importPathOfThisImport = dir.absolutePath() + QLatin1Char('/');
  890. QList<QtDependency> fileNames = findFilesRecursively(*options, info, importPathOfThisImport);
  891. foreach (QtDependency fileName, fileNames) {
  892. if (usedDependencies->contains(fileName.absolutePath))
  893. continue;
  894. usedDependencies->insert(fileName.absolutePath);
  895. if (options->verbose)
  896. fprintf(stdout, " -- Appending dependency found by qmlimportscanner: %s\n", qPrintable(fileName.absolutePath));
  897. // Put all imports in default import path in assets
  898. fileName.relativePath.prepend("qml/");
  899. options->qtDependencies.append(fileName);
  900. if (fileName.absolutePath.endsWith(QLatin1String(".so"))) {
  901. QSet<QString> remainingDependencies;
  902. if (!readDependenciesFromElf(options, fileName.absolutePath, usedDependencies, &remainingDependencies))
  903. return false;
  904. }
  905. }
  906. }
  907. }
  908. return true;
  909. }
  910. bool readDependencies(Options *options)
  911. {
  912. if (options->verbose)
  913. fprintf(stdout, "Detecting dependencies of application.\n");
  914. // Override set in .pro file
  915. if (!options->qtDependencies.isEmpty()) {
  916. if (options->verbose)
  917. fprintf(stdout, "\tDependencies explicitly overridden in .pro file. No detection needed.\n");
  918. return true;
  919. }
  920. if (options->deploymentMechanism == Options::Target)
  921. return true;
  922. QSet<QString> usedDependencies;
  923. QSet<QString> remainingDependencies;
  924. // Add dependencies of application binary first
  925. if (!readDependenciesFromElf(options, options->applicationBinary, &usedDependencies, &remainingDependencies))
  926. return false;
  927. QString qtDir = options->qtInstallDirectory + QLatin1Char('/');
  928. //Additiong default plugin if set
  929. if (!options->qtPlatformPluginName.isEmpty()) {
  930. // Add dependencies of application binary first
  931. QString relativePluginPath = QString("plugins/platforms/libq%1.so").arg(options->qtPlatformPluginName);
  932. QString absolutePluginPath = qtDir + relativePluginPath;
  933. options->qtDependencies.append(QtDependency(relativePluginPath, absolutePluginPath));
  934. if (!readDependenciesFromElf(options, absolutePluginPath, &usedDependencies, &remainingDependencies))
  935. return false;
  936. }
  937. while (!remainingDependencies.isEmpty()) {
  938. QSet<QString>::iterator start = remainingDependencies.begin();
  939. QString fileName = qtDir + *start;
  940. remainingDependencies.erase(start);
  941. QStringList unmetDependencies;
  942. if (goodToCopy(options, fileName, &unmetDependencies)) {
  943. bool ok = readDependenciesFromElf(options, fileName, &usedDependencies, &remainingDependencies);
  944. if (!ok)
  945. return false;
  946. } else if (options->verbose) {
  947. fprintf(stdout, "Skipping %s due to unmet dependencies: %s\n",
  948. qPrintable(fileName),
  949. qPrintable(unmetDependencies.join(QLatin1Char(','))));
  950. }
  951. }
  952. QStringList::iterator it = options->localLibs.begin();
  953. while (it != options->localLibs.end()) {
  954. QStringList unmetDependencies;
  955. if (!goodToCopy(options, qtDir + *it, &unmetDependencies)) {
  956. if (options->verbose) {
  957. fprintf(stdout, "Skipping %s due to unmet dependencies: %s\n",
  958. qPrintable(*it),
  959. qPrintable(unmetDependencies.join(QLatin1Char(','))));
  960. }
  961. it = options->localLibs.erase(it);
  962. } else {
  963. ++it;
  964. }
  965. }
  966. if (!options->rootPath.isEmpty() && !scanImports(options, &usedDependencies))
  967. return false;
  968. return true;
  969. }
  970. bool stripFile(const Options &options, const QString &fileName)
  971. {
  972. QString strip = QDir(options.toolchainBinDirectory)
  973. .absoluteFilePath(options.toolchainPrefix)
  974. + QLatin1String("-strip");
  975. #if defined(Q_OS_WIN32)
  976. strip += QLatin1String(".exe");
  977. #endif
  978. if (!QFile::exists(strip)) {
  979. fprintf(stderr, "Command does not exist: %s\n", qPrintable(strip));
  980. return false;
  981. }
  982. strip = QString::fromLatin1("%1 %2").arg(shellQuote(strip)).arg(shellQuote(fileName));
  983. FILE *stripCommand = openProcess(strip);
  984. if (stripCommand == 0) {
  985. fprintf(stderr, "Cannot execute command %s", qPrintable(strip));
  986. return false;
  987. }
  988. pclose(stripCommand);
  989. return true;
  990. }
  991. bool stripLibraries(const Options &options)
  992. {
  993. if (options.verbose)
  994. fprintf(stdout, "Stripping libraries to minimize size.\n");
  995. QString libraryPath = options.outputDirectory
  996. + QLatin1String("/lib/");
  997. QStringList libraries = QDir(libraryPath).entryList(QDir::Files);
  998. foreach (QString library, libraries) {
  999. if (library.endsWith(".so")) {
  1000. if (!stripFile(options, libraryPath + QLatin1Char('/') + library))
  1001. return false;
  1002. }
  1003. }
  1004. return true;
  1005. }
  1006. bool containsApplicationBinary(const Options &options)
  1007. {
  1008. if (options.verbose)
  1009. fprintf(stdout, "Checking if application binary is in package.\n");
  1010. QFileInfo applicationBinary(options.applicationBinary);
  1011. if (!QFile::exists(options.applicationBinary)) {
  1012. #if defined(Q_OS_WIN32)
  1013. QLatin1String makeTool("mingw32-make"); // Only Mingw host builds supported on Windows currently
  1014. #else
  1015. QLatin1String makeTool("make");
  1016. #endif
  1017. fprintf(stderr, "Application binary is not in output directory: %s. Please run '%s install INSTALL_ROOT=%s' first.\n",
  1018. qPrintable(options.applicationBinary),
  1019. qPrintable(makeTool),
  1020. qPrintable(options.outputDirectory));
  1021. return false;
  1022. }
  1023. return true;
  1024. }
  1025. FILE *runSdb(const Options &options, const QString &arguments)
  1026. {
  1027. if (!QFile::exists(options.sdbBinary)) {
  1028. fprintf(stderr, "Cannot find sdb tool: %s\n", qPrintable(options.sdbBinary));
  1029. return 0;
  1030. }
  1031. QString installOption;
  1032. if (!options.installLocation.isEmpty())
  1033. installOption = QLatin1String(" -s ") + shellQuote(options.installLocation);
  1034. QString sdb = QString::fromLatin1("%1%2 %3").arg(shellQuote(options.sdbBinary)).arg(installOption).arg(arguments);
  1035. if (options.verbose)
  1036. fprintf(stdout, "Running command \"%s\"\n", sdb.toLocal8Bit().constData());
  1037. FILE *sdbCommand = openProcess(sdb);
  1038. if (sdbCommand == 0) {
  1039. fprintf(stderr, "Cannot start sdb: %s\n", qPrintable(sdb));
  1040. return 0;
  1041. }
  1042. return sdbCommand;
  1043. }
  1044. bool fetchRemoteModifications(Options *options, const QString &directory)
  1045. {
  1046. options->fetchedRemoteModificationDates = true;
  1047. QString modificationFileName = directory + QLatin1String("/modification.txt");
  1048. FILE *sdbCommand = runSdb(*options, QLatin1String("shell ") + shellQuote(QString(" [ -e %1 ] && cat %1").arg(modificationFileName)));
  1049. if (sdbCommand == 0)
  1050. return false;
  1051. char buffer[2048];
  1052. QStringList qtPaths;
  1053. while(fgets(buffer, sizeof(buffer), sdbCommand) != 0)
  1054. qtPaths << QString::fromUtf8(buffer,qstrlen(buffer)).trimmed();
  1055. pclose(sdbCommand);
  1056. if (qtPaths.size() < 2 || options->qtInstallDirectory != qtPaths.at(0) || options->qtInstallLibDirectory != qtPaths.at(1)) {
  1057. sdbCommand = runSdb(*options, QLatin1String(" shell rm -rf ") + shellQuote(directory));
  1058. if (options->verbose) {
  1059. fprintf(stdout, " -- Removing old Qt libs.\n");
  1060. while (fgets(buffer, sizeof(buffer), sdbCommand) != 0)
  1061. fprintf(stdout, "%s", buffer);
  1062. }
  1063. pclose(sdbCommand);
  1064. }
  1065. sdbCommand = runSdb(*options, QLatin1String("shell ") + shellQuote(QString(" [ -e %1 ] && stat -c %Y %1").arg(modificationFileName)));
  1066. if (sdbCommand == 0)
  1067. return false;
  1068. QByteArray result;
  1069. while (fgets(buffer, sizeof(buffer), sdbCommand) != 0)
  1070. result += QByteArray::fromRawData(buffer, qstrlen(buffer)).trimmed();
  1071. pclose(sdbCommand);
  1072. bool ok = true;
  1073. quint64 msecsSinceEpoch = result.isEmpty() ? 0 : result.toULongLong(&ok) * 1000;
  1074. if (!ok)
  1075. fprintf(stderr, "wrong date format of modification file. Output: %s", qPrintable(result));
  1076. options->remoteModificationDate = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch);
  1077. {
  1078. QFile file(options->temporaryDirectoryName + "/modification.txt");
  1079. if (!file.open(QIODevice::WriteOnly)) {
  1080. fprintf(stderr, "Cannot create modification timestamp in file %s. Error: %s.\n", qPrintable(file.fileName()),qPrintable(file.errorString()));
  1081. return false;
  1082. }
  1083. file.write(options->qtInstallDirectory.toUtf8()+"\n"+options->qtInstallLibDirectory.toUtf8());
  1084. }
  1085. return true;
  1086. }
  1087. bool goodToCopy(const Options *options, const QString &file, QStringList *unmetDependencies)
  1088. {
  1089. if (!file.endsWith(QLatin1String(".so")))
  1090. return true;
  1091. bool ret = true;
  1092. foreach (const QString &lib, getQtLibsFromElf(*options, file)) {
  1093. if (!options->qtDependencies.contains(QtDependency(lib, options->qtInstallLibDirectory + QLatin1Char('/') + QString(lib).remove("lib/")))) {
  1094. ret = false;
  1095. unmetDependencies->append(lib);
  1096. }
  1097. }
  1098. return ret;
  1099. }
  1100. bool deployToLocalTmp(Options *options,
  1101. const QString &qtDependency, bool &wasCopiedOrUpdated)
  1102. {
  1103. if (!options->installTpk)
  1104. return true;
  1105. if (!options->fetchedRemoteModificationDates)
  1106. fetchRemoteModifications(options, QLatin1String("/tmp/qt"));
  1107. QString absFileName;
  1108. if (qtDependency.startsWith("lib/"))
  1109. absFileName = QString(options->qtInstallLibDirectory + QLatin1Char('/') + QString(qtDependency).remove(0,4));
  1110. else
  1111. absFileName = QString(options->qtInstallDirectory + QLatin1Char('/') + qtDependency);
  1112. QFileInfo fileInfo(absFileName);
  1113. // Make sure precision is the same as what we get from Tizen
  1114. QDateTime sourceModified = QDateTime::fromTime_t(fileInfo.lastModified().toTime_t());
  1115. if (options->remoteModificationDate.isNull() || options->remoteModificationDate < sourceModified) {
  1116. if (!copyFileIfNewer(absFileName,
  1117. options->temporaryDirectoryName + QLatin1Char('/') + qtDependency,
  1118. options->verbose)) {
  1119. return false;
  1120. }
  1121. if (qtDependency.endsWith(QLatin1String(".so"))
  1122. && !stripFile(*options, options->temporaryDirectoryName + QLatin1Char('/') + qtDependency)) {
  1123. return false;
  1124. }
  1125. wasCopiedOrUpdated = true;
  1126. } else
  1127. wasCopiedOrUpdated = false;
  1128. return true;
  1129. }
  1130. bool chrpath(const QString &rpath, const QStringList &files, Options *options, const QRegExp &paddingPatternToReplace = QRegExp())
  1131. {
  1132. if (files.isEmpty())
  1133. return true;
  1134. QString chrpathCommandStr;
  1135. FILE *chrpathCommand;
  1136. QHash<QString, QStringList> rpathsAndFiles;
  1137. if (!paddingPatternToReplace.isEmpty()) {
  1138. chrpathCommandStr = QLatin1String("chrpath -l ");
  1139. if (options->verbose) {
  1140. fprintf(stdout, "Running: %s\n", qPrintable(chrpathCommandStr + files.join(" ")));
  1141. }
  1142. chrpathCommand = openProcess(chrpathCommandStr + files.join(" "));
  1143. if (chrpathCommand == 0) {
  1144. fprintf(stderr, "Cannot execute command %s", qPrintable(files.join(" ")));
  1145. return false;
  1146. }
  1147. size_t bufferSize = 0;
  1148. char *rawLine = 0;
  1149. QRegExp rpathRunPathRegExp("RPATH=|RUNTPATH=");
  1150. QString line;
  1151. int lineLength = 0;
  1152. while ((lineLength = getline(&rawLine, &bufferSize, chrpathCommand)) != -1) {
  1153. line = QString::fromLatin1(rawLine, lineLength);
  1154. if (line.contains(paddingPatternToReplace)) {
  1155. rpathsAndFiles[line.mid(line.indexOf(QLatin1Char(':'))+1)
  1156. .replace(rpathRunPathRegExp,QString::null)
  1157. .replace(paddingPatternToReplace,rpath).trimmed()] << (line.left(line.indexOf(QLatin1Char(':'))).trimmed());
  1158. }
  1159. }
  1160. delete rawLine;
  1161. int returnCode = pclose(chrpathCommand);
  1162. if (returnCode != 0) {
  1163. fprintf(stderr, "Warning: chrpath failed!\n");
  1164. if (!options->verbose)
  1165. fprintf(stderr, " -- Run with --verbose for more information.\n");
  1166. return false;
  1167. }
  1168. } else {
  1169. rpathsAndFiles[rpath] = files;
  1170. }
  1171. foreach(const QString &fileRpath, rpathsAndFiles.keys()) {
  1172. chrpathCommandStr = QString::fromLatin1("chrpath -c -r %1 %2")
  1173. .arg(shellQuote(fileRpath))
  1174. .arg(rpathsAndFiles.value(fileRpath).join(QLatin1Char(' ')));
  1175. if (options->verbose)
  1176. fprintf(stdout, "Running: %s\n", qPrintable(chrpathCommandStr));
  1177. chrpathCommand = openProcess(chrpathCommandStr);
  1178. if (chrpathCommand == 0) {
  1179. fprintf(stderr, "Cannot execute command %s\n", qPrintable(chrpathCommandStr));
  1180. return false;
  1181. }
  1182. char buffer[512];
  1183. while (fgets(buffer, sizeof(buffer), chrpathCommand) != 0)
  1184. if (options->verbose)
  1185. fprintf(stdout, "%s", buffer);
  1186. int returnCode = pclose(chrpathCommand);
  1187. if (returnCode != 0) {
  1188. fprintf(stderr, "Warning: chrpath failed!\n");
  1189. if (!options->verbose)
  1190. fprintf(stderr, " -- Run with --verbose for more information.\n");
  1191. return false;
  1192. }
  1193. }
  1194. return true;
  1195. }
  1196. bool copyQtFiles(Options *options)
  1197. {
  1198. if (options->verbose) {
  1199. switch (options->deploymentMechanism) {
  1200. case Options::Bundled:
  1201. fprintf(stdout, "Copying %d dependencies from Qt into package.\n", options->qtDependencies.size());
  1202. break;
  1203. case Options::Target:
  1204. fprintf(stdout, "Setting %d dependencies from Qt in package.\n", options->qtDependencies.size());
  1205. break;
  1206. case Options::Debug:
  1207. fprintf(stdout, "Copying %d dependencies from Qt to device.\n", options->qtDependencies.size());
  1208. break;
  1209. };
  1210. }
  1211. QStringList chrpathFiles;
  1212. QString rpath(QString::fromLatin1("/opt/usr/apps/%1/lib").arg(options->packageName));
  1213. {
  1214. QFileInfo fInfo(options->applicationBinary);
  1215. QString correspondingBinaryName;
  1216. QString absolutePath = fInfo.absolutePath().append(QLatin1Char('/'));
  1217. if (fInfo.fileName().endsWith(".so")) {
  1218. correspondingBinaryName = absolutePath + fInfo.fileName()
  1219. .replace(QRegExp("^lib"),"")
  1220. .replace(QRegExp(".so$"),"");
  1221. } else {
  1222. correspondingBinaryName = absolutePath + fInfo.fileName()
  1223. .prepend(QLatin1String("lib"))
  1224. .prepend(".so");
  1225. }
  1226. chrpathFiles << fInfo.absoluteFilePath();
  1227. if (QFile::exists(correspondingBinaryName))
  1228. chrpathFiles << correspondingBinaryName;
  1229. }
  1230. if (options->deploymentMechanism == Options::Debug) {
  1231. // For debug deployment, we copy all libraries and plugins
  1232. rpath.append(":/tmp/qt/lib");
  1233. QDirIterator installDirIterator(options->qtInstallDirectory, QDirIterator::Subdirectories);
  1234. QDirIterator installLibDirIterator(options->qtInstallLibDirectory, QDirIterator::Subdirectories);
  1235. QDirIterator *dirIterator;
  1236. QString relativePathPrefix;
  1237. if (installDirIterator.hasNext())
  1238. dirIterator = &installDirIterator;
  1239. else {
  1240. dirIterator = &installLibDirIterator;
  1241. relativePathPrefix = QString("lib/");
  1242. }
  1243. bool wasCopiedOrUpdated;
  1244. while (dirIterator->hasNext()) {
  1245. dirIterator->next();
  1246. QFileInfo info = dirIterator->fileInfo();
  1247. if (!info.isDir()) {
  1248. QString relativePath = info.absoluteFilePath().mid(dirIterator->path().length());
  1249. if (relativePath.startsWith(QLatin1Char('/')))
  1250. relativePath.remove(0, 1);
  1251. relativePath = relativePathPrefix + relativePath;
  1252. if ((relativePath.startsWith("lib/libQt") && relativePath.endsWith(".so.5"))
  1253. || relativePath.startsWith("plugins/")
  1254. || relativePath.startsWith("imports/")
  1255. || relativePath.startsWith("qml/")) {
  1256. wasCopiedOrUpdated = false;
  1257. if (!deployToLocalTmp(options, relativePath, wasCopiedOrUpdated))
  1258. return false;
  1259. if(wasCopiedOrUpdated)
  1260. options->bundledFiles += qMakePair(options->temporaryDirectoryName + QLatin1Char('/') + relativePath, relativePath);
  1261. }
  1262. }
  1263. if (!dirIterator->hasNext() && dirIterator == &installDirIterator) {
  1264. dirIterator = &installLibDirIterator;
  1265. relativePathPrefix = QString("lib/");
  1266. }
  1267. }
  1268. } else if (options->deploymentMechanism == Options::Bundled) {
  1269. if (!options->build)
  1270. return true;
  1271. QString libDestinationDirectory = "lib/qt/";
  1272. rpath.append(":/opt/usr/apps/%1/lib");
  1273. // Copy other Qt dependencies
  1274. foreach (QtDependency qtDependency, options->qtDependencies) {
  1275. QString sourceFileName = qtDependency.absolutePath;
  1276. QString destinationFileName;
  1277. destinationFileName = libDestinationDirectory + qtDependency.relativePath;
  1278. if (!QFile::exists(sourceFileName)) {
  1279. fprintf(stderr, "Source Qt file does not exist: %s.\n", qPrintable(sourceFileName));
  1280. return false;
  1281. }
  1282. QStringList unmetDependencies;
  1283. if (!goodToCopy(options, sourceFileName, &unmetDependencies)) {
  1284. if (options->verbose) {
  1285. fprintf(stdout, " -- Skipping %s. It has unmet dependencies: %s.\n",
  1286. qPrintable(sourceFileName),
  1287. qPrintable(unmetDependencies.join(QLatin1Char(','))));
  1288. }
  1289. continue;
  1290. }
  1291. if (options->deploymentMechanism == Options::Bundled
  1292. && !copyFileIfNewer(sourceFileName,
  1293. options->outputDirectory + QLatin1Char('/') + destinationFileName,
  1294. options->verbose)) {
  1295. return false;
  1296. }
  1297. options->bundledFiles += qMakePair(options->outputDirectory + QLatin1Char('/') + destinationFileName, qtDependency.relativePath);
  1298. }
  1299. }
  1300. foreach (Options::BundledFile bundled, options->bundledFiles) {
  1301. if (bundled.first.endsWith(".so") || bundled.first.endsWith(".so.5"))
  1302. chrpathFiles << shellQuote(bundled.first);
  1303. }
  1304. return chrpath(rpath, chrpathFiles, options, QRegExp("@_+PADDING_+@"));
  1305. }
  1306. QString findInPath(const QString &fileName)
  1307. {
  1308. QString path = qgetenv("PATH");
  1309. #if defined(Q_OS_WIN32)
  1310. QLatin1Char separator(';');
  1311. #else
  1312. QLatin1Char separator(':');
  1313. #endif
  1314. QStringList paths = path.split(separator);
  1315. foreach (QString path, paths) {
  1316. QFileInfo fileInfo(path + QLatin1Char('/') + fileName);
  1317. if (fileInfo.exists() && fileInfo.isFile() && fileInfo.isExecutable())
  1318. return path + QLatin1Char('/') + fileName;
  1319. }
  1320. return QString();
  1321. }
  1322. bool uninstallTpk(const Options &options)
  1323. {
  1324. if (options.verbose)
  1325. fprintf(stdout, "Uninstalling old Tizen package %s if present.\n", qPrintable(options.packageName));
  1326. FILE *sdbCommand = runSdb(options, QLatin1String(" shell pkgcmd -u -n") + shellQuote(options.packageName));
  1327. if (sdbCommand == 0)
  1328. return false;
  1329. if (options.verbose || mustReadOutputAnyway) {
  1330. char buffer[512];
  1331. while (fgets(buffer, sizeof(buffer), sdbCommand) != 0)
  1332. if (options.verbose)
  1333. fprintf(stdout, "%s", buffer);
  1334. }
  1335. int returnCode = pclose(sdbCommand);
  1336. if (returnCode != 0) {
  1337. fprintf(stderr, "Warning: Uninstall failed!\n");
  1338. if (!options.verbose)
  1339. fprintf(stderr, " -- Run with --verbose for more information.\n");
  1340. return false;
  1341. }
  1342. return true;
  1343. }
  1344. enum PackageType {
  1345. UnsignedAPK,
  1346. SignedAPK
  1347. };
  1348. QString tpkPath(const Options &options)
  1349. {
  1350. QString path(options.outputDirectory);
  1351. QString tpkName = QString("%1_%2_%3.tpk")
  1352. .arg(cleanPackageName(options.packageName))
  1353. .arg(QString(options.packageVersion).replace(QLatin1Char('.'),QLatin1Char('_')))
  1354. .arg(options.architecture);
  1355. return shellQuote(QDir(path).absoluteFilePath(tpkName));
  1356. }
  1357. bool installTpk(const Options &options)
  1358. {
  1359. fflush(stdout);
  1360. // Uninstall if necessary
  1361. if (options.unInstallTpk)
  1362. uninstallTpk(options);
  1363. if (options.verbose)
  1364. fprintf(stdout, "Installing Tizen package to device.\n");
  1365. FILE *sdbCommand = runSdb(options,
  1366. QLatin1String(" install ")
  1367. + tpkPath(options));
  1368. if (sdbCommand == 0)
  1369. return false;
  1370. if (options.verbose || mustReadOutputAnyway) {
  1371. char buffer[512];
  1372. while (fgets(buffer, sizeof(buffer), sdbCommand) != 0)
  1373. if (options.verbose)
  1374. fprintf(stdout, "%s", buffer);
  1375. }
  1376. int returnCode = pclose(sdbCommand);
  1377. if (returnCode != 0) {
  1378. fprintf(stderr, "Installing to device failed!\n");
  1379. if (!options.verbose)
  1380. fprintf(stderr, " -- Run with --verbose for more information.\n");
  1381. return false;
  1382. }
  1383. return true;
  1384. }
  1385. bool copyGnuStl(Options *options)
  1386. {
  1387. if (options->deploymentMechanism == Options::Debug && !options->installTpk)
  1388. return true;
  1389. if (options->verbose)
  1390. fprintf(stdout, "Copying GNU STL library\n");
  1391. // QString filePath = options->ndkPath
  1392. // + QLatin1String("/sources/cxx-stl/gnu-libstdc++/")
  1393. // + options->toolchainVersion
  1394. // + QLatin1String("/libs/")
  1395. // + options->architecture
  1396. // + QLatin1String("/libgnustl_shared.so");
  1397. // if (!QFile::exists(filePath)) {
  1398. // fprintf(stderr, "GNU STL library does not exist at %s\n", qPrintable(filePath));
  1399. // return false;
  1400. // }
  1401. // QString destinationDirectory =
  1402. // options->deploymentMechanism == Options::Debug
  1403. // ? options->temporaryDirectoryName + QLatin1String("/lib")
  1404. // : options->outputDirectory + QLatin1String("/libs/") + options->architecture;
  1405. // if (!copyFileIfNewer(filePath, destinationDirectory
  1406. // + QLatin1String("/libgnustl_shared.so"), options->verbose)) {
  1407. // return false;
  1408. // }
  1409. // if (options->deploymentMechanism == Options::Debug && !deployToLocalTmp(options, QLatin1String("/lib/libgnustl_shared.so")))
  1410. // return false;
  1411. return true;
  1412. }
  1413. bool signPackage(const Options &options)
  1414. {
  1415. if (options.verbose)
  1416. fprintf(stdout, "Signing Tizen package.\n");
  1417. if (!QFile::exists(options.nativeSigningBinary)) {
  1418. fprintf(stderr, "Cannot find native-signing: %s.\n", qPrintable(options.nativeSigningBinary));
  1419. return false;
  1420. }
  1421. QString nativeSigningTool = QString::fromLatin1("%1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12")
  1422. .arg(shellQuote(options.nativeSigningBinary))
  1423. .arg(shellQuote(options.outputDirectory))
  1424. .arg(shellQuote(options.tizenDeveloperCaCerFile))
  1425. .arg(shellQuote(options.authorKey))
  1426. .arg(shellQuote(options.authorPass))
  1427. .arg(shellQuote(options.tizenDistributorSignerFile))
  1428. .arg(shellQuote(options.tizenDistributorSignerPassword))
  1429. .arg(shellQuote(options.tizenDistributorCaCerFile))
  1430. .arg(shellQuote(options.tizenDistributor2SignerFile))
  1431. .arg(shellQuote(options.tizenDistributor2SignerPassword))
  1432. .arg(shellQuote(options.tizenDistributor2CaCerFile))
  1433. .arg(shellQuote(options.tizenDistributor2RootPath));
  1434. QStringList tpkFilesToRemove = QDir(options.outputDirectory).entryList(QStringList() << "*.tpk");
  1435. tpkFilesToRemove << tpkPath(options);
  1436. foreach (QString tpkFile, tpkFilesToRemove)
  1437. QFile::remove(tpkFile);
  1438. if (QFile::exists(tpkPath(options)) && !QFile::remove(tpkPath(options)))
  1439. return false;
  1440. QString qtConfFileName = options.outputDirectory+"bin/qt.conf";
  1441. if(options.deploymentMechanism == Options::Debug) {
  1442. QFile qtConfFile(qtConfFileName);
  1443. if (!qtConfFile.open(QIODevice::WriteOnly)) {
  1444. fprintf(stderr, "Cannot open %s for write.\n", qPrintable(qtConfFile.fileName()));
  1445. return false;
  1446. }
  1447. qtConfFile.write(QByteArray("[Paths]\nPrefix = /tmp/qt"));
  1448. } else if (options.deploymentMechanism == Options::Bundled) {
  1449. QFile qtConfFile(qtConfFileName);
  1450. if (!qtConfFile.open(QIODevice::WriteOnly)) {
  1451. fprintf(stderr, "Cannot open %s for write.\n", qPrintable(qtConfFile.fileName()));
  1452. return false;
  1453. }
  1454. qtConfFile.write(QByteArray("[Paths]\nPrefix = ../lib/qt"));
  1455. }
  1456. if (options.verbose)
  1457. fprintf(stdout, "Running: %s\n", qPrintable(nativeSigningTool));
  1458. FILE *nativeSigningCommand = openProcess(nativeSigningTool);
  1459. if (nativeSigningCommand == 0) {
  1460. fprintf(stderr, "Couldn't run native-signing.\n");
  1461. if (options.deploymentMechanism == Options::Debug)
  1462. QFile::remove(qtConfFileName);
  1463. return false;
  1464. }
  1465. if (options.verbose) {
  1466. char buffer[512];
  1467. while (fgets(buffer, sizeof(buffer), nativeSigningCommand) != 0)
  1468. fprintf(stdout, "%s", buffer);
  1469. }
  1470. int errorCode = pclose(nativeSigningCommand);
  1471. if (errorCode != 0) {
  1472. fprintf(stderr, "native signing command failed.\n");
  1473. if (!options.verbose)
  1474. fprintf(stderr, " -- Run with --verbose for more information.\n");
  1475. return false;
  1476. }
  1477. QString zipTool = "zip";
  1478. #if defined(Q_OS_WIN32)
  1479. zipTool += QLatin1String(".exe");
  1480. #endif
  1481. zipTool = QStandardPaths::findExecutable(zipTool);
  1482. if (zipTool.isEmpty()) {
  1483. fprintf(stderr, "zip tool not found: %s\n", qPrintable(zipTool));
  1484. return false;
  1485. }
  1486. QDir::setCurrent(options.outputDirectory);
  1487. zipTool = QString::fromLatin1("%1%2 -r %3 .")
  1488. .arg(shellQuote(zipTool))
  1489. .arg(options.verbose ? QString::fromLatin1(" -v") : QString())
  1490. .arg(tpkPath(options));
  1491. if (options.verbose)
  1492. fprintf(stdout, "Running:%s\n", qPrintable(zipTool));
  1493. FILE *zipToolCommand = openProcess(zipTool);
  1494. if (zipToolCommand == 0) {
  1495. fprintf(stderr, "Couldn't run zip.\n");
  1496. return false;
  1497. }
  1498. char buffer[512];
  1499. while (fgets(buffer, sizeof(buffer), zipToolCommand) != 0)
  1500. fprintf(stdout, "%s", buffer);
  1501. errorCode = pclose(zipToolCommand);
  1502. if (errorCode != 0) {
  1503. fprintf(stderr, "zip command failed.\n");
  1504. if (!options.verbose)
  1505. fprintf(stderr, " -- Run with --verbose for more information.\n");
  1506. return false;
  1507. }
  1508. return true;
  1509. }
  1510. bool copyGdbServer(const Options &options)
  1511. {
  1512. if (options.verbose)
  1513. fprintf(stdout, "Copying gdbserver into package.\n");
  1514. // QString architectureSubDirectory;
  1515. // if (options.architecture.startsWith(QLatin1String("arm")))
  1516. // architectureSubDirectory = QLatin1String("android-arm");
  1517. // else
  1518. // architectureSubDirectory = QLatin1String("android-") + options.architecture;
  1519. // QString gdbServerBinary = options.ndkPath
  1520. // + QLatin1String("/prebuilt/")
  1521. // + architectureSubDirectory
  1522. // + QLatin1String("/gdbserver/gdbserver");
  1523. // if (!QFile::exists(gdbServerBinary)) {
  1524. // fprintf(stderr, "Cannot find gdbserver at %s.\n", qPrintable(gdbServerBinary));
  1525. // return false;
  1526. // }
  1527. // QString gdbServerTarget = options.outputDirectory + QLatin1String("/libs/") + options.architecture;
  1528. // if (!copyFileIfNewer(gdbServerBinary,
  1529. // gdbServerTarget + QLatin1String("/gdbserver"),
  1530. // options.verbose)
  1531. // || !copyFileIfNewer(gdbServerBinary,
  1532. // gdbServerTarget + QLatin1String("/libgdbserver.so"),
  1533. // options.verbose)) {
  1534. // return false;
  1535. // }
  1536. return true;
  1537. }
  1538. bool deployAllToLocalTmp(const Options &options)
  1539. {
  1540. FILE *sdbCommand = runSdb(options,
  1541. QString::fromLatin1(" push %1 /tmp/qt")
  1542. .arg(shellQuote(options.temporaryDirectoryName)));
  1543. if (sdbCommand == 0)
  1544. return false;
  1545. if (options.verbose) {
  1546. fprintf(stdout, " -- Deploying Qt files to device.\n");
  1547. char buffer[512];
  1548. while (fgets(buffer, sizeof(buffer), sdbCommand) != 0)
  1549. fprintf(stdout, "%s", buffer);
  1550. }
  1551. int errorCode = pclose(sdbCommand);
  1552. if (errorCode != 0) {
  1553. fprintf(stderr, "Copying files to device failed!\n");
  1554. return false;
  1555. }
  1556. // sdbCommand = runSdb(options,
  1557. // QString::fromLatin1("shell 'cd %1; for file in `ls *.so`; do ln -sf $file $file.5; done'")
  1558. // .arg("/tmp/qt/lib"));
  1559. // if (sdbCommand == 0)
  1560. // return false;
  1561. // if (options.verbose) {
  1562. // fprintf(stdout, " -- Creating necessary symbolic links to Qt libraries on device.\n");
  1563. // char buffer[512];
  1564. // while (fgets(buffer, sizeof(buffer), sdbCommand) != 0)
  1565. // fprintf(stdout, "%s", buffer);
  1566. // }
  1567. // errorCode = pclose(sdbCommand);
  1568. // if (errorCode != 0) {
  1569. // fprintf(stderr, "Creating necessary symbolic links to Qt libraries on device failed!\n");
  1570. // return false;
  1571. // }
  1572. return true;
  1573. }
  1574. enum ErrorCode
  1575. {
  1576. Success,
  1577. SyntaxErrorOrHelpRequested = 1,
  1578. CannotReadDependencies,
  1579. CannotCopyGnuStl,
  1580. CannotCopyQtFiles,
  1581. CannotFindApplicationBinary,
  1582. CannotCopyGdbServer,
  1583. CannotStripLibraries,
  1584. CannotSignPackage,
  1585. CannotInstallTpk,
  1586. CannotDeployAllToLocalTmp
  1587. };
  1588. int main(int argc, char *argv[])
  1589. {
  1590. QCoreApplication a(argc, argv);
  1591. Options options = parseOptions();
  1592. if (options.helpRequested || options.outputDirectory.isEmpty()) {
  1593. printHelp();
  1594. return SyntaxErrorOrHelpRequested;
  1595. }
  1596. options.timer.start();
  1597. if (Q_UNLIKELY(options.timing))
  1598. fprintf(stdout, "[TIMING] %d ms: Read input file\n", options.timer.elapsed());
  1599. fprintf(stdout,
  1600. // "012345678901234567890123456789012345678901234567890123456789012345678901"
  1601. "Generating Tizen Package\n"
  1602. " Output directory: %s\n"
  1603. " Application binary: %s\n"
  1604. " Tizen build platform: %s\n"
  1605. " Install to device: %s\n",
  1606. qPrintable(options.outputDirectory),
  1607. qPrintable(options.applicationBinary),
  1608. qPrintable(options.tizenPlatform),
  1609. options.installTpk
  1610. ? (options.installLocation.isEmpty() ? "Default device" : qPrintable(options.installLocation))
  1611. : "No"
  1612. );
  1613. if (!readDependencies(&options))
  1614. return CannotReadDependencies;
  1615. if (Q_UNLIKELY(options.timing))
  1616. fprintf(stdout, "[TIMING] %d ms: Read dependencies\n", options.timer.elapsed());
  1617. if (options.deploymentMechanism != Options::Target && !copyGnuStl(&options))
  1618. return CannotCopyGnuStl;
  1619. if (Q_UNLIKELY(options.timing))
  1620. fprintf(stdout, "[TIMING] %d ms: Copied GNU STL\n", options.timer.elapsed());
  1621. if (!copyQtFiles(&options))
  1622. return CannotCopyQtFiles;
  1623. if (options.installTpk && options.deploymentMechanism == Options::Debug && !deployAllToLocalTmp(options))
  1624. return CannotDeployAllToLocalTmp;
  1625. if (options.build) {
  1626. if (Q_UNLIKELY(options.timing))
  1627. fprintf(stdout, "[TIMING] %d ms: Copied Qt files\n", options.timer.elapsed());
  1628. if (!containsApplicationBinary(options))
  1629. return CannotFindApplicationBinary;
  1630. if (Q_UNLIKELY(options.timing))
  1631. fprintf(stdout, "[TIMING] %d ms: Checked for application binary\n", options.timer.elapsed());
  1632. if (!options.releasePackage && !copyGdbServer(options))
  1633. return CannotCopyGdbServer;
  1634. if (Q_UNLIKELY(options.timing))
  1635. fprintf(stdout, "[TIMING] %d ms: Copied GDB server\n", options.timer.elapsed());
  1636. if (!stripLibraries(options))
  1637. return CannotStripLibraries;
  1638. if (Q_UNLIKELY(options.timing))
  1639. fprintf(stdout, "[TIMING] %d ms: Stripped libraries\n", options.timer.elapsed());
  1640. if (!options.authorKey.isEmpty() && !signPackage(options))
  1641. return CannotSignPackage;
  1642. if (Q_UNLIKELY(options.timing))
  1643. fprintf(stdout, "[TIMING] %d ms: Signed package\n", options.timer.elapsed());
  1644. }
  1645. if (options.installTpk && !installTpk(options))
  1646. return CannotInstallTpk;
  1647. if (Q_UNLIKELY(options.timing))
  1648. fprintf(stdout, "[TIMING] %d ms: Installed APK\n", options.timer.elapsed());
  1649. fprintf(stdout, "Tizen package built successfully in %.3f ms.\n", options.timer.elapsed() / 1000.);
  1650. if (options.installTpk)
  1651. fprintf(stdout, " -- It can now be run from the selected device/emulator.\n");
  1652. fprintf(stdout, " -- File: %s\n", qPrintable(tpkPath(options)));
  1653. fflush(stdout);
  1654. return 0;
  1655. }