diff --git a/CMakeLists.txt b/CMakeLists.txt index e5271ad..fff6993 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,10 +47,12 @@ if (APPLE) # of the updater binary set(CMAKE_OSX_ARCHITECTURES i386;x86_64) - # Build the updater so that it works on OS X 10.5 and above. - set(MIN_OSX_VERSION 10.5) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${MIN_OSX_VERSION}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=${MIN_OSX_VERSION}") + # Build the updater so that it works on OS X 10.6 and above. + if (NOT (DEFINED MIN_OSX_DEPLOYMENT_VERSION)) + set(MIN_OSX_DEPLOYMENT_VERSION 10.6) + endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${MIN_OSX_DEPLOYMENT_VERSION}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=${MIN_OSX_DEPLOYMENT_VERSION}") endif() add_subdirectory(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d8d9da..6a29bfa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,7 +47,6 @@ if (APPLE) set(SOURCES ${SOURCES} MacBundle.cpp StandardDirs.mm - StlSymbolsLeopard.cpp UpdateDialogCocoa.mm mac_dock_icon.cpp mac_info_plist.cpp) diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp index ffad7e2..03f033c 100644 --- a/src/FileUtils.cpp +++ b/src/FileUtils.cpp @@ -629,3 +629,41 @@ std::string FileUtils::getcwd() throw (IOException) #endif } +bool FileUtils::removeEmptyDirs(const char* path) +{ + DirIterator iter(path); + int fileCount = 0; + while (iter.next()) + { + if (iter.fileName() == "." || iter.fileName() == "..") + { + continue; + } + + if (!iter.isDir() || !removeEmptyDirs(iter.filePath().c_str())) + { + // entry is either not a directory or is a + // directory hierarchy which contains one or more non-directories + // once all empty dirs have been recursively removed + ++fileCount; + } + } + if (fileCount == 0) + { + try + { + FileUtils::rmdir(path); + return true; + } + catch (const std::exception& ex) + { + LOG(Error,"Unable to remove empty directory " + std::string(ex.what())); + return false; + } + } + else + { + return false; + } +} + diff --git a/src/FileUtils.h b/src/FileUtils.h index 24a241b..33b41fe 100644 --- a/src/FileUtils.h +++ b/src/FileUtils.h @@ -145,5 +145,12 @@ class FileUtils /** Returns the current working directory of the application. */ static std::string getcwd() throw (IOException); + + /** Recursively remove all empty directories from the path rooted at + * @p path. + * + * Returns true if @p path was removed. + */ + static bool removeEmptyDirs(const char* path); }; diff --git a/src/Platform.h b/src/Platform.h index b410855..c990c2c 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -28,6 +28,7 @@ // platform-specific type aliases #if defined(PLATFORM_UNIX) + #include #define PLATFORM_PID pid_t #else #define PLATFORM_PID DWORD diff --git a/src/ProcessUtils.cpp b/src/ProcessUtils.cpp index 1a774dc..3952978 100644 --- a/src/ProcessUtils.cpp +++ b/src/ProcessUtils.cpp @@ -201,6 +201,11 @@ int ProcessUtils::runElevatedLinux(const std::string& executable, #endif #ifdef PLATFORM_MAC + +// suppress warning about AuthorizationExecuteWithPriviledges +// being deprecated since OS X 10.7 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" int ProcessUtils::runElevatedMac(const std::string& executable, const std::list& args) { @@ -315,6 +320,7 @@ int ProcessUtils::runElevatedMac(const std::string& executable, return RunElevatedFailed; } } +#pragma clang diagnostic pop #endif // convert a list of arguments in a space-separated string. diff --git a/src/StlSymbolsLeopard.cpp b/src/StlSymbolsLeopard.cpp deleted file mode 100644 index d5f58eb..0000000 --- a/src/StlSymbolsLeopard.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Workarounds for iostream symbols that are referenced when building on OS X 10.7 but missing from -// OS X 10.5's stdlibc++.dylib. -// -// In the headers these are declared as extern templates but the symbols are not present under 10.5. -// This file forces the compiler to instantiate the templates. -// -// see http://stackoverflow.com/questions/3484043/os-x-program-runs-on-dev-machine-crashing-horribly-on-others - -#include - -_GLIBCXX_BEGIN_NAMESPACE(std) -// From ostream_insert.h -template ostream& __ostream_insert(ostream&, const char*, streamsize); - -#ifdef _GLIBCXX_USE_WCHAR_T -template wostream& __ostream_insert(wostream&, const wchar_t*, streamsize); -#endif - -// From ostream.tcc -template ostream& ostream::_M_insert(long); -template ostream& ostream::_M_insert(unsigned long); -template ostream& ostream::_M_insert(bool); -#ifdef _GLIBCXX_USE_LONG_LONG -template ostream& ostream::_M_insert(long long); -template ostream& ostream::_M_insert(unsigned long long); -#endif -template ostream& ostream::_M_insert(double); -template ostream& ostream::_M_insert(long double); -template ostream& ostream::_M_insert(const void*); - -#ifdef _GLIBCXX_USE_WCHAR_T -template wostream& wostream::_M_insert(long); -template wostream& wostream::_M_insert(unsigned long); -template wostream& wostream::_M_insert(bool); -#ifdef _GLIBCXX_USE_LONG_LONG -template wostream& wostream::_M_insert(long long); -template wostream& wostream::_M_insert(unsigned long long); -#endif -template wostream& wostream::_M_insert(double); -template wostream& wostream::_M_insert(long double); -template wostream& wostream::_M_insert(const void*); -#endif - -// From istream.tcc -template istream& istream::_M_extract(unsigned short&); -template istream& istream::_M_extract(unsigned int&); -template istream& istream::_M_extract(long&); -template istream& istream::_M_extract(unsigned long&); -template istream& istream::_M_extract(bool&); -#ifdef _GLIBCXX_USE_LONG_LONG -template istream& istream::_M_extract(long long&); -template istream& istream::_M_extract(unsigned long long&); -#endif -template istream& istream::_M_extract(float&); -template istream& istream::_M_extract(double&); -template istream& istream::_M_extract(long double&); -template istream& istream::_M_extract(void*&); - -#ifdef _GLIBCXX_USE_WCHAR_T -template wistream& wistream::_M_extract(unsigned short&); -template wistream& wistream::_M_extract(unsigned int&); -template wistream& wistream::_M_extract(long&); -template wistream& wistream::_M_extract(unsigned long&); -template wistream& wistream::_M_extract(bool&); -#ifdef _GLIBCXX_USE_LONG_LONG -template wistream& wistream::_M_extract(long long&); -template wistream& wistream::_M_extract(unsigned long long&); -#endif -template wistream& wistream::_M_extract(float&); -template wistream& wistream::_M_extract(double&); -template wistream& wistream::_M_extract(long double&); -template wistream& wistream::_M_extract(void*&); -#endif - -_GLIBCXX_END_NAMESPACE diff --git a/src/UpdateDialogGtkFactory.cpp b/src/UpdateDialogGtkFactory.cpp index b1da8b5..c62e129 100644 --- a/src/UpdateDialogGtkFactory.cpp +++ b/src/UpdateDialogGtkFactory.cpp @@ -1,5 +1,6 @@ #include "UpdateDialogGtkFactory.h" +#include "FileUtils.h" #include "Log.h" #include "UpdateDialog.h" #include "StringUtils.h" @@ -21,34 +22,41 @@ extern unsigned int libupdatergtk_so_len; // pointers to helper functions in the GTK updater UI library UpdateDialogGtk* (*update_dialog_gtk_new)() = 0; -#define BIND_FUNCTION(library,function) \ - function = reinterpret_cast(dlsym(library,#function)); +#if __cplusplus >= 201103L +#define TYPEOF(x) decltype(x) +#else +#define TYPEOF(x) typeof(x) +#endif -bool extractFileFromBinary(const char* path, const void* buffer, size_t length) +#define BIND_FUNCTION(library,function) \ + function = reinterpret_cast(dlsym(library,#function)); + +#define MAX_FILE_PATH 4096 + +bool extractFileFromBinary(int fd, const void* buffer, size_t length) { - int fd = open(path,O_CREAT | O_WRONLY | O_TRUNC,0755); size_t count = write(fd,buffer,length); - if (fd < 0 || count < length) - { - if (fd >= 0) - { - close(fd); - } - return false; - } - close(fd); - return true; + return count >= length; } UpdateDialog* UpdateDialogGtkFactory::createDialog() { - const char* libPath = "/tmp/libupdatergtk.so"; + char libPath[MAX_FILE_PATH]; + strncpy(libPath, "/tmp/mendeley-libUpdaterGtk.so.XXXXXX", MAX_FILE_PATH); - if (!extractFileFromBinary(libPath,libupdatergtk_so,libupdatergtk_so_len)) + int libFd = mkostemp(libPath, O_CREAT | O_WRONLY | O_TRUNC); + if (libFd == -1) + { + LOG(Warn,"Failed to create temporary file - " + std::string(strerror(errno))); + return 0; + } + + if (!extractFileFromBinary(libFd,libupdatergtk_so,libupdatergtk_so_len)) { LOG(Warn,"Failed to load the GTK UI library - " + std::string(strerror(errno))); return 0; } + close(libFd); void* gtkLib = dlopen(libPath,RTLD_LAZY); if (!gtkLib) @@ -58,6 +66,7 @@ UpdateDialog* UpdateDialogGtkFactory::createDialog() } BIND_FUNCTION(gtkLib,update_dialog_gtk_new); + + FileUtils::removeFile(libPath); return reinterpret_cast(update_dialog_gtk_new()); } - diff --git a/src/UpdateInstaller.cpp b/src/UpdateInstaller.cpp index ebce4f4..ba2724d 100644 --- a/src/UpdateInstaller.cpp +++ b/src/UpdateInstaller.cpp @@ -448,6 +448,10 @@ void UpdateInstaller::postInstallUpdate() // Info.plist file. FileUtils::touch(m_installDir.c_str()); #endif + + // recursively remove any empty directories in the installation dir + // remove any empty directories in the installation dir + FileUtils::removeEmptyDirs(m_installDir.c_str()); } void UpdateInstaller::setAutoClose(bool autoClose) diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 2af9b9c..9afe181 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -1,18 +1,12 @@ include_directories("${CMAKE_CURRENT_SOURCE_DIR}/..") -if (APPLE) - set(HELPER_SHARED_SOURCES ../StlSymbolsLeopard.cpp) -endif() - # Create helper binaries for unit tests add_executable(oldapp old_app.cpp - ${HELPER_SHARED_SOURCES} ) add_executable(newapp new_app.cpp - ${HELPER_SHARED_SOURCES} ) # Install data files required by unit tests diff --git a/src/tests/TestFileUtils.cpp b/src/tests/TestFileUtils.cpp index 709acc5..5a16161 100644 --- a/src/tests/TestFileUtils.cpp +++ b/src/tests/TestFileUtils.cpp @@ -39,6 +39,25 @@ void TestFileUtils::testStandardDirs() TEST_COMPARE(FileUtils::fileExists(tmpDir.data()), true); } +void TestFileUtils::testRemoveEmptyDirs() +{ + std::string tmpDir = FileUtils::tempPath(); + std::string rootDir = tmpDir + "/TestFileUtils-testRemoveEmptyDirs"; + std::string content = "non-empty-file-content"; + + FileUtils::mkpath((rootDir + "/nested/empty/dir").c_str()); + FileUtils::mkpath((rootDir + "/nested/empty2/dir").c_str()); + FileUtils::writeFile((rootDir + "/nonempty.txt").c_str(), content.c_str(), content.size()); + FileUtils::removeEmptyDirs(rootDir.c_str()); + + // root dir and the regular file should still exist + TEST_COMPARE(FileUtils::fileExists(rootDir.c_str()), true); + TEST_COMPARE(FileUtils::fileExists((rootDir + "/nonempty.txt").c_str()), true); + + // the empty nested directories should have been removed + TEST_COMPARE(FileUtils::fileExists((rootDir + "/nested").c_str()), true); +} + int main(int,char**) { TestList tests; diff --git a/src/tests/TestFileUtils.h b/src/tests/TestFileUtils.h index 1a45164..e20b804 100644 --- a/src/tests/TestFileUtils.h +++ b/src/tests/TestFileUtils.h @@ -7,4 +7,5 @@ class TestFileUtils void testIsRelative(); void testSymlinkFileExists(); void testStandardDirs(); + void testRemoveEmptyDirs(); }; diff --git a/src/tests/file_list.xml b/src/tests/file_list.xml index dff4b54..f31f7e8 100644 --- a/src/tests/file_list.xml +++ b/src/tests/file_list.xml @@ -45,8 +45,8 @@ - file-to-uninstall.txt symlink-to-file-to-uninstall.txt + will-become-empty-after-update/nested/file-to-uninstall.txt diff --git a/src/tests/test-update.rb b/src/tests/test-update.rb index 82965cf..fac81e0 100755 --- a/src/tests/test-update.rb +++ b/src/tests/test-update.rb @@ -141,6 +141,12 @@ else create_test_file("#{INSTALL_DIR}/symlink-to-file-to-uninstall.txt", "dummy file. this is a symlink on Unix") end +# Create a dummy file to uninstall in a directory +# which becomes empty after the update +empty_dir_path = "#{INSTALL_DIR}/will-become-empty-after-update/nested" +FileUtils.mkdir_p(empty_dir_path) +create_test_file("#{empty_dir_path}/file-to-uninstall.txt", "this file and its containing dir should be removed after the update") + # Populate package source dir with files to install Dir.mkdir(PACKAGE_SRC_DIR) nested_dir_path = "#{PACKAGE_SRC_DIR}/new-dir/new-dir2" diff --git a/src/tests/v2_file_list.xml b/src/tests/v2_file_list.xml index 202e5bb..7f449f6 100644 --- a/src/tests/v2_file_list.xml +++ b/src/tests/v2_file_list.xml @@ -51,6 +51,7 @@ test-dir/app-symlink ../app + new-dir/new-dir2/new-file.txt $TEST_FILENAME @@ -60,8 +61,8 @@ - file-to-uninstall.txt symlink-to-file-to-uninstall.txt + will-become-empty-after-update/nested/file-to-uninstall.txt diff --git a/tools/create-packages.rb b/tools/create-packages.rb index 7fcf961..583dce6 100755 --- a/tools/create-packages.rb +++ b/tools/create-packages.rb @@ -1,5 +1,6 @@ #!/usr/bin/ruby +require 'digest/sha1' require 'fileutils' require 'rubygems' require 'find' @@ -82,7 +83,7 @@ def strip_prefix(string,prefix) end def file_sha1(path) - return `sha1sum "#{path}"`.split(' ')[0] + Digest::SHA1.file(path).to_s end class UpdateScriptGenerator