androidtoolchain.cpp 15.5 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
45
#include <projectexplorer/toolchainmanager.h>
#include <projectexplorer/projectexplorer.h>

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

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

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

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

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

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

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

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

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

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

AndroidToolChain::~AndroidToolChain()
{ }

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

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

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

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

Daniel Teske's avatar
Daniel Teske committed
112
    env.set(QLatin1String("ANDROID_NDK_HOST"), AndroidConfigurations::currentConfig().toolchainHost());
Daniel Teske's avatar
Daniel Teske committed
113
114
    env.set(QLatin1String("ANDROID_NDK_TOOLCHAIN_PREFIX"), AndroidConfig::toolchainPrefix(targetAbi()));
    env.set(QLatin1String("ANDROID_NDK_TOOLS_PREFIX"), AndroidConfig::toolsPrefix(targetAbi()));
115
    env.set(QLatin1String("ANDROID_NDK_TOOLCHAIN_VERSION"), m_ndkToolChainVersion);
Daniel Teske's avatar
Daniel Teske committed
116
    QString javaHome = AndroidConfigurations::currentConfig().openJDKLocation().toString();
117
    if (!javaHome.isEmpty() && QFileInfo::exists(javaHome))
118
        env.set(QLatin1String("JAVA_HOME"), javaHome);
Daniel Teske's avatar
Daniel Teske committed
119
120
    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
121
122
}

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

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

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

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

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

155
    return FileName();
156
157
}

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

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

170
171
    if (data.contains(QLatin1String(ANDROID_QT_VERSION_KEY))) {
        QString command = compilerCommand().toString();
Daniel Teske's avatar
Daniel Teske committed
172
        QString ndkPath = AndroidConfigurations::currentConfig().ndkLocation().toString();
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
        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
189
        setTargetAbi(AndroidConfig::abiForToolChainPrefix(platform));
190
191
192
    } else {
        m_ndkToolChainVersion = data.value(QLatin1String(ANDROID_NDK_TC_VERION)).toString();
    }
BogDan Vatra's avatar
BogDan Vatra committed
193

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

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

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

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

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

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

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

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

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

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

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

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

hjk's avatar
hjk committed
262
QList<ToolChain *> AndroidToolChainFactory::autoDetect()
BogDan Vatra's avatar
BogDan Vatra committed
263
{
Daniel Teske's avatar
Daniel Teske committed
264
    return createToolChainsForNdk(AndroidConfigurations::currentConfig().ndkLocation());
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
QList<ToolChain *> AndroidToolChainFactory::createToolChainsForNdk(const FileName &ndkPath)
BogDan Vatra's avatar
BogDan Vatra committed
356
{
hjk's avatar
hjk committed
357
    QList<ToolChain *> result;
358
359
360
361
362
363
    if (ndkPath.isEmpty())
        return result;
    QRegExp versionRegExp(NDKGccVersionRegExp);
    FileName path = ndkPath;
    QDirIterator it(path.appendPath(QLatin1String("toolchains")).toString(),
                    QStringList() << QLatin1String("*"), QDir::Dirs);
Daniel Teske's avatar
Daniel Teske committed
364
    QHash<Abi, AndroidToolChain *> newestToolChainForArch;
365

366
    while (it.hasNext()) {
367
        const QString &fileName = FileName::fromString(it.next()).fileName();
368
369
        int idx = versionRegExp.indexIn(fileName);
        if (idx == -1)
BogDan Vatra's avatar
BogDan Vatra committed
370
            continue;
371
372
        QString version = fileName.mid(idx + 1);
        QString platform = fileName.left(idx);
Daniel Teske's avatar
Daniel Teske committed
373
374
        Abi abi = AndroidConfig::abiForToolChainPrefix(platform);
        if (abi.architecture() == Abi::UnknownArchitecture) // e.g. mipsel which is not yet supported
375
            continue;
Daniel Teske's avatar
Daniel Teske committed
376
377
        AndroidToolChain *tc = new AndroidToolChain(abi, version, ToolChain::AutoDetection);
        FileName compilerPath = AndroidConfigurations::currentConfig().gccPath(abi, version);
378
        tc->resetToolChain(compilerPath);
379
        result.append(tc);
380

Daniel Teske's avatar
Daniel Teske committed
381
382
        QHash<Abi, AndroidToolChain *>::const_iterator it
                = newestToolChainForArch.constFind(abi);
383
        if (it == newestToolChainForArch.constEnd())
Daniel Teske's avatar
Daniel Teske committed
384
            newestToolChainForArch.insert(abi, tc);
385
        else if (versionCompareLess(it.value(), tc))
Daniel Teske's avatar
Daniel Teske committed
386
            newestToolChainForArch[abi] = tc;
BogDan Vatra's avatar
BogDan Vatra committed
387
    }
388
389
390

    foreach (ToolChain *tc, result) {
        AndroidToolChain *atc = static_cast<AndroidToolChain *>(tc);
Daniel Teske's avatar
Daniel Teske committed
391
        if (newestToolChainForArch.value(atc->targetAbi()) != atc)
392
393
394
            atc->setSecondaryToolChain(true);
    }

BogDan Vatra's avatar
BogDan Vatra committed
395
396
397
    return result;
}

Daniel Teske's avatar
Daniel Teske committed
398
QList<int> AndroidToolChainFactory::newestToolChainVersionForArch(const Abi &abi)
399
{
Daniel Teske's avatar
Daniel Teske committed
400
    if (m_newestVersionForAbi.isEmpty()
Daniel Teske's avatar
Daniel Teske committed
401
            || m_ndkLocation != AndroidConfigurations::currentConfig().ndkLocation()) {
402
        QRegExp versionRegExp(NDKGccVersionRegExp);
Daniel Teske's avatar
Daniel Teske committed
403
        m_ndkLocation = AndroidConfigurations::currentConfig().ndkLocation();
404
405
406
407
        FileName path = m_ndkLocation;
        QDirIterator it(path.appendPath(QLatin1String("toolchains")).toString(),
                        QStringList() << QLatin1String("*"), QDir::Dirs);
        while (it.hasNext()) {
408
            const QString &fileName = FileName::fromString(it.next()).fileName();
409
410
411
412
413
            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
414
415
            Abi abi = AndroidConfig::abiForToolChainPrefix(platform);
            if (abi.architecture() == Abi::UnknownArchitecture) // e.g. mipsel which is not yet supported
416
                continue;
Daniel Teske's avatar
Daniel Teske committed
417
418
419
420
            QHash<Abi, QList<int> >::const_iterator it
                    = m_newestVersionForAbi.constFind(abi);
            if (it == m_newestVersionForAbi.constEnd())
                m_newestVersionForAbi.insert(abi, version);
421
            else if (versionCompareLess(it.value(), version))
Daniel Teske's avatar
Daniel Teske committed
422
                m_newestVersionForAbi[abi] = version;
423
424
        }
    }
Daniel Teske's avatar
Daniel Teske committed
425
    return m_newestVersionForAbi.value(abi);
426
427
}

BogDan Vatra's avatar
BogDan Vatra committed
428
429
} // namespace Internal
} // namespace Android