Compare commits

...

27 Commits

Author SHA1 Message Date
Mark Liversedge
8279b22f42 CI Fix macdeployqt AFTER it runs
.. LOL
2015-11-08 13:32:29 +00:00
Mark Liversedge
83f9d44fc2 Re-order includes main.cpp
.. why does it bork on brew qt 5.5.1
2015-11-08 12:44:58 +00:00
Mark Liversedge
f0ae40fbfa CI OSX Image xcode7.2b1 NOT xcode7.2 2015-11-08 12:13:30 +00:00
Mark Liversedge
02d51156cd CI Xcode 7.2 on El Cap with QT5.5
.. see if it helps !
2015-11-08 12:05:24 +00:00
Mark Liversedge
3c99576313 CI .py missing
.. from name of macdeployqtfix.py
2015-11-08 10:21:53 +00:00
Mark Liversedge
8837306bb6 CI add macdeployqtfix.py
.. see if it can help with the created bundle
2015-11-08 10:07:02 +00:00
gcoco
04c5a77d7e APP is found at /Volumes/GoldenCheetah
[ci skip]
2015-11-07 23:15:17 -05:00
Mark Liversedge
85437c8c3d CI Back to prior state, test ok
.. but binary created is broken
2015-11-07 23:34:58 +00:00
Mark Liversedge
15b5b596a0 CI Test remove main.cpp (!)
.. is there something weird about that source file (!?)

NOTE ** BUILD WILL NOW FAIL TO LINK     **
     ** WILL REVERT ONCE TEST COMPLETES **
2015-11-07 23:17:47 +00:00
Mark Liversedge
a07180b960 CI Try Brew QT551 with XCode 7.0
.. sadly running out of ideas.
2015-11-07 23:03:38 +00:00
Mark Liversedge
815e36e463 CI move main.cpp to fail early!
.. whilst debugging build issues.
2015-11-07 22:44:03 +00:00
Mark Liversedge
c49fb3acf3 CI brew update twice
.. in the XCode 6.4 image brew needs this fixup
   all a bit naff. See: https://github.com/Homebrew/homebrew/issues/42553
2015-11-07 22:10:09 +00:00
Mark Liversedge
f5232e5cb9 CI Try QT5.5.1 with XCode 6.4
.. which uses the 10.10 sdk
2015-11-07 21:53:38 +00:00
Mark Liversedge
5d6ba9d868 Revert "CI Another Test for QT5.5 and XCode 7.1"
This reverts commit c6a134adb3.

Giving up for now on QT5.5
2015-11-07 21:34:23 +00:00
Mark Liversedge
c6a134adb3 CI Another Test for QT5.5 and XCode 7.1
.. setting QMAKE_MAC_SDK when running qmake to see if it helps....
2015-11-07 20:48:48 +00:00
Mark Liversedge
56944bc09b CI Dropbox secrets
.. were moved to Secrets.h, no need to edit them
   out of comments in gcconfig.pri
2015-11-07 20:32:17 +00:00
Mark Liversedge
4ee0562f56 Merge pull request #1638 from dresco/ANT-FEC
ANT FE-C
2015-11-07 20:25:43 +00:00
Mark Liversedge
394591c805 Merge pull request #1636 from Joern-R/autobackup2
Autobackup
2015-11-07 20:25:33 +00:00
Mark Liversedge
d72ea80f15 Merge pull request #1626 from vlcvboyer/adjust_trig
adjust ANT null rpm trigger values
2015-11-07 20:25:26 +00:00
Mark Liversedge
ee6ff308be Revert "CI Test build with QT5.5 and XCode 7.1"
This reverts commit 2f9d74d759.

Will come back and look again at QT5.5 with QT7 but for
now lets just stick with the method that works (albeit the
binary created doesn't work!)
2015-11-07 20:23:03 +00:00
Mark Liversedge
2f9d74d759 CI Test build with QT5.5 and XCode 7.1
.. may need to fixup QMAKE_MAC_SDK too, but want to
   test building with QT5.5.1
2015-11-07 19:47:07 +00:00
Jon Escombe
07ef48ec06 ANT FE-C Add comments describing alternative slope mode support
If a trainer does not advertise simulation support, it could be
put into power mode, with GC calcuating the required load from
current speed/gradient etc.
2015-11-07 19:10:35 +00:00
Jon Escombe
4a33fd5f38 ANT: Avoid race condition when pairing
setChannelID() could be called with a null device_id. This may
result in pairing with an unexpected channel type if more than
one channel is available from that device_number.
2015-11-07 18:59:07 +00:00
Mark Liversedge
8290a19b47 Sintegral Qwt3D fixup for QT5.5.1 *only*
.. only need to fix when compiling on later release of QT
2015-11-07 18:47:29 +00:00
Mark Liversedge
653199b8ee Fix QT5.5.1 compile with Sintegral Qwt3D fork
.. isnan -> std::isnan via a macro
2015-11-07 18:27:17 +00:00
Joern
96c9d9c6af Autobackup
... additional information what "sequence" means
... fixing https://github.com/GoldenCheetah/GoldenCheetah/issues/1628
2015-11-07 15:10:53 +01:00
Vianney Boyer
8e81ed9c64 adjust ANT null rpm trigger values 2015-11-06 11:03:28 +01:00
8 changed files with 380 additions and 30 deletions

View File

@@ -1,5 +1,6 @@
branches:
- master
osx_image: xcode7.2b1 # will also get OSX El Capitan
language:
- objective-c
compiler:
@@ -7,8 +8,8 @@ compiler:
- gcc
env:
matrix:
## For now, but build with QT5
## - BRANCH=master QT=qt4 QT_PATH=qt
## Test 4.8 or 5.4.2
- BRANCH=master QT=qt4 QT_PATH=qt
- BRANCH=master QT=qt5 QT_PATH=qt5
global:
- secure: iqYW7f3//ZkMVzeCEarYn0S0DqKjFU9juBh0KF6WTlUsKX902Jtsk7dFoJlNDYBf63HLgV+wW2Hc6MxI9sGiUkom0gY9/To/aeGIJFGEX2sLm/e0Ok3qN521FA0Q/OiCFsD0RC6J+yrHxzI+rf8Z1rujceUsz2KgsrfAjYYv+BY=
@@ -20,7 +21,8 @@ env:
before_install:
- date
- brew update
- sh travis/install-qt.sh
##- sh travis/install-qt.sh
- brew install $QT
- brew install libical
- brew install libusb libusb-compat
- brew install srmio
@@ -77,7 +79,6 @@ before_script:
- sed -i "" "s|#\(SAMPLERATE_LIBS =\).*|\1 -L/usr/local/lib -lsamplerate|" src/gcconfig.pri
- sed -i "" "s|#\(DEFINES += GC_HAVE_LION*\)|\1|" src/gcconfig.pri
- sed -i "" "s|#\(HTPATH = ../httpserver.*\)|\1 |" src/gcconfig.pri
- sed -i "" "s|#\(DEFINES += GC_DROPBOX*\)|\1 |" src/gcconfig.pri
- sed -i "" "s|#\(DEFINES += GC_WANT_ROBOT.*\)|\1 |" src/gcconfig.pri
- sed -i "" "s|\(DEFINES += GC_VIDEO_NONE.*\)|#\1 |" src/gcconfig.pri
- sed -i "" "s|#\(DEFINES += GC_VIDEO_QUICKTIME.*\)|\1 |" src/gcconfig.pri
@@ -95,16 +96,15 @@ script:
- CC=clang CXX=clang++ make -j4 sub-src
after_success:
- cd src
#fix for libcocoa.dylib issue with qt5.4.2
- mkdir -p GoldenCheetah.app/Contents/MacOS/platforms
- cp /usr/local/opt/$QT_PATH/plugins/platforms/libqcocoa.dylib GoldenCheetah.app/Contents/MacOS/platforms/
- ls -laR GoldenCheetah.app
- sh ../travis/check-gc.sh
- /usr/local/opt/$QT_PATH/bin/macdeployqt GoldenCheetah.app -verbose=2 -dmg
## fix up the bundle with macdeployqtfix
- python ../travis/macdeployqtfix.py GoldenCheetah.app /usr/local/opt/$QT_PATH
- brew remove $QT
- mv GoldenCheetah.dmg GoldenCheetah_$QT.dmg
- hdiutil mount GoldenCheetah_$QT.dmg
- cd /Volumes/GoldenCheetah_$QT
- cd /Volumes/GoldenCheetah
- ls -laR GoldenCheetah.app
- GoldenCheetah.app/Contents/MacOS/GoldenCheetah --help

View File

@@ -340,6 +340,8 @@ ANT::setGradient(double gradient)
sendMessage(ANTMessage::fecSetTrackResistance(fecChannel, gradient, currentRollingResistance));
currentGradient = gradient;
// TODO : obtain acknowledge / confirm value using fecRequestCommandStatus
// TODO : if trainer does not have simulation capabilities, use power mode & let GC calculate
// the desired load based on gradient, wind, rolling resistance...
}
}

View File

@@ -586,17 +586,19 @@ void ANTChannel::broadcastEvent(unsigned char *ant_message)
case CHANNEL_TYPE_CADENCE:
{
float rpm;
static float last_measured_rpm;
uint16_t time = antMessage.crankMeasurementTime - lastMessage.crankMeasurementTime;
uint16_t revs = antMessage.crankRevolutions - lastMessage.crankRevolutions;
if (time) {
rpm = 1024*60*revs / time;
last_measured_rpm = rpm;
lastMessageTimestamp = QTime::currentTime();
} else {
int ms = lastMessageTimestamp.msecsTo(QTime::currentTime());
rpm = qMin((float)(1000.0*60.0*1.0) / ms, parent->getCadence());
// If we received a message but timestamp remain unchanged then we know that sensor have not detected magnet thus we deduct that rpm cannot be higher than this
if (rpm < (float) 4.0)
rpm = 0.0; // if rpm is less than 4 then we consider that we are stopped
if (rpm < last_measured_rpm / 2.0)
rpm = 0.0; // if rpm is less than half previous cadence we consider that we are stopped
}
parent->setCadence(rpm);
value2 = value = rpm;
@@ -607,11 +609,13 @@ void ANTChannel::broadcastEvent(unsigned char *ant_message)
case CHANNEL_TYPE_SandC:
{
float rpm;
static float last_measured_rpm;
// cadence first...
uint16_t time = antMessage.crankMeasurementTime - lastMessage.crankMeasurementTime;
uint16_t revs = antMessage.crankRevolutions - lastMessage.crankRevolutions;
if (time) {
rpm = 1024*60*revs / time;
last_measured_rpm = rpm;
if (is_moxy) /* do nothing for now */ ; //XXX fixme when moxy arrives XXX
else parent->setCadence(rpm);
@@ -620,8 +624,8 @@ void ANTChannel::broadcastEvent(unsigned char *ant_message)
int ms = lastMessageTimestamp.msecsTo(QTime::currentTime());
rpm = qMin((float)(1000.0*60.0*1.0) / ms, parent->getCadence());
// If we received a message but timestamp remain unchanged then we know that sensor have not detected magnet thus we deduct that rpm cannot be higher than this
if (rpm < (float) 4.0)
rpm = 0.0; // if rpm is less than 4 then we consider that we are stopped
if (rpm < last_measured_rpm / 2.0)
rpm = 0.0; // if rpm is less than half previous cadence we consider that we are stopped
parent->setCadence(rpm);
}
value = rpm;
@@ -638,8 +642,8 @@ void ANTChannel::broadcastEvent(unsigned char *ant_message)
int ms = lastMessageTimestamp2.msecsTo(QTime::currentTime());
rpm = qMin((float)(1000.0*60.0*1.0) / ms, parent->getWheelRpm());
// If we received a message but timestamp remain unchanged then we know that sensor have not detected magnet thus we deduct that rpm cannot be higher than this
if (rpm < (float) 4.0)
rpm = 0.0; // if rpm is less than 4 then we consider that we are stopped
if (rpm < (float) 15.0)
rpm = 0.0; // if rpm is less than 15rpm (=4s) then we consider that we are stopped
parent->setWheelRpm(rpm);
}
value2 = rpm;
@@ -659,8 +663,8 @@ void ANTChannel::broadcastEvent(unsigned char *ant_message)
int ms = lastMessageTimestamp.msecsTo(QTime::currentTime());
rpm = qMin((float)(1000.0*60.0*1.0) / ms, parent->getWheelRpm());
// If we received a message but timestamp remain unchanged then we know that sensor have not detected magnet thus we deduct that rpm cannot be higher than this
if (rpm < (float) 4.0)
rpm = 0.0; // if rpm is less than 4 then we consider that we are stopped
if (rpm < (float) 15.0)
rpm = 0.0; // if rpm is less than 15 (4s) then we consider that we are stopped
}
parent->setWheelRpm(rpm);
value2=value=rpm;
@@ -859,8 +863,10 @@ void ANTChannel::attemptTransition(int message_id)
const ant_sensor_type_t *st;
int previous_state=state;
st=&(parent->ant_sensor_types[channel_type]);
device_id=st->device_id;
setId();
//qDebug()<<number<<"type="<<channel_type<<"device type="<<device_id<<"freq="<<st->frequency;
qDebug()<<number<<"type="<<channel_type<<"device type="<<device_id<<"freq="<<st->frequency;
// update state
state=message_id;
@@ -883,11 +889,9 @@ void ANTChannel::attemptTransition(int message_id)
//qDebug()<<number<<"TRANSITION from unassigned";
qDebug()<<number<<"assign channel type RX";
// assign and set channel id all in one
parent->sendMessage(ANTMessage::assignChannel(number, CHANNEL_TYPE_RX, st->network)); // receive channel on network 1
device_id=st->device_id;
setId();
break;
case ANT_ASSIGN_CHANNEL:

View File

@@ -28,6 +28,12 @@
#include <QLabel>
#include "Context.h"
// sintegral fork of QwtPlot3D defines IS_NAN as isnan instead of std::isnan
// so we add a macro to fix that; this only applies when compiling on QT5.5.1
#if QT_VERSION >= 0x050501
#define isnan(x) std::isnan(x)
#endif
#include <qwt3d_global.h>
// qwtplot3d api changes between 0.2.x and 0.3.x

View File

@@ -1016,9 +1016,13 @@ AboutRiderPage::AboutRiderPage(QWidget *parent, Context *context) : QWidget(pare
autoBackupPeriod->setMinimum(0);
autoBackupPeriod->setMaximum(9999);
autoBackupPeriod->setSingleStep(1);
QLabel *autoBackupPeriodLabel = new QLabel(tr("Auto Backup Period"));
QLabel *autoBackupPeriodLabel = new QLabel(tr("Auto Backup execution every"));
autoBackupPeriod->setValue(appsettings->cvalue(context->athlete->cyclist, GC_AUTOBACKUP_PERIOD, 0).toInt());
QLabel *autoBackupUnitLabel = new QLabel(tr("times the athlete is closed - 0 means never"));
QHBoxLayout *backupInput = new QHBoxLayout();
backupInput->addWidget(autoBackupPeriod);
//backupInput->addStretch();
backupInput->addWidget(autoBackupUnitLabel);
Qt::Alignment alignment = Qt::AlignLeft|Qt::AlignVCenter;
@@ -1038,7 +1042,7 @@ AboutRiderPage::AboutRiderPage(QWidget *parent, Context *context) : QWidget(pare
grid->addWidget(autoBackupFolder, 7, 1, alignment);
grid->addWidget(autoBackupFolderBrowse, 7, 2, alignment);
grid->addWidget(autoBackupPeriodLabel, 8, 0,alignment);
grid->addWidget(autoBackupPeriod, 8, 1, alignment);
grid->addLayout(backupInput, 8, 1, alignment);
grid->addWidget(avatarButton, 0, 1, 4, 2, Qt::AlignRight|Qt::AlignVCenter);
all->addLayout(grid);

View File

@@ -16,23 +16,25 @@
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "Context.h"
#include "Athlete.h"
#include "MainWindow.h"
#include "Settings.h"
#include "TrainDB.h"
#include "Colors.h"
#include "GcUpgrade.h"
#include <QApplication>
#include <QtGui>
#include <QFile>
#include <QWebSettings>
#include <QMessageBox>
#include "ChooseCyclistDialog.h"
#include "MainWindow.h"
#include "Settings.h"
#include "TrainDB.h"
#include "Colors.h"
#ifdef GC_WANT_HTTP
#include "httplistener.h"
#include "httprequesthandler.h"
#endif
#include "GcUpgrade.h"
#include <signal.h>
// redirect errors to `home'/goldencheetah.log

View File

@@ -544,6 +544,7 @@ DEFERRES += RouteWindow.h \
RouteItem.cpp
SOURCES += \
main.cpp \
AboutDialog.cpp \
AddDeviceWizard.cpp \
AddIntervalDialog.cpp \
@@ -780,7 +781,6 @@ SOURCES += \
WorkoutWizard.cpp \
WPrime.cpp \
Zones.cpp \
main.cpp \
../qtsolutions/json/mvjson.cpp
RESOURCES = application.qrc \

332
travis/macdeployqtfix.py Normal file
View File

@@ -0,0 +1,332 @@
"""
finish the job started by macdeployqtfix
"""
from subprocess import Popen, PIPE
from string import Template
import os, sys
import logging
import argparse
import re
from collections import namedtuple
QTLIB_NAME_REGEX = r'^(?:@executable_path)?/.*/(Qt[a-zA-Z]*).framework/(?:Versions/\d/)?\1$'
QTPLUGIN_NAME_REGEX = r'^(?:@executable_path)?/.*/[pP]lug[iI]ns/(.*)/(.*).dylib$'
QTLIB_NORMALIZED = r'$prefix/Frameworks/$qtlib.framework/Versions/$qtversion/$qtlib'
QTPLUGIN_NORMALIZED = r'$prefix/PlugIns/$plugintype/$pluginname.dylib'
class GlobalConfig:
logger = None
qtpath = None
exepath = None
def run_and_get_output(popen_args):
"""
exec process and get all output
"""
process_output = namedtuple('ProcessOutput', ['stdout', 'stderr', 'retcode'])
try:
GlobalConfig.logger.debug('run_and_get_output({0})'.format(repr(popen_args)))
proc = Popen(popen_args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
stdout, stderr = proc.communicate(b'')
proc_out = process_output(stdout, stderr, proc.returncode)
GlobalConfig.logger.debug('\tprocess_output: {0}'.format(proc_out))
return proc_out
except Exception as exc:
GlobalConfig.logger.error('\texception: {0}'.format(exc))
return process_output('', exc.message, -1)
def get_dependencies(filename):
"""
input: filename fullpath
should call otool on mac and returns the list of dependencies,
unsorted, unmodified, just the raw list
so then we could eventually re-use in other more specialized functions
"""
GlobalConfig.logger.debug('get_dependencies({0})'.format(filename))
popen_args = ['otool', '-L', filename]
proc_out = run_and_get_output(popen_args)
deps = []
if proc_out.retcode == 0:
# some string splitting
deps = [s.strip().split(' ')[0] for s in proc_out.stdout.splitlines()[1:] if s]
# prevent infinite recursion when a binary depends on itself (seen with QtWidgets)...
deps = [s for s in deps if os.path.basename(filename) not in s]
return deps
def is_qt_plugin(filename):
"""
check if a given file is a qt plugin
accept absolute path as well as path containing @executable_path
"""
qtlib_name_rgx = re.compile(QTPLUGIN_NAME_REGEX)
rgxret = qtlib_name_rgx.match(filename)
if rgxret is not None:
GlobalConfig.logger.debug('rgxret is not None for {0}: {1}'.format(filename, rgxret.groups()))
return rgxret is not None
def is_qt_lib(filename):
"""
check if a given file is a qt library
accept absolute path as well as path containing @executable_path
"""
qtlib_name_rgx = re.compile(QTLIB_NAME_REGEX)
rgxret = qtlib_name_rgx.match(filename)
return rgxret is not None
def normalize_qtplugin_name(filename):
"""
input: a path to a qt plugin, as returned by otool, that can have this form :
- an absolute path /../plugins/PLUGINTYPE/PLUGINNAME.dylib
- @executable_path/../plugins/PLUGINTYPE/PLUGINNAME.dylib
output:
a tuple (qtlib, abspath, rpath) where:
- qtname is the name of the plugin (libqcocoa.dylib, etc.)
- abspath is the absolute path of the qt lib inside the app bundle of exepath
- relpath is the correct rpath to a qt lib inside the app bundle
"""
GlobalConfig.logger.debug('normalize_plugin_name({0})'.format(filename))
qtplugin_name_rgx = re.compile(QTPLUGIN_NAME_REGEX)
rgxret = qtplugin_name_rgx.match(filename)
if not rgxret:
msg = 'couldn\'t normalize a non-qt plugin filename: {0}'.format(filename)
GlobalConfig.logger.critical(msg)
raise Exception(msg)
# qtplugin normalization settings
qtplugintype = rgxret.groups()[0]
qtpluginname = rgxret.groups()[1]
templ = Template(QTPLUGIN_NORMALIZED)
# from qtlib, forge 2 path :
# - absolute path of qt lib in bundle,
abspath = os.path.normpath(templ.safe_substitute(
prefix=os.path.dirname(GlobalConfig.exepath) + '/..',
plugintype=qtplugintype,
pluginname=qtpluginname))
# - and rpath containing @executable_path, relative to exepath
rpath = templ.safe_substitute(
prefix='@executable_path/..',
plugintype=qtplugintype,
pluginname=qtpluginname)
GlobalConfig.logger.debug('\treturns({0})'.format((qtpluginname, abspath, rpath)))
return qtpluginname, abspath, rpath
def normalize_qtlib_name(filename):
"""
input: a path to a qt library, as returned by otool, that can have this form :
- an absolute path /lib/xxx/yyy
- @executable_path/../Frameworks/QtSerialPort.framework/Versions/5/QtSerialPort
output:
a tuple (qtlib, abspath, rpath) where:
- qtlib is the name of the qtlib (QtCore, QtWidgets, etc.)
- abspath is the absolute path of the qt lib inside the app bundle of exepath
- relpath is the correct rpath to a qt lib inside the app bundle
"""
GlobalConfig.logger.debug('normalize_qtlib_name({0})'.format(filename))
qtlib_name_rgx = re.compile(QTLIB_NAME_REGEX)
rgxret = qtlib_name_rgx.match(filename)
if not rgxret:
msg = 'couldn\'t normalize a non-qt lib filename: {0}'.format(filename)
GlobalConfig.logger.critical(msg)
raise Exception(msg)
# qtlib normalization settings
qtlib = rgxret.groups()[0]
qtversion = 5
templ = Template(QTLIB_NORMALIZED)
# from qtlib, forge 2 path :
# - absolute path of qt lib in bundle,
abspath = os.path.normpath(templ.safe_substitute(
prefix=os.path.dirname(GlobalConfig.exepath) + '/..',
qtlib=qtlib,
qtversion=qtversion))
# - and rpath containing @executable_path, relative to exepath
rpath = templ.safe_substitute(
prefix='@executable_path/..',
qtlib=qtlib,
qtversion=qtversion)
GlobalConfig.logger.debug('\treturns({0})'.format((qtlib, abspath, rpath)))
return qtlib, abspath, rpath
def fix_dependency(binary, dep):
"""
fix 'dep' dependency of 'binary'. 'dep' is a qt library
"""
if is_qt_lib(dep):
qtname, dep_abspath, dep_rpath = normalize_qtlib_name(dep)
elif is_qt_plugin(dep):
qtname, dep_abspath, dep_rpath = normalize_qtplugin_name(dep)
else:
return True
dep_ok = True
# check that rpath of 'dep' inside binary has been correctly set
# (ie: relative to exepath using '@executable_path' syntax)
if dep != dep_rpath:
# dep rpath is not ok
GlobalConfig.logger.info('changing rpath \'{0}\' in binary {1}'.format(dep, binary))
# call install_name_tool -change on binary
popen_args = ['install_name_tool', '-change', dep, dep_rpath, binary]
proc_out = run_and_get_output(popen_args)
if proc_out.retcode != 0:
GlobalConfig.logger.error(proc_out.stderr)
dep_ok = False
else:
# call install_name_tool -id on binary
popen_args = ['install_name_tool', '-id', dep_rpath, binary]
proc_out = run_and_get_output(popen_args)
if proc_out.retcode != 0:
GlobalConfig.logger.error(proc_out.stderr)
dep_ok = False
# now ensure that 'dep' exists at the specified path, relative to bundle
if dep_ok and not os.path.exists(dep_abspath):
# ensure destination directory exists
GlobalConfig.logger.info('ensuring directory \'{0}\' exists: {0}'.
format(os.path.dirname(dep_abspath)))
popen_args = ['mkdir', '-p', os.path.dirname(dep_abspath)]
proc_out = run_and_get_output(popen_args)
if proc_out.retcode != 0:
GlobalConfig.logger.info(proc_out.stderr)
dep_ok = False
else:
# copy missing dependency into bundle
qtnamesrc = os.path.join(GlobalConfig.qtpath, 'lib', '{0}.framework'.
format(qtname), qtname)
GlobalConfig.logger.info('copying missing dependency in bundle: {0}'.
format(qtname))
popen_args = ['cp', qtnamesrc, dep_abspath]
proc_out = run_and_get_output(popen_args)
if proc_out.retcode != 0:
GlobalConfig.logger.info(proc_out.stderr)
dep_ok = False
else:
# ensure permissions are correct if we ever have to change its rpath
GlobalConfig.logger.info('ensuring 755 perm to {0}'.format(dep_abspath))
popen_args = ['chmod', '755', dep_abspath]
proc_out = run_and_get_output(popen_args)
if proc_out.retcode != 0:
GlobalConfig.logger.info(proc_out.stderr)
dep_ok = False
else:
GlobalConfig.logger.debug('{0} is at correct location in bundle'.format(qtname))
if dep_ok:
return fix_binary(dep_abspath)
return False
def fix_binary(binary):
"""
input:
binary: relative or absolute path (no @executable_path syntax)
process:
- first fix the rpath for the qt libs on which 'binary' depend
- copy into the bundle of exepath the eventual libraries that are missing
- (create the soft links) needed ?
- do the same for all qt dependencies of binary (recursive)
"""
GlobalConfig.logger.debug('fix_binary({0})'.format(binary))
# loop on 'binary' dependencies
for dep in get_dependencies(binary):
if not fix_dependency(binary, dep):
GlobalConfig.logger.error('quitting early: couldn\'t fix dependency {0} of {1}'.format(dep, binary))
return False
return True
def fix_main_binaries():
"""
list the main binaries of the app bundle and fix them
"""
# deduce bundle path
bundlepath = os.path.sep.join(GlobalConfig.exepath.split(os.path.sep)[0:-3])
# fix main binary
GlobalConfig.logger.info('fixing executable \'{0}\''.format(GlobalConfig.exepath))
if fix_binary(GlobalConfig.exepath):
GlobalConfig.logger.info('fixing plugins')
for root, dummy, files in os.walk(bundlepath):
for name in [f for f in files if os.path.splitext(f)[1] == '.dylib']:
GlobalConfig.logger.info('fixing plugin {0}'.format(name))
if not fix_binary(os.path.join(root, name)):
return False
return True
def main():
"""
script entry point
"""
descr = """finish the job started by macdeployqt!
- find dependencies/rpathes with otool
- copy missed dependencies with cp and mkdir
- fix missed rpathes with install_name_tool
exit codes:
- 0 : success
- 1 : error
"""
parser = argparse.ArgumentParser(description=descr,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('exepath', help='path to the binary depending on Qt')
parser.add_argument('qtpath', help='path of Qt libraries used to build the Qt application')
parser.add_argument('-q', '--quiet', action='store_true', default=False,
help='do not create log on standard output')
parser.add_argument('-nl', '--no-log-file', action='store_true', default=False,
help='do not create log file \'./macdeployqtfix.log\'')
parser.add_argument('-v', '--verbose', action='store_true', default=False,
help='produce more log messages(debug log)')
args = parser.parse_args()
# globals
GlobalConfig.qtpath = os.path.normpath(args.qtpath)
GlobalConfig.exepath = args.exepath
GlobalConfig.logger = logging.getLogger()
# configure logging
###################
# create formatter
formatter = logging.Formatter('%(levelname)s | %(message)s')
# create console GlobalConfig.logger
if not args.quiet:
chdlr = logging.StreamHandler(sys.stdout)
chdlr.setFormatter(formatter)
GlobalConfig.logger.addHandler(chdlr)
# create file GlobalConfig.logger
if not args.no_log_file:
fhdlr = logging.FileHandler('./macdeployqtfix.log', mode='w')
fhdlr.setFormatter(formatter)
GlobalConfig.logger.addHandler(fhdlr)
if args.no_log_file and args.quiet:
GlobalConfig.logger.addHandler(logging.NullHandler())
else:
if args.verbose:
GlobalConfig.logger.setLevel(logging.DEBUG)
else:
GlobalConfig.logger.setLevel(logging.INFO)
if fix_main_binaries():
GlobalConfig.logger.info('process terminated with success')
sys.exit(0)
else:
GlobalConfig.logger.error('process terminated with error')
sys.exit(1)
if __name__ == "__main__":
main()