osx: Implement auto-upgrade function using Sparkle
During the upgrade, NFD and ndn-autoupdate will be stopped (and, if
enabled, automatically started with new versions).
Change-Id: I010ae4a8aa6f99a0039d58065ab7eb0c9189a979
diff --git a/.waf-tools/osx-frameworks.py b/.waf-tools/osx-frameworks.py
new file mode 100644
index 0000000..254fa8a
--- /dev/null
+++ b/.waf-tools/osx-frameworks.py
@@ -0,0 +1,69 @@
+#! /usr/bin/env python
+# encoding: utf-8
+
+from waflib import Logs, Utils
+from waflib.Configure import conf
+
+OSX_SECURITY_CODE='''
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+#import <Sparkle/Sparkle.h>
+#import <CoreWLAN/CWInterface.h>
+#import <CoreWLAN/CoreWLAN.h>
+#import <CoreWLAN/CoreWLANConstants.h>
+#import <CoreWLAN/CoreWLANTypes.h>
+int main() { }
+'''
+
+@conf
+def configure(conf):
+ if Utils.unversioned_sys_platform () == "darwin":
+ conf.check_cxx(framework_name='Foundation', uselib_store='OSX_FOUNDATION', mandatory=False, compile_filename='test.mm')
+ conf.check_cxx(framework_name='AppKit', uselib_store='OSX_APPKIT', mandatory=False, compile_filename='test.mm')
+ conf.check_cxx(framework_name='CoreWLAN', uselib_store='OSX_COREWLAN', define_name='HAVE_COREWLAN',
+ use="OSX_FOUNDATION", mandatory=False, compile_filename='test.mm')
+
+ def check_sparkle(**kwargs):
+ conf.check_cxx(framework_name="Sparkle", header_name=["Foundation/Foundation.h", "AppKit/AppKit.h"],
+ uselib_store='OSX_SPARKLE', define_name='HAVE_SPARKLE', mandatory=True,
+ compile_filename='test.mm', use="OSX_FOUNDATION OSX_APPKIT",
+ **kwargs
+ )
+ try:
+ # Try standard paths first
+ check_sparkle()
+ except:
+ try:
+ # Try local path
+ Logs.info ("Check local version of Sparkle framework")
+ check_sparkle(cxxflags="-F%s/osx/Frameworks/" % conf.path.abspath(),
+ linkflags="-F%s/osx/Frameworks/" % conf.path.abspath())
+ conf.env.HAVE_LOCAL_SPARKLE = 1
+ except:
+ import urllib, subprocess, os, shutil
+ if not os.path.exists('osx/Frameworks/Sparkle.framework'):
+ # Download to local path and retry
+ Logs.info ("Sparkle framework not found, trying to download it to 'build/'")
+
+ urllib.urlretrieve ("https://github.com/sparkle-project/Sparkle/releases/download/1.16.0/Sparkle-1.16.0.tar.bz2", "build/Sparkle.tar.bz2")
+ if os.path.exists('build/Sparkle.tar.bz2'):
+ try:
+ subprocess.check_call(['mkdir', 'build/Sparkle'])
+ subprocess.check_call(['tar', 'xjf', 'build/Sparkle.tar.bz2', '-C', 'build/Sparkle'])
+ os.remove("build/Sparkle.tar.bz2")
+ if not os.path.exists("osx/Frameworks"):
+ os.mkdir ("osx/Frameworks")
+ os.rename("build/Sparkle/Sparkle.framework", "osx/Frameworks/Sparkle.framework")
+ shutil.rmtree("build/Sparkle", ignore_errors=True)
+
+ check_sparkle(cxxflags="-F%s/osx/Frameworks/" % conf.path.abspath(),
+ linkflags="-F%s/osx/Frameworks/" % conf.path.abspath())
+ conf.env.HAVE_LOCAL_SPARKLE = 1
+ except subprocess.CalledProcessError as e:
+ conf.fatal("Cannot find Sparkle framework. Auto download failed: '%s' returned %s" % (' '.join(e.cmd), e.returncode))
+ except:
+ conf.fatal("Unknown Error happened when auto downloading Sparkle framework")
+
+ conf.env['LDFLAGS_OSX_SPARKLE'] += ['-Wl,-rpath,@loader_path/../Frameworks']
+ if conf.is_defined('HAVE_SPARKLE'):
+ conf.env.HAVE_SPARKLE = 1 # small cheat for wscript
diff --git a/make-deps.sh b/make-deps.sh
index 6a773ab..ae73214 100755
--- a/make-deps.sh
+++ b/make-deps.sh
@@ -9,16 +9,6 @@
mkdir build 2>/dev/null || true
path="$(pwd)"
-pushd build
-# wget https://github.com/sparkle-project/Sparkle/releases/download/1.14.0/Sparkle-1.14.0.tar.bz2
-wget https://github.com/sparkle-project/Sparkle/releases/download/1.16.0/Sparkle-1.16.0.tar.bz2
-mkdir Sparkle-1.16 || true
-pushd Sparkle-1.16
-tar xf ../Sparkle-1.16.0.tar.bz2
-popd
-mv Sparkle-1.16/Sparkle.framework .
-popd
-
#######################################
rm -Rf build/ndn-cxx
diff --git a/make-osx-bundle.py b/make-osx-bundle.py
index b7f98d4..6677d32 100755
--- a/make-osx-bundle.py
+++ b/make-osx-bundle.py
@@ -388,15 +388,13 @@
a.copy_etc(['nfd.conf'])
a.set_min_macosx_version('%s.0' % MIN_SUPPORTED_VERSION)
a.macdeployqt()
- a.copy_framework("build/Sparkle.framework")
+ a.copy_framework("osx/Frameworks/Sparkle.framework")
a.done()
# Sign our binaries, etc.
if options.codesign:
print ' * Signing binaries with identity `%s\'' % options.codesign
- binaries = (a.bundle)
-
- codesign(binaries)
+ codesign(a.bundle)
print ''
# Create diskimage
@@ -406,3 +404,8 @@
d.symlink('/Applications', '/Applications')
d.copy('build/%s/NDN.app' % MIN_SUPPORTED_VERSION, '/NDN.app')
d.create()
+
+ if options.codesign:
+ print ' * Signing .dmg with identity `%s\'' % options.codesign
+ codesign(fn)
+ print ''
diff --git a/ndn-appcast.xml b/ndn-appcast.xml
deleted file mode 100644
index dd442ef..0000000
--- a/ndn-appcast.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
- <channel>
- <title>NDN.app Changleog</title>
- <link>
- http://named-data.net/binaries/ndn-appcast.xml
- </link>
- <description>Most recent changes with links to updates.</description>
- <language>en</language>
- <item>
- <title>Version 0.1.0 (based on NFD version 0.4.0-22-g150b80d)</title>
- <description><![CDATA[
- <h2>Initial release</h2>
- ]]>
- </description>
- <pubDate>Mon, 21 March 2016 15:38:00 +0000</pubDate>
- <enclosure url="https://github.com/2nd-ndn-hackathon/nfd-binary-release/releases/download/0.1.0/NDN-0.1.0-NFD-0.4.0-22-g150b80d-10.10.dmg"
- sparkle:version="0.1.0"
- sparkle:dsaSignature="MC0CFQCSEv9IA78djidIEpQ4/X76rIEMIQIUXDRb1Stu8530bEJS4yZGUpymm1g="
- length="25623942"
- type="application/octet-stream" />
- </item>
- <item>
- <title>Version 0.1.1 (based on NFD version 0.4.0-22-g150b80d)</title>
- <description><![CDATA[
- <h2>Minor fixes</h2>
- <ul>
- <li>Should work on all OSX 10.10+ systems</li>
- <li>Fix bug with `ndn` wrapper</li>
- </ul>
- ]]>
- </description>
- <pubDate>Mon, 22 March 2016 14:42:00 +0000</pubDate>
- <enclosure url="https://github.com/2nd-ndn-hackathon/nfd-binary-release/releases/download/0.1.1/NDN-0.1.1-NFD-0.4.0-22-g150b80d-10.10.dmg"
- sparkle:version="0.1.1"
- sparkle:dsaSignature="MC0CFAPv0czBOZJTfZMW7NHG4WsoU1tFAhUAnNVAlhd1pfc1IYtKEQmQ+oBXePQ="
- length="18643229"
- type="application/octet-stream" />
- </item>
- </channel>
-</rss>
diff --git a/ndn-control-center.xml b/ndn-control-center.xml
new file mode 100644
index 0000000..360e599
--- /dev/null
+++ b/ndn-control-center.xml
@@ -0,0 +1,22 @@
+<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
+ <channel>
+ <title>NDN App Changleog</title>
+ <link>
+ https://irl.cs.ucla.edu/~cawka/ndn-control-center.xml
+ </link>
+ <description>Most recent changes with links to updates.</description>
+ <language>en</language>
+ <item>
+ <title>Version 0.1.1 (based on NFD version 0.5.1)</title>
+ <description><![CDATA[
+ ]]>
+ </description>
+ <pubDate>Wed, 1 Feb 2017 20:05:00 +0000</pubDate>
+ <enclosure url="https://irl.cs.ucla.edu/~cawka/NDN-0.1.1.dmg"
+ sparkle:version="0.1.1"
+ sparkle:dsaSignature="MC0CFB9uEpOR2T0SNQK5tMdztcUAmq+8AhUAroo1gpugccjxwepKEp+VdgILeNc="
+ length="30468966"
+ type="application/octet-stream" />
+ </item>
+ </channel>
+</rss>
diff --git a/src/Info.plist b/src/Info.plist
index 40eda16..6bb818d 100644
--- a/src/Info.plist
+++ b/src/Info.plist
@@ -33,7 +33,7 @@
<key>LSUIElement</key>
<string>1</string>
<key>SUFeedURL</key>
- <string>http://named-data.net/binaries/ndn-appcast.xml</string>
+ <string>https://irl.cs.ucla.edu/~cawka/ndn-control-center.xml</string>
<key>SUPublicDSAKeyFile</key>
<string>ndn_sparkle_pub.pem</string>
</dict>
diff --git a/src/main.cpp b/src/main.cpp
index d03f0cd..e39e70c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -140,7 +140,7 @@
ForwarderStatusModel m_forwarderStatusModel;
FibStatusModel m_fibModel;
- TrayMenu m_tray;
+ ncc::TrayMenu m_tray;
};
} // namespace ndn
diff --git a/src/osx-auto-update-sparkle.hpp b/src/osx-auto-update-sparkle.hpp
new file mode 100644
index 0000000..716f1dc
--- /dev/null
+++ b/src/osx-auto-update-sparkle.hpp
@@ -0,0 +1,53 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017, Regents of the University of California.
+ *
+ * This file is part of NFD Control Center. See AUTHORS.md for complete list of NFD
+ * authors and contributors.
+ *
+ * NFD Control Center is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Control Center is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with NFD
+ * Control Center, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NCC_OSX_AUTO_UPDATE_SPARKLE_HPP
+#define NCC_OSX_AUTO_UPDATE_SPARKLE_HPP
+
+#include "config.hpp"
+
+#ifndef OSX_BUILD
+#error "Cannot be included for non-OSX build"
+#endif // OSX_BUILD
+
+#include <string>
+#include <ndn-cxx/util/backports.hpp>
+
+namespace ndn {
+namespace ncc {
+
+class OsxAutoUpdateSparkle
+{
+public:
+ OsxAutoUpdateSparkle(const std::string& updateUrl);
+
+ ~OsxAutoUpdateSparkle();
+
+ void
+ checkForUpdates();
+
+private:
+ class Impl;
+ unique_ptr<Impl> m_impl;
+};
+
+} // namespace ncc
+} // namespace ndn
+
+#endif // NCC_OSX_AUTO_UPDATE_SPARKLE_HPP
diff --git a/src/osx-auto-update-sparkle.mm b/src/osx-auto-update-sparkle.mm
new file mode 100644
index 0000000..a4e5a0f
--- /dev/null
+++ b/src/osx-auto-update-sparkle.mm
@@ -0,0 +1,96 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017, Regents of the University of California.
+ *
+ * This file is part of NFD Control Center. See AUTHORS.md for complete list of NFD
+ * authors and contributors.
+ *
+ * NFD Control Center is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Control Center is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with NFD
+ * Control Center, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "osx-auto-update-sparkle.hpp"
+
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+#import <Sparkle/Sparkle.h>
+
+@interface UpdaterDelegate : NSObject<SUUpdaterDelegate> {
+ ndn::ncc::OsxAutoUpdateSparkle* this_;
+}
+
+- (void)setSelf:(ndn::ncc::OsxAutoUpdateSparkle*)ptr;
+
+- (BOOL)updater:(SUUpdater *)updater
+ shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update
+ untilInvoking:(NSInvocation *)invocation;
+
+@end
+
+@implementation UpdaterDelegate
+
+- (void)setSelf:(ndn::ncc::OsxAutoUpdateSparkle*)ptr
+{
+ this_ = ptr;
+}
+
+- (BOOL)updater:(SUUpdater *)updater
+ shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update
+ untilInvoking:(NSInvocation *)invocation
+{
+ NSTask *task = [[NSTask alloc] init];
+ task.launchPath = @"/usr/bin/killall";
+ task.arguments = @[@"ndn-autoconfig", @"nfd"];
+ [task launch];
+ return NO;
+}
+
+@end
+
+namespace ndn {
+namespace ncc {
+
+class OsxAutoUpdateSparkle::Impl
+{
+public:
+ SUUpdater* m_updater;
+ UpdaterDelegate* delegate;
+};
+
+OsxAutoUpdateSparkle::OsxAutoUpdateSparkle(const std::string& updateUrl)
+ : m_impl(make_unique<Impl>())
+{
+ m_impl->m_updater = [[SUUpdater sharedUpdater] retain];
+ NSURL* url = [NSURL URLWithString:[NSString stringWithUTF8String:updateUrl.data()]];
+ [m_impl->m_updater setFeedURL:url];
+ [m_impl->m_updater setAutomaticallyChecksForUpdates:YES];
+ [m_impl->m_updater setUpdateCheckInterval:86400];
+
+ m_impl->delegate = [[UpdaterDelegate alloc] init];
+ [m_impl->delegate setSelf:this];
+ [m_impl->m_updater setDelegate:m_impl->delegate];
+}
+
+OsxAutoUpdateSparkle::~OsxAutoUpdateSparkle()
+{
+ [m_impl->m_updater release];
+ // presummably SUUpdater handles garbage collection
+}
+
+void
+OsxAutoUpdateSparkle::checkForUpdates()
+{
+ //[m_impl->m_updater checkForUpdatesInBackground];
+ [m_impl->m_updater checkForUpdates:nil];
+}
+
+} // namespace ncc
+} // namespace ndn
diff --git a/src/tray-menu.cpp b/src/tray-menu.cpp
index d4d2637..2ec3cb4 100644
--- a/src/tray-menu.cpp
+++ b/src/tray-menu.cpp
@@ -35,6 +35,7 @@
#endif // OSX_BUILD
namespace ndn {
+namespace ncc {
TrayMenu::TrayMenu(QQmlContext* context, Face& face)
: m_context(context)
@@ -42,24 +43,22 @@
, m_menu(new QMenu(this))
, m_entryPref(new QAction("Preferences...", m_menu))
, m_entrySec(new QAction("Security...", m_menu))
+ , m_acProc(new QProcess())
+ , m_settings(new QSettings())
#ifdef OSX_BUILD
, m_entryEnableCli(new QAction("Enable Command Terminal Usage...", m_menu))
+ , m_checkForUpdates(new QAction("Check for updates", m_menu))
+ , m_sparkle("https://irl.cs.ucla.edu/~cawka/ndn-control-center.xml")
#endif
, m_entryQuit(new QAction("Quit", m_menu))
, m_keyViewerDialog(new ncc::KeyViewerDialog)
, m_face(face)
- , m_acProc(new QProcess())
- , m_settings(new QSettings())
{
connect(m_entryPref, SIGNAL(triggered()), this, SIGNAL(showApp()));
connect(this, SIGNAL(showApp()), this, SLOT(showPref()));
connect(m_entrySec, SIGNAL(triggered()), m_keyViewerDialog, SLOT(present()));
connect(m_entryQuit, SIGNAL(triggered()), this, SLOT(quitApp()));
-#ifdef OSX_BUILD
- connect(m_entryEnableCli, SIGNAL(triggered()), this, SLOT(enableCli()));
-#endif
-
connect(this, SIGNAL(nfdActivityUpdate(bool)), this, SLOT(updateNfdActivityIcon(bool)),
Qt::QueuedConnection);
@@ -69,7 +68,11 @@
m_menu->addAction(m_entryPref);
m_menu->addAction(m_entrySec);
#ifdef OSX_BUILD
+ connect(m_entryEnableCli, SIGNAL(triggered()), this, SLOT(enableCli()));
m_menu->addAction(m_entryEnableCli);
+
+ connect(m_checkForUpdates, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
+ m_menu->addAction(m_checkForUpdates);
#endif
m_menu->addAction(m_entryQuit);
m_tray = new QSystemTrayIcon(this);
@@ -284,7 +287,6 @@
connect(addNewRoute,SIGNAL(finished(int)), addNewRoute, SLOT(deleteLater()));
addNewRoute->start("bash", QStringList() << "-c" << cmd);
std::cout << "Done" << std::endl;
-
}
void
@@ -347,4 +349,13 @@
#endif
}
+#ifdef OSX_BUILD
+void
+TrayMenu::checkForUpdates()
+{
+ m_sparkle.checkForUpdates();
+}
+#endif // OSX_BUILD
+
+} // namespace ncc
} // namespace ndn
diff --git a/src/tray-menu.hpp b/src/tray-menu.hpp
index 3c7b0f0..841f068 100644
--- a/src/tray-menu.hpp
+++ b/src/tray-menu.hpp
@@ -39,10 +39,16 @@
#include "key-viewer-dialog.hpp"
+#ifdef OSX_BUILD
+#include "osx-auto-update-sparkle.hpp"
+#endif // OSX_BUILD
+
namespace ndn {
class Face;
+namespace ncc {
+
class TrayMenu : public QWidget
{
Q_OBJECT
@@ -113,6 +119,11 @@
static void
appendMsg(QString &target, QString source);
+#ifdef OSX_BUILD
+ void
+ checkForUpdates();
+#endif // OSX_BUILD
+
private:
QQmlContext* m_context;
bool m_isNfdRunning;
@@ -125,7 +136,9 @@
QSettings m_settings;
#ifdef OSX_BUILD
QAction* m_entryEnableCli;
-#endif
+ QAction* m_checkForUpdates;
+ OsxAutoUpdateSparkle m_sparkle;
+#endif // OSX_BUILD
QAction* m_entryQuit;
@@ -133,6 +146,7 @@
Face& m_face;
};
+} // namespace ncc
} // namespace ndn
#endif // NCC_TRAY_MENU_HPP
diff --git a/wscript b/wscript
index f536815..a25a5ad 100644
--- a/wscript
+++ b/wscript
@@ -7,10 +7,10 @@
def options(opt):
opt.load('compiler_c compiler_cxx qt5 gnu_dirs')
- opt.load('boost default-compiler-flags', tooldir='.waf-tools')
+ opt.load('boost osx-frameworks default-compiler-flags', tooldir='.waf-tools')
def configure(conf):
- conf.load('compiler_c compiler_cxx default-compiler-flags boost')
+ conf.load('compiler_c compiler_cxx default-compiler-flags boost osx-frameworks')
if 'PKG_CONFIG_PATH' not in os.environ:
conf.environ['PKG_CONFIG_PATH'] = Utils.subst_vars('${LIBDIR}/pkgconfig', conf.env)
@@ -55,7 +55,15 @@
bld.install_files("${DATAROOTDIR}/nfd-control-center",
bld.path.ant_glob(['res/*']))
else:
+ app.source += bld.path.ant_glob(['src/osx-*.mm', 'src/osx-*.cpp'])
+ app.use += " OSX_FOUNDATION OSX_APPKIT OSX_SPARKLE OSX_COREWLAN"
app.target = "NFD Control Center"
app.mac_app = True
app.mac_plist = 'src/Info.plist'
app.mac_files = [i.path_from(bld.path) for i in bld.path.ant_glob('res/**/*', excl='**/*.ai')]
+
+from waflib import TaskGen
+@TaskGen.extension('.mm')
+def m_hook(self, node):
+ """Alias .mm files to be compiled the same as .cpp files, gcc/clang will do the right thing."""
+ return self.create_compiled_task('cxx', node)