Switch to extracted desktop file library

This commit is contained in:
TheAssassin
2018-12-22 23:09:04 +01:00
parent 6b715c691a
commit 5374e7ece9
34 changed files with 18 additions and 2227 deletions
+3 -18
View File
@@ -76,24 +76,9 @@
[submodule "lib/googletest"]
path = lib/googletest
url = https://github.com/google/googletest
[submodule "lib/boost-lexical_cast"]
path = lib/boost-lexical_cast
url = https://github.com/boostorg/lexical_cast
[submodule "lib/boost-concept_check"]
path = lib/boost-concept_check
url = https://github.com/boostorg/concept_check
[submodule "lib/boost-numeric_conversion"]
path = lib/boost-numeric_conversion
url = https://github.com/boostorg/numeric_conversion
[submodule "lib/boost-array"]
path = lib/boost-array
url = https://github.com/boostorg/array
[submodule "lib/boost-container"]
path = lib/boost-container
url = https://github.com/boostorg/container
[submodule "lib/boost-move"]
path = lib/boost-move
url = https://github.com/boostorg/move
[submodule "lib/boost-math"]
path = lib/boost-math
url = https://github.com/boostorg/math
[submodule "lib/linuxdeploy-desktopfile"]
path = lib/linuxdeploy-desktopfile
url = https://github.com/linuxdeploy/linuxdeploy-desktopfile
+1 -1
View File
@@ -5,7 +5,7 @@
#include <boost/filesystem.hpp>
// local includes
#include "linuxdeploy/core/desktopfile/desktopfile.h"
#include "linuxdeploy/desktopfile/desktopfile.h"
#pragma once
@@ -1,126 +0,0 @@
// system includes
#include <unordered_map>
// library includes
#include <boost/filesystem.hpp>
// local includes
#include "desktopfileentry.h"
#pragma once
namespace linuxdeploy {
namespace core {
namespace desktopfile {
/*
* Parse and read desktop files.
*/
class DesktopFile {
public:
// describes a single section
typedef std::unordered_map<std::string, DesktopFileEntry> section_t;
// describes all sections in the desktop file
typedef std::unordered_map<std::string, section_t> sections_t;
private:
// private data class pattern
class PrivateData;
std::shared_ptr<PrivateData> d;
// (in)equality operators are implemented outside this class
friend bool operator==(const DesktopFile& first, const DesktopFile& second);
friend bool operator!=(const DesktopFile& first, const DesktopFile& second);
public:
// default constructor
DesktopFile();
// construct from existing desktop file
// if the file exists, it will be read using DesktopFileReader
// if reading fails, exceptions will be thrown (see DesktopFileReader for more information)
explicit DesktopFile(const boost::filesystem::path& path);
// construct by reading an existing stream
// file must exist, otherwise std::runtime_error is thrown
explicit DesktopFile(std::istream& is);
// copy constructor
DesktopFile(const DesktopFile& other);
// copy assignment constructor
DesktopFile& operator=(const DesktopFile& other);
// move assignment operator
DesktopFile& operator=(DesktopFile&& other) noexcept;
public:
// returns true if a file has been loaded, false otherwise
bool isEmpty() const;
// read desktop file
// sets path associated with this file
// throws exceptions in case of issues, see DesktopFileReader for more information
void read(const boost::filesystem::path& path);
// read desktop file from existing stream
// throws exceptions in case of issues, see DesktopFileReader for more information
void read(std::istream& is);
// get path associated with this file
boost::filesystem::path path() const;
// sets the path associated with this desktop file
// used to e.g., save the desktop file
void setPath(const boost::filesystem::path& path);
// clear contents of desktop file
void clear();
// save desktop file
bool save() const;
// save desktop file to path
// does not change path associated with desktop file
// throws exceptions in case of errors, see DesktopFileWriter::save(...) for more information
bool save(const boost::filesystem::path& path) const;
// save desktop file to ostream
// does not change path associated with desktop file
// throws exceptions in case of errors, see DesktopFileWriter::save(...) for more information
bool save(std::ostream& os) const;
// check if entry exists in given section and key
bool entryExists(const std::string& section, const std::string& key) const;
// get key from desktop file
// an std::string passed as value parameter will be populated with the contents
// returns true (and populates value) if the key exists, false otherwise
bool getEntry(const std::string& section, const std::string& key, DesktopFileEntry& value) const;
// add key to section in desktop file
// the section will be created if it doesn't exist already
// returns true if an existing key was overwritten, false otherwise
bool setEntry(const std::string& section, const DesktopFileEntry& entry);
// add key to section in desktop file
// the section will be created if it doesn't exist already
// returns true if an existing key was overwritten, false otherwise
bool setEntry(const std::string& section, DesktopFileEntry&& entry);
// create common application entries in desktop file
// returns false if one of the keys exists and was left unmodified
bool addDefaultKeys(const std::string& executableFileName);
// validate desktop file
bool validate() const;
};
// DesktopFile equality operator
bool operator==(const DesktopFile& first, const DesktopFile& second);
// DesktopFile inequality operator
bool operator!=(const DesktopFile& first, const DesktopFile& second);
}
}
}
@@ -1,71 +0,0 @@
#pragma once
// system headers
#include <memory>
#include <string>
// library headers
#include <boost/lexical_cast.hpp>
namespace linuxdeploy {
namespace core {
namespace desktopfile {
class DesktopFileEntry {
private:
// opaque data class pattern
class PrivateData;
std::shared_ptr<PrivateData> d;
public:
// default constructor
DesktopFileEntry();
// construct from key and value
explicit DesktopFileEntry(std::string key, std::string value);
// copy constructor
DesktopFileEntry(const DesktopFileEntry& other);
// copy assignment constructor
DesktopFileEntry& operator=(const DesktopFileEntry& other);
// move assignment operator
DesktopFileEntry& operator=(DesktopFileEntry&& other) noexcept;
// equality operator
bool operator==(const DesktopFileEntry& other) const;
// inequality operator
bool operator!=(const DesktopFileEntry& other) const;
public:
// checks whether a key and value have been set
bool isEmpty() const;
// return entry's key
const std::string& key() const;
// return entry's value
const std::string& value() const;
public:
// convert value to integer
// throws boost::bad_lexical_cast in case of type errors
int32_t asInt() const;
// convert value to long
// throws boost::bad_lexical_cast in case of type errors
int64_t asLong() const;
// convert value to double
// throws boost::bad_lexical_cast in case of type errors
double asDouble() const;
// split CSV list value into vector
// the separator used to split the string is a semicolon as per desktop file spec
std::vector<std::string> parseStringList() const;
};
}
}
}
@@ -1,40 +0,0 @@
#pragma once
// system includes
#include <stdexcept>
#include <string>
namespace linuxdeploy {
namespace core {
namespace desktopfile {
/**
* Desktop file library's base exception.
*/
class DesktopFileError : public std::runtime_error {
public:
explicit DesktopFileError(const std::string& message = "unknown desktop file error") : runtime_error(message) {};
};
/**
* Exception thrown by DesktopFileReader on parsing errors.
*/
class ParseError : public DesktopFileError {
public:
explicit ParseError(const std::string& message = "unknown parse error") : DesktopFileError(message) {};
};
/**
* I/O exception, thrown if files cannot be opened, reading or writing fails etc.
*/
class IOError : public DesktopFileError {
public:
explicit IOError(const std::string& message = "unknown I/O error") : DesktopFileError(message) {};
};
class UnknownSectionError : public DesktopFileError {
public:
explicit UnknownSectionError(const std::string& section) : DesktopFileError("unknown section: " + section) {};
};
}
}
}
+3 -31
View File
@@ -9,6 +9,8 @@ add_library(args INTERFACE)
target_sources(args INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/args/args.hxx)
target_include_directories(args INTERFACE args)
add_subdirectory(linuxdeploy-desktopfile)
if(NOT USE_SYSTEM_BOOST)
add_library(boost_config INTERFACE)
set_property(TARGET boost_config PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-config/include>")
@@ -101,38 +103,8 @@ if(NOT USE_SYSTEM_BOOST)
boost_config boost_predef boost_assert boost_throw_exception boost_smart_ptr boost_core boost_mpl
boost_type_traits boost_static_assert boost_integer boost_preprocessor boost_functional boost_detail
)
add_library(boost_numeric_conversion INTERFACE)
set_property(TARGET boost_numeric_conversion PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-numeric_conversion/include>")
set_property(TARGET boost_numeric_conversion PROPERTY INTERFACE_LINK_LIBRARIES "boost_config")
add_library(boost_concept_check INTERFACE)
set_property(TARGET boost_concept_check PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-concept_check/include>")
set_property(TARGET boost_concept_check PROPERTY INTERFACE_LINK_LIBRARIES "boost_config")
target_link_libraries(boost_concept_check INTERFACE boost_numeric_conversion)
add_library(boost_array INTERFACE)
set_property(TARGET boost_array PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-array/include>")
set_property(TARGET boost_array PROPERTY INTERFACE_LINK_LIBRARIES "boost_config")
add_library(boost_move INTERFACE)
set_property(TARGET boost_move PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-move/include>")
set_property(TARGET boost_move PROPERTY INTERFACE_LINK_LIBRARIES "boost_config")
add_library(boost_container INTERFACE)
set_property(TARGET boost_container PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-container/include>")
set_property(TARGET boost_container PROPERTY INTERFACE_LINK_LIBRARIES boost_config boost_move)
add_library(boost_math INTERFACE)
set_property(TARGET boost_math PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-math/include>")
set_property(TARGET boost_math PROPERTY INTERFACE_LINK_LIBRARIES boost_config boost_move)
add_library(boost_lexical_cast INTERFACE)
set_property(TARGET boost_lexical_cast PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-lexical_cast/include>")
set_property(TARGET boost_lexical_cast PROPERTY INTERFACE_LINK_LIBRARIES "boost_config")
target_link_libraries(boost_lexical_cast INTERFACE boost_concept_check boost_numeric_conversion boost_array boost_container boost_math)
endif()
if(BUILD_TESTING)
if(BUILD_TESTING AND NOT TARGET gtest)
add_subdirectory(googletest)
endif()
Submodule lib/boost-array deleted from cef221d8b4
Submodule lib/boost-math deleted from 6bbba17f9e
Submodule lib/boost-move deleted from 3ce9452f93
+1 -1
View File
@@ -10,7 +10,7 @@ if(USE_SYSTEM_BOOST)
set(BOOST_LIBS Boost::filesystem Boost::regex)
else()
# use custom built libs
set(BOOST_LIBS boost_system boost_filesystem boost_regex boost_lexical_cast)
set(BOOST_LIBS boost_system boost_filesystem boost_regex)
endif()
+1
View File
@@ -8,6 +8,7 @@
using namespace linuxdeploy::core;
using namespace linuxdeploy::core::log;
using namespace linuxdeploy::desktopfile;
namespace bf = boost::filesystem;
namespace linuxdeploy {
+1 -2
View File
@@ -18,11 +18,10 @@ target_include_directories(linuxdeploy_core_log PUBLIC ${PROJECT_SOURCE_DIR}/inc
target_link_libraries(linuxdeploy_core_log PUBLIC ${BOOST_LIBS})
add_subdirectory(copyright)
add_subdirectory(desktopfile)
add_library(linuxdeploy_core STATIC elf.cpp appdir.cpp ${HEADERS})
target_link_libraries(linuxdeploy_core PUBLIC
linuxdeploy_plugin linuxdeploy_core_log linuxdeploy_util linuxdeploy_core_desktopfile
linuxdeploy_plugin linuxdeploy_core_log linuxdeploy_util linuxdeploy_desktopfile
${BOOST_LIBS} CImg ${CMAKE_THREAD_LIBS_INIT}
)
target_link_libraries(linuxdeploy_core PRIVATE linuxdeploy_core_copyright)
+3 -3
View File
@@ -12,9 +12,9 @@
// local headers
#include "linuxdeploy/core/appdir.h"
#include "linuxdeploy/core/desktopfile/desktopfileentry.h"
#include "linuxdeploy/core/elf.h"
#include "linuxdeploy/core/log.h"
#include "linuxdeploy/desktopfile/desktopfileentry.h"
#include "linuxdeploy/util/util.h"
#include "copyright.h"
@@ -22,7 +22,7 @@
#include "excludelist.h"
using namespace linuxdeploy::core;
using namespace linuxdeploy::core::desktopfile;
using namespace linuxdeploy::desktopfile;
using namespace linuxdeploy::core::log;
using namespace cimg_library;
@@ -651,7 +651,7 @@ namespace linuxdeploy {
}), paths.end());
for (const auto& path : paths) {
desktopFiles.emplace_back(path);
desktopFiles.emplace_back(path.string());
}
return desktopFiles;
-17
View File
@@ -1,17 +0,0 @@
cmake_minimum_required(VERSION 3.0)
file(GLOB HEADERS ${PROJECT_SOURCE_DIR}/include/linuxdeploy/core/desktopfile/*.h)
add_library(linuxdeploy_core_desktopfile STATIC
desktopfile.cpp
desktopfileentry.cpp
desktopfilereader.cpp
desktopfilewriter.cpp
desktopfilereader.h
desktopfilewriter.h
${HEADERS}
)
target_include_directories(linuxdeploy_core_desktopfile PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(linuxdeploy_core_desktopfile PUBLIC linuxdeploy_util linuxdeploy_core_log ${BOOST_LIBS})
-214
View File
@@ -1,214 +0,0 @@
// local headers
#include "linuxdeploy/core/desktopfile/exceptions.h"
#include "linuxdeploy/core/desktopfile/desktopfile.h"
#include "linuxdeploy/core/log.h"
#include "desktopfilereader.h"
#include "desktopfilewriter.h"
using namespace linuxdeploy::core;
using namespace linuxdeploy::core::log;
namespace bf = boost::filesystem;
namespace linuxdeploy {
namespace core {
namespace desktopfile {
class DesktopFile::PrivateData {
public:
bf::path path;
sections_t data;
public:
PrivateData() = default;
void copyData(const std::shared_ptr<PrivateData>& other) {
path = other->path;
data = other->data;
}
public:
bool isEmpty() const {
return data.empty();
}
};
DesktopFile::DesktopFile() : d(std::make_shared<PrivateData>()) {}
DesktopFile::DesktopFile(const bf::path& path) : DesktopFile() {
// if the file doesn't exist, an exception shall be thrown
// otherwise, a user cannot know for sure whether a file was actually read (would need to check this
// manually beforehand
if (!bf::exists(path)) {
throw IOError("Could not find file " + path.string());
}
// will throw exceptions in case of issues
read(path);
};
DesktopFile::DesktopFile(std::istream& is) : DesktopFile() {
// will throw exceptions in case of issues
read(is);
};
// copy constructor
DesktopFile::DesktopFile(const DesktopFile& other) : DesktopFile() {
d->copyData(other.d);
}
// copy assignment constructor
DesktopFile& DesktopFile::operator=(const DesktopFile& other) {
if (this != &other) {
d->copyData(other.d);
}
return *this;
}
// move assignment operator
DesktopFile& DesktopFile::operator=(DesktopFile&& other) noexcept {
if (this != &other) {
d = other.d;
other.d = nullptr;
}
return *this;
}
void DesktopFile::read(const boost::filesystem::path& path) {
setPath(path);
// clear data before reading a new file
clear();
DesktopFileReader reader(path);
d->data = std::move(reader.data());
}
void DesktopFile::read(std::istream& is) {
// clear data before reading a new file
clear();
DesktopFileReader reader(is);
d->data = reader.data();
}
boost::filesystem::path DesktopFile::path() const {
return d->path;
}
void DesktopFile::setPath(const boost::filesystem::path& path) {
d->path = path;
}
bool DesktopFile::isEmpty() const {
return d->isEmpty();
}
void DesktopFile::clear() {
d->data.clear();
}
bool DesktopFile::save() const {
return save(d->path);
}
bool DesktopFile::save(const boost::filesystem::path& path) const {
DesktopFileWriter writer(d->data);
writer.save(path);
return true;
}
bool DesktopFile::save(std::ostream& os) const {
DesktopFileWriter writer(d->data);
writer.save(os);
return true;
}
bool DesktopFile::entryExists(const std::string& section, const std::string& key) const {
auto it = d->data.find(section);
if (it == d->data.end())
return false;
return (it->second.find(key) != it->second.end());
}
bool DesktopFile::setEntry(const std::string& section, const DesktopFileEntry& entry) {
// check if value exists -- used for return value
auto rv = entryExists(section, entry.key());
d->data[section][entry.key()] = entry;
return rv;
}
bool DesktopFile::setEntry(const std::string& section, DesktopFileEntry&& entry) {
// check if value exists -- used for return value
auto rv = entryExists(section, entry.key());
d->data[section][entry.key()] = entry;
return rv;
}
bool DesktopFile::getEntry(const std::string& section, const std::string& key, DesktopFileEntry& entry) const {
if (!entryExists(section, key))
return false;
entry = d->data[section][key];
// make sure keys are equal
assert(key == entry.key());
return true;
}
bool DesktopFile::addDefaultKeys(const std::string& executableFileName) {
ldLog() << "Adding default values to desktop file:" << path() << std::endl;
auto rv = true;
auto setDefault = [&rv, this](const std::string& section, const std::string& key, const std::string& value) {
if (entryExists(section, key)) {
DesktopFileEntry entry;
// this should never return false
auto entryExists = getEntry(section, key, entry);
assert(entryExists);
ldLog() << LD_WARNING << "Key exists, not modified:" << key << "(current value:" << entry.value() << LD_NO_SPACE << ")" << std::endl;
rv = false;
} else {
auto entryOverwritten = setEntry(section, DesktopFileEntry(key, value));
assert(!entryOverwritten);
}
};
setDefault("Desktop Entry", "Name", executableFileName);
setDefault("Desktop Entry", "Exec", executableFileName);
setDefault("Desktop Entry", "Icon", executableFileName);
setDefault("Desktop Entry", "Type", "Application");
setDefault("Desktop Entry", "Categories", "Utility;");
return rv;
}
bool DesktopFile::validate() const {
// FIXME: call desktop-file-validate
return true;
}
bool operator==(const DesktopFile& first, const DesktopFile& second) {
return first.d->path == second.d->path && first.d->data == second.d->data;
}
bool operator !=(const DesktopFile& first, const DesktopFile& second) {
return !operator==(first, second);
}
}
}
}
-125
View File
@@ -1,125 +0,0 @@
// library headers
#include <boost/lexical_cast.hpp>
// local headers
#include "linuxdeploy/core/log.h"
#include "linuxdeploy/core/desktopfile/desktopfileentry.h"
using boost::lexical_cast;
namespace linuxdeploy {
namespace core {
using namespace log;
namespace desktopfile {
class DesktopFileEntry::PrivateData {
public:
std::string key;
std::string value;
public:
void copyData(const std::shared_ptr<PrivateData>& other) {
key = other->key;
value = other->value;
}
void assertValueNotEmpty() {
if (value.empty())
throw std::invalid_argument("value is empty");
}
};
DesktopFileEntry::DesktopFileEntry() : d(new PrivateData) {}
DesktopFileEntry::DesktopFileEntry(std::string key, std::string value) : DesktopFileEntry() {
d->key = std::move(key);
d->value = std::move(value);
}
DesktopFileEntry::DesktopFileEntry(const DesktopFileEntry& other) : DesktopFileEntry() {
d->copyData(other.d);
}
DesktopFileEntry& DesktopFileEntry::operator=(const DesktopFileEntry& other) {
if (this != &other) {
d.reset(new PrivateData);
d->copyData(other.d);
}
return *this;
}
DesktopFileEntry& DesktopFileEntry::operator=(DesktopFileEntry&& other) noexcept {
if (this != &other) {
d = other.d;
other.d = nullptr;
}
return *this;
}
bool DesktopFileEntry::operator==(const DesktopFileEntry& other) const {
return d->key == other.d->key && d->value == other.d->value;
}
bool DesktopFileEntry::operator!=(const DesktopFileEntry& other) const {
return !operator==(other);
}
bool DesktopFileEntry::isEmpty() const {
return d->key.empty();
}
const std::string& DesktopFileEntry::key() const {
return d->key;
}
const std::string& DesktopFileEntry::value() const {
return d->value;
}
int32_t DesktopFileEntry::asInt() const {
d->assertValueNotEmpty();
return lexical_cast<int32_t>(value());
}
int64_t DesktopFileEntry::asLong() const {
d->assertValueNotEmpty();
return lexical_cast<int64_t>(value());
}
double DesktopFileEntry::asDouble() const {
d->assertValueNotEmpty();
return lexical_cast<double>(value());
}
std::vector<std::string> DesktopFileEntry::parseStringList() const {
const auto& value = this->value();
if (value.empty())
return {};
if (value.back() != ';')
ldLog() << LD_DEBUG << "desktop file string list does not end with semicolon:" << value
<< std::endl;
std::vector<std::string> list;
std::stringstream ss(value);
std::string currentVal;
while (std::getline(ss, currentVal, ';')) {
// the last value will be empty, as in desktop files, lists shall end with a semicolon
// therefore we skip all empty values (assuming that empty values in lists in desktop files don't make sense anyway)
if (!currentVal.empty())
list.emplace_back(currentVal);
}
return list;
}
}
}
};

Some files were not shown because too many files have changed in this diff Show More