androidtoolchain.cpp 16.1 KB
Newer Older
BogDan Vatra's avatar
BogDan Vatra committed
1
2
/**************************************************************************
**
Eike Ziller's avatar
Eike Ziller committed
3
4
** Copyright (C) 2015 BogDan Vatra <bog_dan_ro@yahoo.com>
** Contact: http://www.qt.io/licensing
BogDan Vatra's avatar
BogDan Vatra committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
BogDan Vatra's avatar
BogDan Vatra committed
7
**
hjk's avatar
hjk committed
8
9
10
11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
Eike Ziller's avatar
Eike Ziller committed
12
13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
BogDan Vatra's avatar
BogDan Vatra committed
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25
26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
BogDan Vatra's avatar
BogDan Vatra committed
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
BogDan Vatra's avatar
BogDan Vatra committed
30
31
32
33
34

#include "androidtoolchain.h"
#include "androidconstants.h"
#include "androidconfigurations.h"
#include "androidqtversion.h"
BogDan Vatra's avatar
BogDan Vatra committed
35

36
37
#include <extensionsystem/pluginmanager.h>

BogDan Vatra's avatar
BogDan Vatra committed
38
39
40
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionmanager.h>

41
#include <projectexplorer/target.h>
BogDan Vatra's avatar
BogDan Vatra committed
42
43
44
#include <projectexplorer/toolchainmanager.h>
#include <projectexplorer/projectexplorer.h>

45
#include <utils/algorithm.h>
BogDan Vatra's avatar
BogDan Vatra committed
46
#include <utils/environment.h>
47
#include <utils/hostosinfo.h>
BogDan Vatra's avatar
BogDan Vatra committed
48
49

#include <QDir>
50
#include <QDirIterator>
51
#include <QFormLayout>
BogDan Vatra's avatar
BogDan Vatra committed
52
53
54
#include <QLabel>
#include <QVBoxLayout>

55
56
57
58
namespace {
    const QLatin1String NDKGccVersionRegExp("-\\d[\\.\\d]+");
}

BogDan Vatra's avatar
BogDan Vatra committed
59
60
61
namespace Android {
namespace Internal {

hjk's avatar
hjk committed
62
using namespace ProjectExplorer;
63
using namespace Utils;
BogDan Vatra's avatar
BogDan Vatra committed
64
65

static const char ANDROID_QT_VERSION_KEY[] = "Qt4ProjectManager.Android.QtVersion";
66
static const char ANDROID_NDK_TC_VERION[] = "Qt4ProjectManager.Android.NDK_TC_VERION";
BogDan Vatra's avatar
BogDan Vatra committed
67

Daniel Teske's avatar
Daniel Teske committed
68
QHash<Abi, QList<int> > AndroidToolChainFactory::m_newestVersionForAbi;
69
FileName AndroidToolChainFactory::m_ndkLocation;
70

Daniel Teske's avatar
Daniel Teske committed
71
AndroidToolChain::AndroidToolChain(const Abi &abi, const QString &ndkToolChainVersion, Detection d)
72
    : GccToolChain(Constants::ANDROID_TOOLCHAIN_ID, d),
73
      m_ndkToolChainVersion(ndkToolChainVersion), m_secondaryToolChain(false)
74
75
76
{
    setTargetAbi(abi);
    setDisplayName(QString::fromLatin1("Android GCC (%1-%2)")
Daniel Teske's avatar
Daniel Teske committed
77
                   .arg(AndroidConfig::displayName(targetAbi()))
78
79
                   .arg(ndkToolChainVersion));
}
BogDan Vatra's avatar
BogDan Vatra committed
80

81
82
// for fromMap
AndroidToolChain::AndroidToolChain()
83
    : GccToolChain(Constants::ANDROID_TOOLCHAIN_ID, ToolChain::ManualDetection),
84
      m_secondaryToolChain(false)
85
86
{
}
BogDan Vatra's avatar
BogDan Vatra committed
87
88

AndroidToolChain::AndroidToolChain(const AndroidToolChain &tc) :
89
90
    GccToolChain(tc), m_ndkToolChainVersion(tc.m_ndkToolChainVersion),
    m_secondaryToolChain(tc.m_secondaryToolChain)
BogDan Vatra's avatar
BogDan Vatra committed
91
92
93
94
95
96
97
98
99
100
101
102
{ }

AndroidToolChain::~AndroidToolChain()
{ }

QString AndroidToolChain::typeDisplayName() const
{
    return AndroidToolChainFactory::tr("Android GCC");
}

bool AndroidToolChain::isValid() const
{
103
    return GccToolChain::isValid() && targetAbi().isValid() && !m_ndkToolChainVersion.isEmpty()
Daniel Teske's avatar
Daniel Teske committed
104
            && compilerCommand().isChildOf(AndroidConfigurations::currentConfig().ndkLocation());
BogDan Vatra's avatar
BogDan Vatra committed
105
106
}

107
void AndroidToolChain::addToEnvironment(Environment &env) const
BogDan Vatra's avatar
BogDan Vatra committed
108
109
110
111
112
{

// TODO this vars should be configurable in projects -> build tab
// TODO invalidate all .pro files !!!

Daniel Teske's avatar
Daniel Teske committed
113
    env.set(QLatin1String("ANDROID_NDK_HOST"), AndroidConfigurations::currentConfig().toolchainHost());
Daniel Teske's avatar
Daniel Teske committed
114
115
    env.set(QLatin1String("ANDROID_NDK_TOOLCHAIN_PREFIX"), AndroidConfig::toolchainPrefix(targetAbi()));
    env.set(QLatin1String("ANDROID_NDK_TOOLS_PREFIX"), AndroidConfig::toolsPrefix(targetAbi()));
116
    env.set(QLatin1String("ANDROID_NDK_TOOLCHAIN_VERSION"), m_ndkToolChainVersion);
Daniel Teske's avatar
Daniel Teske committed
117
    QString javaHome = AndroidConfigurations::currentConfig().openJDKLocation().toString();
118
    if (!javaHome.isEmpty() && QFileInfo::exists(javaHome))
119
        env.set(QLatin1String("JAVA_HOME"), javaHome);
Daniel Teske's avatar
Daniel Teske committed
120
121
    env.set(QLatin1String("ANDROID_HOME"), AndroidConfigurations::currentConfig().sdkLocation().toString());
    env.set(QLatin1String("ANDROID_SDK_ROOT"), AndroidConfigurations::currentConfig().sdkLocation().toString());
BogDan Vatra's avatar
BogDan Vatra committed
122
123
}

hjk's avatar
hjk committed
124
bool AndroidToolChain::operator ==(const ToolChain &tc) const
BogDan Vatra's avatar
BogDan Vatra committed
125
{
126
    if (!GccToolChain::operator ==(tc))
BogDan Vatra's avatar
BogDan Vatra committed
127
128
        return false;

129
    return m_ndkToolChainVersion == static_cast<const AndroidToolChain &>(tc).m_ndkToolChainVersion;
BogDan Vatra's avatar
BogDan Vatra committed
130
131
}

hjk's avatar
hjk committed
132
ToolChainConfigWidget *AndroidToolChain::configurationWidget()
BogDan Vatra's avatar
BogDan Vatra committed
133
134
135
136
{
    return new AndroidToolChainConfigWidget(this);
}

137
138
FileName AndroidToolChain::suggestedDebugger() const
{
Daniel Teske's avatar
Daniel Teske committed
139
    return AndroidConfigurations::currentConfig().gdbPath(targetAbi(), m_ndkToolChainVersion);
140
141
142
143
}

FileName AndroidToolChain::suggestedGdbServer() const
{
144
    FileName path = AndroidConfigurations::currentConfig().ndkLocation();
145
146
    path.appendPath(QString::fromLatin1("prebuilt/android-%1/gdbserver/gdbserver")
                    .arg(Abi::toString(targetAbi().architecture())));
hjk's avatar
hjk committed
147
    if (path.exists())
148
        return path;
Daniel Teske's avatar
Daniel Teske committed
149
    path = AndroidConfigurations::currentConfig().ndkLocation();
150
    path.appendPath(QString::fromLatin1("toolchains/%1-%2/prebuilt/gdbserver")
Daniel Teske's avatar
Daniel Teske committed
151
                               .arg(AndroidConfig::toolchainPrefix(targetAbi()))
152
                               .arg(m_ndkToolChainVersion));
hjk's avatar
hjk committed
153
    if (path.exists())
154
155
        return path;

156
    return FileName();
157
158
}

BogDan Vatra's avatar
BogDan Vatra committed
159
160
161
QVariantMap AndroidToolChain::toMap() const
{
    QVariantMap result = GccToolChain::toMap();
162
    result.insert(QLatin1String(ANDROID_NDK_TC_VERION), m_ndkToolChainVersion);
BogDan Vatra's avatar
BogDan Vatra committed
163
164
165
166
167
168
169
170
    return result;
}

bool AndroidToolChain::fromMap(const QVariantMap &data)
{
    if (!GccToolChain::fromMap(data))
        return false;

171
172
    if (data.contains(QLatin1String(ANDROID_QT_VERSION_KEY))) {
        QString command = compilerCommand().toString();
Daniel Teske's avatar
Daniel Teske committed
173
        QString ndkPath = AndroidConfigurations::currentConfig().ndkLocation().toString();
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
        if (!command.startsWith(ndkPath))
            return false;
        command = command.mid(ndkPath.length());
        if (!command.startsWith(QLatin1String("/toolchains/")))
            return false;
        command = command.mid(12);
        int index = command.indexOf(QLatin1Char('/'));
        if (index == -1)
            return false;
        command = command.left(index);
        QRegExp versionRegExp(NDKGccVersionRegExp);
        index = versionRegExp.indexIn(command);
        if (index == -1)
            return false;
        m_ndkToolChainVersion = command.mid(index + 1);
        QString platform = command.left(index);
Daniel Teske's avatar
Daniel Teske committed
190
        setTargetAbi(AndroidConfig::abiForToolChainPrefix(platform));
191
192
193
    } else {
        m_ndkToolChainVersion = data.value(QLatin1String(ANDROID_NDK_TC_VERION)).toString();
    }
BogDan Vatra's avatar
BogDan Vatra committed
194

Daniel Teske's avatar
Daniel Teske committed
195
    Abi abi = targetAbi();
196
    m_secondaryToolChain = AndroidToolChainFactory::versionCompareLess(AndroidToolChainFactory::versionNumberFromString(m_ndkToolChainVersion),
Daniel Teske's avatar
Daniel Teske committed
197
                                                                       AndroidToolChainFactory::newestToolChainVersionForArch(abi));
BogDan Vatra's avatar
BogDan Vatra committed
198
199
200
    return isValid();
}

201
QList<FileName> AndroidToolChain::suggestedMkspecList() const
BogDan Vatra's avatar
BogDan Vatra committed
202
{
203
    return QList<FileName>()<< FileName::fromLatin1("android-g++");
BogDan Vatra's avatar
BogDan Vatra committed
204
205
}

206
QString AndroidToolChain::makeCommand(const Environment &env) const
BogDan Vatra's avatar
BogDan Vatra committed
207
{
Daniel Teske's avatar
Daniel Teske committed
208
    QStringList extraDirectories = AndroidConfigurations::currentConfig().makeExtraSearchDirectories();
209
    if (HostOsInfo::isWindowsHost()) {
210
        FileName tmp = env.searchInPath(QLatin1String("ma-make.exe"), extraDirectories);
211
        if (!tmp.isEmpty())
212
            return QString();
213
        tmp = env.searchInPath(QLatin1String("mingw32-make"), extraDirectories);
214
        return tmp.isEmpty() ? QLatin1String("mingw32-make") : tmp.toString();
215
216
217
    }

    QString make = QLatin1String("make");
218
219
    FileName tmp = env.searchInPath(make, extraDirectories);
    return tmp.isEmpty() ? make : tmp.toString();
BogDan Vatra's avatar
BogDan Vatra committed
220
221
}

222
QString AndroidToolChain::ndkToolChainVersion() const
BogDan Vatra's avatar
BogDan Vatra committed
223
{
224
    return m_ndkToolChainVersion;
BogDan Vatra's avatar
BogDan Vatra committed
225
226
}

227
bool AndroidToolChain::isSecondaryToolChain() const
228
229
230
231
232
233
234
235
236
{
    return m_secondaryToolChain;
}

void AndroidToolChain::setSecondaryToolChain(bool b)
{
    m_secondaryToolChain = b;
}

hjk's avatar
hjk committed
237
QList<Abi> AndroidToolChain::detectSupportedAbis() const
BogDan Vatra's avatar
BogDan Vatra committed
238
{
239
    return QList<Abi>() << targetAbi();
BogDan Vatra's avatar
BogDan Vatra committed
240
241
242
243
244
245
246
}

// --------------------------------------------------------------------------
// ToolChainConfigWidget
// --------------------------------------------------------------------------

AndroidToolChainConfigWidget::AndroidToolChainConfigWidget(AndroidToolChain *tc) :
hjk's avatar
hjk committed
247
   ToolChainConfigWidget(tc)
BogDan Vatra's avatar
BogDan Vatra committed
248
{
Daniel Teske's avatar
Daniel Teske committed
249
    QLabel *label = new QLabel(AndroidConfigurations::currentConfig().ndkLocation().toUserOutput());
250
    m_mainLayout->addRow(tr("NDK Root:"), label);
BogDan Vatra's avatar
BogDan Vatra committed
251
252
253
254
255
256
}

// --------------------------------------------------------------------------
// ToolChainFactory
// --------------------------------------------------------------------------

257
AndroidToolChainFactory::AndroidToolChainFactory()
BogDan Vatra's avatar
BogDan Vatra committed
258
{
259
    setDisplayName(tr("Android GCC"));
BogDan Vatra's avatar
BogDan Vatra committed
260
261
}

262
QList<ToolChain *> AndroidToolChainFactory::autoDetect(const QList<ToolChain *> &alreadyKnown)
BogDan Vatra's avatar
BogDan Vatra committed
263
{
264
    return autodetectToolChainsForNdk(AndroidConfigurations::currentConfig().ndkLocation(), alreadyKnown);
BogDan Vatra's avatar
BogDan Vatra committed
265
266
267
268
}

bool AndroidToolChainFactory::canRestore(const QVariantMap &data)
{
269
    return typeIdFromMap(data) == Constants::ANDROID_TOOLCHAIN_ID;
BogDan Vatra's avatar
BogDan Vatra committed
270
271
}

hjk's avatar
hjk committed
272
ToolChain *AndroidToolChainFactory::restore(const QVariantMap &data)
BogDan Vatra's avatar
BogDan Vatra committed
273
{
274
    AndroidToolChain *tc = new AndroidToolChain();
BogDan Vatra's avatar
BogDan Vatra committed
275
276
277
278
279
280
281
    if (tc->fromMap(data))
        return tc;

    delete tc;
    return 0;
}

282
QList<AndroidToolChainFactory::AndroidToolChainInformation> AndroidToolChainFactory::toolchainPathsForNdk(const FileName &ndkPath)
Daniel Teske's avatar
Daniel Teske committed
283
284
285
286
287
288
289
290
291
{
    QList<AndroidToolChainInformation> result;
    if (ndkPath.isEmpty())
        return result;
    QRegExp versionRegExp(NDKGccVersionRegExp);
    FileName path = ndkPath;
    QDirIterator it(path.appendPath(QLatin1String("toolchains")).toString(),
                    QStringList() << QLatin1String("*"), QDir::Dirs);
    while (it.hasNext()) {
292
        const QString &fileName = FileName::fromString(it.next()).fileName();
Daniel Teske's avatar
Daniel Teske committed
293
294
295
296
297
298
        int idx = versionRegExp.indexIn(fileName);
        if (idx == -1)
            continue;
        AndroidToolChainInformation ati;
        ati.version = fileName.mid(idx + 1);
        QString platform = fileName.left(idx);
Daniel Teske's avatar
Daniel Teske committed
299
300
        ati.abi = AndroidConfig::abiForToolChainPrefix(platform);
        if (ati.abi.architecture() == Abi::UnknownArchitecture) // e.g. mipsel which is not yet supported
Daniel Teske's avatar
Daniel Teske committed
301
302
            continue;
        // AndroidToolChain *tc = new AndroidToolChain(arch, version, true);
Daniel Teske's avatar
Daniel Teske committed
303
        ati.compilerCommand = AndroidConfigurations::currentConfig().gccPath(ati.abi, ati.version);
Daniel Teske's avatar
Daniel Teske committed
304
305
306
307
308
309
        // tc->setCompilerCommand(compilerPath);
        result.append(ati);
    }
    return result;
}

310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
QList<int> AndroidToolChainFactory::versionNumberFromString(const QString &version)
{
    QList<int> result;
    int start = 0;
    int end = version.length();
    while (start <= end) {
        int index = version.indexOf(QLatin1Char('.'), start);
        if (index == -1)
            index = end;

        bool ok;
        int v = version.mid(start, index - start).toInt(&ok);
        if (!ok) // unparseable, return what we have
            return result;

        result << v;
        start = index + 1;
    }
    return result;
}

bool AndroidToolChainFactory::versionCompareLess(const QList<int> &a, const QList<int> &b)
{
    int aend = a.length();
    int bend = b.length();
    int end = qMax(aend, bend);
    for (int i = 0; i < end; ++i) {
        int an = i < aend ? a.at(i) : 0;
        int bn = i < bend ? b.at(i) : 0;
        if (an < bn)
            return true;
        if (bn < an)
            return false;
    }
    return false;
}

bool AndroidToolChainFactory::versionCompareLess(AndroidToolChain *atc, AndroidToolChain *btc)
{
    QList<int> a = versionNumberFromString(atc->ndkToolChainVersion());
    QList<int> b = versionNumberFromString(btc->ndkToolChainVersion());

    return versionCompareLess(a, b);
}

355
356
357
358
359
360
361
362
363
364
365
366
static AndroidToolChain *findToolChain(Utils::FileName &compilerPath, const QList<ToolChain *> &alreadyKnown)
{
    return static_cast<AndroidToolChain *>(
                Utils::findOrDefault(alreadyKnown, [compilerPath](ToolChain *tc) {
                                                       return tc->compilerCommand() == compilerPath
                                                           && tc->typeId() == Constants::ANDROID_TOOLCHAIN_ID;
                                                   }));
}

QList<ToolChain *>
AndroidToolChainFactory::autodetectToolChainsForNdk(const FileName &ndkPath,
                                                    const QList<ToolChain *> &alreadyKnown)
BogDan Vatra's avatar
BogDan Vatra committed
367
{
hjk's avatar
hjk committed
368
    QList<ToolChain *> result;
369
370
    if (ndkPath.isEmpty())
        return result;
371

372
373
374
375
    QRegExp versionRegExp(NDKGccVersionRegExp);
    FileName path = ndkPath;
    QDirIterator it(path.appendPath(QLatin1String("toolchains")).toString(),
                    QStringList() << QLatin1String("*"), QDir::Dirs);
Daniel Teske's avatar
Daniel Teske committed
376
    QHash<Abi, AndroidToolChain *> newestToolChainForArch;
377

378
    while (it.hasNext()) {
379
        const QString &fileName = FileName::fromString(it.next()).fileName();
380
381
        int idx = versionRegExp.indexIn(fileName);
        if (idx == -1)
BogDan Vatra's avatar
BogDan Vatra committed
382
            continue;
383
384
        QString version = fileName.mid(idx + 1);
        QString platform = fileName.left(idx);
Daniel Teske's avatar
Daniel Teske committed
385
386
        Abi abi = AndroidConfig::abiForToolChainPrefix(platform);
        if (abi.architecture() == Abi::UnknownArchitecture) // e.g. mipsel which is not yet supported
387
            continue;
Daniel Teske's avatar
Daniel Teske committed
388
        FileName compilerPath = AndroidConfigurations::currentConfig().gccPath(abi, version);
389
390
391
392
393
394

        AndroidToolChain *tc = findToolChain(compilerPath, alreadyKnown);
        if (!tc) {
            tc = new AndroidToolChain(abi, version, ToolChain::AutoDetection);
            tc->resetToolChain(compilerPath);
        }
395
        result.append(tc);
396

397
        auto it = newestToolChainForArch.constFind(abi);
398
        if (it == newestToolChainForArch.constEnd())
Daniel Teske's avatar
Daniel Teske committed
399
            newestToolChainForArch.insert(abi, tc);
400
        else if (versionCompareLess(it.value(), tc))
Daniel Teske's avatar
Daniel Teske committed
401
            newestToolChainForArch[abi] = tc;
BogDan Vatra's avatar
BogDan Vatra committed
402
    }
403
404
405

    foreach (ToolChain *tc, result) {
        AndroidToolChain *atc = static_cast<AndroidToolChain *>(tc);
406
        atc->setSecondaryToolChain(newestToolChainForArch.value(atc->targetAbi()) != atc);
407
408
    }

BogDan Vatra's avatar
BogDan Vatra committed
409
410
411
    return result;
}

Daniel Teske's avatar
Daniel Teske committed
412
QList<int> AndroidToolChainFactory::newestToolChainVersionForArch(const Abi &abi)
413
{
Daniel Teske's avatar
Daniel Teske committed
414
    if (m_newestVersionForAbi.isEmpty()
Daniel Teske's avatar
Daniel Teske committed
415
            || m_ndkLocation != AndroidConfigurations::currentConfig().ndkLocation()) {
416
        QRegExp versionRegExp(NDKGccVersionRegExp);
Daniel Teske's avatar
Daniel Teske committed
417
        m_ndkLocation = AndroidConfigurations::currentConfig().ndkLocation();
418
419
420
421
        FileName path = m_ndkLocation;
        QDirIterator it(path.appendPath(QLatin1String("toolchains")).toString(),
                        QStringList() << QLatin1String("*"), QDir::Dirs);
        while (it.hasNext()) {
422
            const QString &fileName = FileName::fromString(it.next()).fileName();
423
424
425
426
427
            int idx = versionRegExp.indexIn(fileName);
            if (idx == -1)
                continue;
            QList<int> version = versionNumberFromString(fileName.mid(idx + 1));
            QString platform = fileName.left(idx);
Daniel Teske's avatar
Daniel Teske committed
428
429
            Abi abi = AndroidConfig::abiForToolChainPrefix(platform);
            if (abi.architecture() == Abi::UnknownArchitecture) // e.g. mipsel which is not yet supported
430
                continue;
Daniel Teske's avatar
Daniel Teske committed
431
432
433
434
            QHash<Abi, QList<int> >::const_iterator it
                    = m_newestVersionForAbi.constFind(abi);
            if (it == m_newestVersionForAbi.constEnd())
                m_newestVersionForAbi.insert(abi, version);
435
            else if (versionCompareLess(it.value(), version))
Daniel Teske's avatar
Daniel Teske committed
436
                m_newestVersionForAbi[abi] = version;
437
438
        }
    }
Daniel Teske's avatar
Daniel Teske committed
439
    return m_newestVersionForAbi.value(abi);
440
441
}

BogDan Vatra's avatar
BogDan Vatra committed
442
443
} // namespace Internal
} // namespace Android