You've already forked linuxdeploy
mirror of
https://github.com/encounter/linuxdeploy.git
synced 2026-07-10 12:18:44 -07:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b86ccd46ee | |||
| 83a9690da1 | |||
| 5fc87e1c1c | |||
| 3375de8fc4 | |||
| 906c948304 | |||
| 371343baed | |||
| 63764e2e18 | |||
| 8441dbb589 | |||
| d13814d784 | |||
| cd83c28196 | |||
| f99cac25b5 | |||
| 11a923b672 | |||
| a8aaa09a55 | |||
| 0ced1390ce | |||
| af310722df | |||
| dcf1726889 | |||
| ea12263be2 | |||
| 9a44a10173 | |||
| b550e4da21 | |||
| c4167165ad | |||
| 276c6f2d18 | |||
| 5b54db31cc | |||
| 4a098896a9 | |||
| ec051c3e0c | |||
| 0741d9441b | |||
| 9b1b214142 | |||
| db77174f24 | |||
| a0b0378d36 | |||
| 388e7d5acb | |||
| cc30bb6bd1 | |||
| 0d60f67da3 | |||
| e8dee64447 | |||
| 36c6be95d6 | |||
| 2c4fed2fad | |||
| 21849f8fbf | |||
| 4de8b6589f | |||
| 7184bcb61d | |||
| 55d524b136 | |||
| 9743988c80 | |||
| 84b614c1ea | |||
| 1035ef726d | |||
| 1c7e768b86 | |||
| a28181d98c | |||
| fcd3ad49db | |||
| cc6310cbbd |
@@ -25,6 +25,7 @@ matrix:
|
||||
- g++-multilib
|
||||
- automake # required for patchelf
|
||||
- libfuse2:i386
|
||||
- libcairo2:i386
|
||||
|
||||
install:
|
||||
- git clone https://github.com/NixOS/patchelf.git -b 0.8
|
||||
|
||||
@@ -5,9 +5,6 @@ project(linuxdeploy C CXX)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(LINUXDEPLOY_VERSION 0.1-alpha-1)
|
||||
add_definitions(-DLINUXDEPLOY_VERSION="${LINUXDEPLOY_VERSION}")
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/Modules/")
|
||||
|
||||
set(USE_SYSTEM_BOOST OFF CACHE BOOL "Set to ON to use system boost libraries instead of building up to date boost libraries from source")
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
AppDir creation and maintenance tool.
|
||||
|
||||
|
||||
## About
|
||||
|
||||
AppImages are a well known and quite popular format for distributing applications from developers to end users.
|
||||
@@ -58,12 +59,12 @@ If your build system cannot produce such install trees or you prefer to bundle e
|
||||
An example run could look like this:
|
||||
|
||||
```bash
|
||||
./linuxdeploy*.AppImage --app-name myapp --appdir AppDir --init-appdir -e myapp -d myapp.desktop -i myapp_64x64.png
|
||||
./linuxdeploy*.AppImage --appdir AppDir -e myapp -d myapp.desktop -i myapp_64x64.png
|
||||
```
|
||||
|
||||
Of course both approaches can be combined, e.g., you can bundle additional executables with your main app.
|
||||
|
||||
linuxdeploy doesn't mind being run on an AppDir more than once, as it recognize previous runs, and should not break files within the AppDir. This is to allow you debug issues allows you to run linuxdeploy after resolving an issue that it reports.
|
||||
linuxdeploy doesn't mind being run on an AppDir more than once, as it recognize previous runs, and should not break files within the AppDir. This is called an "iterative workflow". In case of errors, you can simply fix them, and re-run linuxdeploy afterwards.
|
||||
If you ever encounter issues when running linuxdeploy on an existing AppDir, please let us know by [creating an issue](https://github.com/TheAssassin/linuxdeploy/issues/new).
|
||||
|
||||
|
||||
@@ -81,10 +82,17 @@ linuxdeploy looks for plugins in the following places:
|
||||
|
||||
You can use `./linuxdeploy*.AppImage --list-plugins` to get a list of all the plugins linuxdeploy has detected on your system.
|
||||
|
||||
linuxdeploy ships with some plugins
|
||||
linuxdeploy currently ships with some plugins. These are likely out of date. In case of issues, please download the latest version, which will take precendence over the bundled plugin.
|
||||
|
||||
If you want to use a plugin to bundle additional resources, please add `./linuxdeploy*.AppImage --plugin <name>` to your linuxdeploy command. Output plugins can be activated using `./linuxdeploy*.AppImage --output <name>`.
|
||||
|
||||
**Note:** If you want to suggest a plugin for a specific framework, language etc., please feel free to [create a new issue](https://github.com/linuxdeploy/linuxdeploy/issues/new). Current plugin requests can be found [here](https://github.com/linuxdeploy/linuxdeploy/issues?utf8=%E2%9C%93&q=label%3A%22plugin+request%22).
|
||||
|
||||
|
||||
## User guides and examples
|
||||
|
||||
Please see the [linuxdeploy user guide](https://docs.appimage.org/packaging-guide/linuxdeploy-user-guide.html) and the [native binaries packaging guide](https://docs.appimage.org/packaging-guide/native-binaries.html) in the [AppImage documentation](https://docs.appimage.org). There's also an [examples section](https://docs.appimage.org/packaging-guide/native-binaries.html#Examples).
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -92,6 +100,7 @@ If you want to use a plugin to bundle additional resources, please add `./linuxd
|
||||
|
||||
linuxdeploy does not change any environment variables such as `$PATH`. Your application **must** search for additional resources such as icon files or executables relative to the main binary.
|
||||
|
||||
|
||||
## Contact
|
||||
|
||||
The easiest way to get in touch with the developers is to join the IRC chatroom [#AppImage](https://webchat.freenode.net/?channels=appimage) on FreeNode. This is the preferred way for general feedback or questions how to use this application.
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# required for zlib imported target
|
||||
cmake_minimum_required(VERSION 3.3)
|
||||
|
||||
message(STATUS "Searching for libmagic")
|
||||
|
||||
find_library(LIBMAGIC_A libmagic.a)
|
||||
|
||||
if(NOT LIBMAGIC_A)
|
||||
message(FATAL_ERROR "libmagic.a not found")
|
||||
endif()
|
||||
|
||||
find_path(LIBMAGIC_MAGIC_H_DIR
|
||||
NAMES magic.h
|
||||
HINTS ${CMAKE_INSTALL_PREFIX}
|
||||
PATH_SUFFIXES include include/linux
|
||||
)
|
||||
|
||||
if(NOT LIBMAGIC_MAGIC_H_DIR)
|
||||
message(FATAL_ERROR "magic.h not found")
|
||||
endif()
|
||||
|
||||
# on Debian-ish systems, libmagic depends on zlib
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
add_library(libmagic_static INTERFACE IMPORTED)
|
||||
set_property(TARGET libmagic_static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${LIBMAGIC_MAGIC_H_DIR})
|
||||
set_property(TARGET libmagic_static PROPERTY INTERFACE_LINK_LIBRARIES ${LIBMAGIC_A};ZLIB::ZLIB)
|
||||
@@ -79,12 +79,6 @@ namespace linuxdeploy {
|
||||
// create symlinks for AppRun, desktop file and icon in the AppDir root directory
|
||||
bool createLinksInAppDirRoot(const desktopfile::DesktopFile& desktopFile, boost::filesystem::path customAppRunPath = "");
|
||||
|
||||
// set application name of primary "entry point" application
|
||||
// icons and other resources will then automatically be renamed using this value in order to
|
||||
// make the deployment easier by not requiring special filenames
|
||||
// resources' filenames should be prefixed with this value (example: linuxdeploy_48x48.png)
|
||||
void setAppName(const std::string& appName);
|
||||
|
||||
// list all executables in <AppDir>/usr/bin
|
||||
// this function does not perform a recursive search, but only searches the bin directory
|
||||
std::vector<boost::filesystem::path> listExecutables();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// system includes
|
||||
#include <vector>
|
||||
#include <string>
|
||||
// including system elf header, which allows for interpretation of the return values of the methods
|
||||
#include <elf.h>
|
||||
|
||||
// library includes
|
||||
#include <boost/filesystem.hpp>
|
||||
@@ -16,6 +18,12 @@ namespace linuxdeploy {
|
||||
explicit ElfFileParseError(const std::string& msg) : std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
// thrown by traceDynamicDependencies() if a dependency is missing
|
||||
class DependencyNotFoundError : public std::runtime_error {
|
||||
public:
|
||||
explicit DependencyNotFoundError(const std::string& msg) : std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
class ElfFile {
|
||||
private:
|
||||
class PrivateData;
|
||||
@@ -25,6 +33,16 @@ namespace linuxdeploy {
|
||||
explicit ElfFile(const boost::filesystem::path& path);
|
||||
~ElfFile();
|
||||
|
||||
public:
|
||||
// return system ELF OS ABI
|
||||
static uint8_t getSystemElfABI();
|
||||
|
||||
// return system ELF class (32-bit or 64-bit)
|
||||
static uint8_t getSystemElfClass();
|
||||
|
||||
// return system (ELF) endianness
|
||||
static uint8_t getSystemElfEndianness();
|
||||
|
||||
public:
|
||||
// recursively trace dynamic library dependencies of a given ELF file
|
||||
// this works for both libraries and executables
|
||||
@@ -40,6 +58,12 @@ namespace linuxdeploy {
|
||||
// set rpath in ELF file
|
||||
// returns true on success, false otherwise
|
||||
bool setRPath(const std::string& value);
|
||||
|
||||
// return ELF class
|
||||
uint8_t getElfClass();
|
||||
|
||||
// return OS ABI
|
||||
uint8_t getElfABI();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
namespace linuxdeploy {
|
||||
namespace plugin {
|
||||
namespace base {
|
||||
using namespace linuxdeploy::core::log;
|
||||
|
||||
template<int API_LEVEL>
|
||||
class PluginBase<API_LEVEL>::PrivateData {
|
||||
public:
|
||||
@@ -127,7 +129,7 @@ namespace linuxdeploy {
|
||||
auto pluginPath = path();
|
||||
auto args = {pluginPath.c_str(), "--appdir", appDirPath.string().c_str()};
|
||||
|
||||
auto log = linuxdeploy::core::log::ldLog();
|
||||
auto log = ldLog();
|
||||
log << "Running process:";
|
||||
for (const auto& arg : args) {
|
||||
log << "" << arg;
|
||||
@@ -147,41 +149,65 @@ namespace linuxdeploy {
|
||||
epfd->fd = fileno(process.error());
|
||||
epfd->events = POLLIN;
|
||||
|
||||
for (const auto& fd : {process.output(), process.error()}) {
|
||||
auto flags = fcntl(fileno(fd), F_GETFL, 0);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(fileno(fd), F_SETFL, flags);
|
||||
}
|
||||
|
||||
auto printOutput = [&pfds, opfd, epfd, this, &process]() {
|
||||
poll(pfds.data(), pfds.size(), -1);
|
||||
|
||||
if (opfd->revents & POLLIN) {
|
||||
std::vector<char> lineBuf(16384);
|
||||
fgets(lineBuf.data(), static_cast<int>(lineBuf.size()), process.output());
|
||||
|
||||
std::stringstream ss;
|
||||
ss << lineBuf.data();
|
||||
lineBuf.clear();
|
||||
|
||||
std::string currentLine;
|
||||
|
||||
std::ostringstream oss;
|
||||
|
||||
std::vector<char> buf(4096);
|
||||
auto* lineptr = buf.data();
|
||||
auto n = buf.size();
|
||||
|
||||
while (getline(&lineptr, &n, process.output()) != -1) {
|
||||
oss << "[" << d->name << "/stdout] " << buf.data();
|
||||
while (std::getline(ss, currentLine)) {
|
||||
oss << "[" << d->name << "/stdout] " << currentLine << std::endl;
|
||||
}
|
||||
linuxdeploy::core::log::ldLog() << oss.str();
|
||||
}
|
||||
|
||||
if (epfd->revents & POLLIN) {
|
||||
std::vector<char> lineBuf(16384);
|
||||
fgets(lineBuf.data(), static_cast<int>(lineBuf.size()), process.error());
|
||||
|
||||
std::stringstream ss;
|
||||
ss << lineBuf.data();
|
||||
lineBuf.clear();
|
||||
|
||||
std::string currentLine;
|
||||
|
||||
std::ostringstream oss;
|
||||
|
||||
std::vector<char> buf(4096);
|
||||
auto* lineptr = buf.data();
|
||||
auto n = buf.size();
|
||||
|
||||
while (getline(&lineptr, &n, process.error()) != -1) {
|
||||
oss << "[" << d->name << "/stderr] " << buf.data();
|
||||
while (std::getline(ss, currentLine)) {
|
||||
oss << "[" << d->name << "/stderr] " << currentLine << std::endl;
|
||||
}
|
||||
linuxdeploy::core::log::ldLog() << oss.str();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
do {
|
||||
printOutput();
|
||||
if (!printOutput()) {
|
||||
ldLog() << LD_ERROR << "Failed to communicate with process" << std::endl;
|
||||
process.kill();
|
||||
return -1;
|
||||
}
|
||||
} while (process.poll() < 0);
|
||||
|
||||
printOutput();
|
||||
if (!printOutput()) {
|
||||
ldLog() << LD_ERROR << "Failed to communicate with process" << std::endl;
|
||||
process.kill();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return process.retcode();
|
||||
}
|
||||
|
||||
@@ -6,10 +6,33 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace util {
|
||||
namespace misc {
|
||||
static bool path_contains_file(boost::filesystem::path dir, boost::filesystem::path file) {
|
||||
// If dir ends with "/" and isn't the root directory, then the final
|
||||
// component returned by iterators will include "." and will interfere
|
||||
// with the std::equal check below, so we strip it before proceeding.
|
||||
if (dir.filename() == ".")
|
||||
dir.remove_filename();
|
||||
// We're also not interested in the file's name.
|
||||
assert(file.has_filename());
|
||||
file.remove_filename();
|
||||
|
||||
// If dir has more components than file, then file can't possibly
|
||||
// reside in dir.
|
||||
auto dir_len = std::distance(dir.begin(), dir.end());
|
||||
auto file_len = std::distance(file.begin(), file.end());
|
||||
if (dir_len > file_len)
|
||||
return false;
|
||||
|
||||
// This stops checking when it reaches dir.end(), so it's OK if file
|
||||
// has more directory components afterward. They won't be checked.
|
||||
return std::equal(dir.begin(), dir.end(), file.begin());
|
||||
};
|
||||
|
||||
static inline bool ltrim(std::string& s, char to_trim = ' ') {
|
||||
// TODO: find more efficient way to check whether elements have been removed
|
||||
size_t initialLength = s.length();
|
||||
@@ -1,5 +1,4 @@
|
||||
// local includes
|
||||
#include "magicwrapper.h"
|
||||
#include "misc.h"
|
||||
|
||||
// import functions from misc module for convenience
|
||||
@@ -1,6 +1,7 @@
|
||||
add_library(subprocess INTERFACE)
|
||||
target_sources(subprocess INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/cpp-subprocess/subprocess.hpp)
|
||||
target_include_directories(subprocess INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/cpp-subprocess)
|
||||
target_compile_options(subprocess INTERFACE "-Wno-deprecated")
|
||||
|
||||
add_library(args INTERFACE)
|
||||
target_sources(args INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/args/args.hxx)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# 3.5 is required for imported boost targets
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# globally include own includes
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
@@ -10,6 +13,34 @@ else()
|
||||
set(BOOST_LIBS boost_system boost_filesystem boost_regex)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
# read Git revision ID
|
||||
# WARNING: this value will be stored in the CMake cache
|
||||
# to update it, you will have to reset the CMake cache
|
||||
# (doesn't matter for CI builds like Travis for instance, where there's no permanent CMake cache)
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_COMMIT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
# set version and build number
|
||||
set(VERSION 1-alpha)
|
||||
if("$ENV{TRAVIS_BUILD_NUMBER}" STREQUAL "")
|
||||
set(BUILD_NUMBER "<local dev build>")
|
||||
else()
|
||||
set(BUILD_NUMBER "Travis build $ENV{TRAVIS_BUILD_NUMBER}")
|
||||
endif()
|
||||
|
||||
# get current date
|
||||
execute_process(
|
||||
COMMAND env LC_ALL=C date -u "+%Y-%m-%d %H:%M:%S %Z"
|
||||
OUTPUT_VARIABLE DATE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
add_subdirectory(util)
|
||||
add_subdirectory(plugin)
|
||||
add_subdirectory(core)
|
||||
@@ -18,6 +49,11 @@ add_executable(linuxdeploy main.cpp)
|
||||
target_link_libraries(linuxdeploy linuxdeploy_core args)
|
||||
set_target_properties(linuxdeploy PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
|
||||
|
||||
target_compile_definitions(linuxdeploy PRIVATE -DLD_GIT_COMMIT="${GIT_COMMIT}")
|
||||
target_compile_definitions(linuxdeploy PRIVATE -DLD_VERSION="${VERSION}")
|
||||
target_compile_definitions(linuxdeploy PRIVATE -DLD_BUILD_NUMBER="${BUILD_NUMBER}")
|
||||
target_compile_definitions(linuxdeploy PRIVATE -DLD_BUILD_DATE="${DATE}")
|
||||
|
||||
add_executable(plugin_test plugin_test_main.cpp)
|
||||
target_link_libraries(plugin_test linuxdeploy_plugin)
|
||||
set_target_properties(plugin_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
# 3.5 is required for imported boost targets
|
||||
# 3.6 is required for the PkgConfig module's IMPORTED_TARGET library feature
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
# include headers to make CLion happy
|
||||
file(GLOB HEADERS ${PROJECT_SOURCE_DIR}/include/linuxdeploy/core/*.h)
|
||||
|
||||
find_package(Threads)
|
||||
|
||||
find_package(LibMagic REQUIRED)
|
||||
|
||||
find_package(CImg REQUIRED)
|
||||
|
||||
message(STATUS "Generating excludelist")
|
||||
@@ -17,8 +13,12 @@ execute_process(
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
add_library(linuxdeploy_core STATIC elf.cpp log.cpp appdir.cpp desktopfile.cpp ${HEADERS})
|
||||
target_link_libraries(linuxdeploy_core PUBLIC linuxdeploy_plugin linuxdeploy_util ${BOOST_LIBS} subprocess cpp-feather-ini-parser CImg libmagic_static ${CMAKE_THREAD_LIBS_INIT})
|
||||
add_library(linuxdeploy_core_copyright STATIC copyright.cpp copyright.h copyright_dpkgquery.cpp copyright_dpkgquery.h)
|
||||
target_link_libraries(linuxdeploy_core_copyright PUBLIC subprocess ${BOOST_LIBS})
|
||||
|
||||
add_library(linuxdeploy_core STATIC elf.cpp log.cpp appdir.cpp desktopfile.cpp ${HEADERS})
|
||||
target_link_libraries(linuxdeploy_core PUBLIC linuxdeploy_plugin linuxdeploy_util ${BOOST_LIBS} subprocess cpp-feather-ini-parser CImg ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(linuxdeploy_core PRIVATE linuxdeploy_core_copyright)
|
||||
target_include_directories(linuxdeploy_core PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_include_directories(linuxdeploy_core PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_definitions(linuxdeploy_core PUBLIC -DBOOST_NO_CXX11_SCOPED_ENUMS)
|
||||
|
||||
+82
-108
@@ -13,10 +13,14 @@
|
||||
#include "linuxdeploy/core/appdir.h"
|
||||
#include "linuxdeploy/core/elf.h"
|
||||
#include "linuxdeploy/core/log.h"
|
||||
#include "util.h"
|
||||
#include "linuxdeploy/util/util.h"
|
||||
#include "copyright.h"
|
||||
|
||||
// auto-generated headers
|
||||
#include "excludelist.h"
|
||||
|
||||
using namespace linuxdeploy::core;
|
||||
using namespace linuxdeploy::util::misc;
|
||||
using namespace linuxdeploy::core::log;
|
||||
|
||||
using namespace cimg_library;
|
||||
@@ -44,10 +48,31 @@ namespace linuxdeploy {
|
||||
// used to automatically rename resources to improve the UX, e.g. icons
|
||||
std::string appName;
|
||||
|
||||
public:
|
||||
PrivateData() : copyOperations(), stripOperations(), setElfRPathOperations(), visitedFiles(), appDirPath(), appName() {};
|
||||
std::shared_ptr<copyright::ICopyrightFilesManager> copyrightFilesManager;
|
||||
|
||||
public:
|
||||
PrivateData() : copyOperations(), stripOperations(), setElfRPathOperations(), visitedFiles(), appDirPath(), appName() {
|
||||
copyrightFilesManager = copyright::ICopyrightFilesManager::getInstance();
|
||||
};
|
||||
|
||||
public:
|
||||
// calculate library directory name for given ELF file, taking system architecture into account
|
||||
static std::string getLibraryDirName(const bf::path& path) {
|
||||
const auto systemElfClass = elf::ElfFile::getSystemElfClass();
|
||||
const auto elfClass = elf::ElfFile(path).getElfClass();
|
||||
|
||||
std::string libDirName = "lib";
|
||||
|
||||
if (systemElfClass != elfClass) {
|
||||
if (elfClass == ELFCLASS32)
|
||||
libDirName += "32";
|
||||
else
|
||||
libDirName += "64";
|
||||
}
|
||||
|
||||
return libDirName;
|
||||
}
|
||||
|
||||
// actually copy file
|
||||
// mimics cp command behavior
|
||||
bool copyFile(const bf::path& from, bf::path to, bool overwrite = false) {
|
||||
@@ -135,8 +160,10 @@ namespace linuxdeploy {
|
||||
const auto& from = pair.first;
|
||||
const auto& to = pair.second;
|
||||
|
||||
if (!copyFile(from, to))
|
||||
success = false;
|
||||
if (path_contains_file(appDirPath, from)) {
|
||||
success = bf::exists(to) || symlinkFile(from, to, true); // create a symbolic link
|
||||
} else
|
||||
success = copyFile(from, to); // copy the file
|
||||
|
||||
copyOperations.erase(copyOperations.begin());
|
||||
}
|
||||
@@ -148,6 +175,8 @@ namespace linuxdeploy {
|
||||
ldLog() << LD_WARNING << "$NO_STRIP environment variable detected, not stripping binaries" << std::endl;
|
||||
stripOperations.clear();
|
||||
} else {
|
||||
const auto stripPath = getStripPath();
|
||||
|
||||
while (!stripOperations.empty()) {
|
||||
const auto& filePath = *(stripOperations.begin());
|
||||
|
||||
@@ -161,7 +190,7 @@ namespace linuxdeploy {
|
||||
env.insert(std::make_pair(std::string("LC_ALL"), std::string("C")));
|
||||
|
||||
subprocess::Popen proc(
|
||||
{getStripPath().c_str(), filePath.c_str()},
|
||||
{stripPath.c_str(), filePath.c_str()},
|
||||
subprocess::output(subprocess::PIPE),
|
||||
subprocess::error(subprocess::PIPE),
|
||||
subprocess::environment(env)
|
||||
@@ -200,72 +229,14 @@ namespace linuxdeploy {
|
||||
return true;
|
||||
}
|
||||
|
||||
// search for copyright file related to given file
|
||||
// this function utilizes distribution tools like dpkg-query to determine the paths of copyright
|
||||
// files
|
||||
std::vector<bf::path> searchForCopyrightFiles(const bf::path& from) {
|
||||
auto check_command = [](const std::string& command) {
|
||||
auto p = subprocess::Popen(
|
||||
command,
|
||||
subprocess::output(subprocess::PIPE),
|
||||
subprocess::error(subprocess::PIPE)
|
||||
);
|
||||
|
||||
return p.wait();
|
||||
};
|
||||
|
||||
// cannot deploy copyright files for files in AppDir
|
||||
if (!util::stringStartsWith(bf::absolute(from).string(), bf::absolute(appDirPath).string())) {
|
||||
if (check_command("which dpkg-query") == 0) {
|
||||
ldLog() << LD_DEBUG << "Using dpkg-query to search for copyright files" << std::endl;
|
||||
|
||||
auto call = [](const std::initializer_list<const char*>& args) {
|
||||
auto proc = subprocess::Popen(
|
||||
args,
|
||||
subprocess::output(subprocess::PIPE),
|
||||
subprocess::error(subprocess::PIPE)
|
||||
);
|
||||
|
||||
auto output = proc.communicate();
|
||||
return std::make_pair(proc.retcode(), output.first);
|
||||
};
|
||||
|
||||
auto dpkgQueryPackages = call({"dpkg-query", "-S", from.c_str()});
|
||||
|
||||
if (dpkgQueryPackages.first != 0
|
||||
|| dpkgQueryPackages.second.buf.empty()
|
||||
|| dpkgQueryPackages.second.buf.front() == '\0') {
|
||||
ldLog() << LD_WARNING << "Could not find copyright files for file" << from << "using dpkg-query" << std::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto packageName = util::split(util::splitLines(dpkgQueryPackages.second.buf.data())[0], ':')[0];
|
||||
|
||||
if (!packageName.empty()) {
|
||||
|
||||
auto copyrightFilePath = bf::path("/usr/share/doc") / packageName / "copyright";
|
||||
|
||||
if (bf::is_regular_file(copyrightFilePath)) {
|
||||
return {copyrightFilePath};
|
||||
}
|
||||
} else {
|
||||
ldLog() << LD_WARNING << "Could not find copyright files for file" << from << "using dpkg-query" << std::endl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ldLog() << LD_DEBUG << "Cannot deploy copyright files for files in AppDir:" << from << std::endl;
|
||||
}
|
||||
|
||||
ldLog() << LD_DEBUG << "Could not find suitable tool for copyright files deployment, skipping" << from << std::endl;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// search for copyright file for file and deploy it to AppDir
|
||||
bool deployCopyrightFiles(const bf::path& from, const std::string& logPrefix = "") {
|
||||
ldLog() << logPrefix << LD_NO_SPACE << "Deploying copyright files for file" << from << std::endl;
|
||||
|
||||
auto copyrightFiles = searchForCopyrightFiles(from);
|
||||
if (copyrightFilesManager == nullptr)
|
||||
return false;
|
||||
|
||||
auto copyrightFiles = copyrightFilesManager->getCopyrightFilesForPath(from);
|
||||
|
||||
if (copyrightFiles.empty())
|
||||
return false;
|
||||
@@ -308,29 +279,33 @@ namespace linuxdeploy {
|
||||
auto logPrefix = getLogPrefix(recursionLevel);
|
||||
|
||||
ldLog() << logPrefix << LD_NO_SPACE << "Deploying dependencies for ELF file" << path << std::endl;
|
||||
|
||||
for (const auto& dependencyPath : elf::ElfFile(path).traceDynamicDependencies()) {
|
||||
if (!deployLibrary(dependencyPath, recursionLevel + 1))
|
||||
return false;
|
||||
try {
|
||||
for (const auto &dependencyPath : elf::ElfFile(path).traceDynamicDependencies()) {
|
||||
if (!deployLibrary(dependencyPath, recursionLevel + 1))
|
||||
return false;
|
||||
}
|
||||
} catch (const elf::DependencyNotFoundError& e) {
|
||||
ldLog() << LD_ERROR << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string getStripPath() {
|
||||
// by default, try to use a patchelf next to the linuxdeploy binary
|
||||
// if that isn't available, fall back to searching for patchelf in the PATH
|
||||
std::string patchelfPath = "strip";
|
||||
// by default, try to use a strip next to the linuxdeploy binary
|
||||
// if that isn't available, fall back to searching for strip in the PATH
|
||||
std::string stripPath = "strip";
|
||||
|
||||
auto binDirPath = bf::path(util::getOwnExecutablePath());
|
||||
auto localStripPath = binDirPath.parent_path() / "strip";
|
||||
auto binDirPath = bf::path(util::getOwnExecutablePath()).parent_path();
|
||||
auto localStripPath = binDirPath / "strip";
|
||||
|
||||
if (bf::exists(localStripPath))
|
||||
patchelfPath = localStripPath.string();
|
||||
stripPath = localStripPath.string();
|
||||
|
||||
ldLog() << LD_DEBUG << "Using strip:" << patchelfPath << std::endl;
|
||||
ldLog() << LD_DEBUG << "Using strip:" << stripPath << std::endl;
|
||||
|
||||
return patchelfPath;
|
||||
return stripPath;
|
||||
}
|
||||
|
||||
bool deployLibrary(const bf::path& path, int recursionLevel = 0, bool forceDeploy = false,const bf::path &destination = bf::path()) {
|
||||
@@ -372,19 +347,22 @@ namespace linuxdeploy {
|
||||
return true;
|
||||
}
|
||||
|
||||
// note for self: make sure to have a trailing slash in libraryDir, otherwise copyFile won't
|
||||
// create a directory
|
||||
bf::path libraryDir = appDirPath / "usr" / (getLibraryDirName(path) + "/");
|
||||
|
||||
ldLog() << logPrefix << LD_NO_SPACE << "Deploying shared library" << path;
|
||||
if (!destination.empty())
|
||||
ldLog() << " (destination:" << destination << LD_NO_SPACE << ")";
|
||||
ldLog() << std::endl;
|
||||
|
||||
auto destinationPath = destination.empty() ? appDirPath / "usr/lib/" : destination;
|
||||
auto destinationPath = destination.empty() ? libraryDir : destination;
|
||||
|
||||
// not sure whether this is 100% bullet proof, but it simulates the cp command behavior
|
||||
if (destinationPath.string().back() == '/' || bf::is_directory(destinationPath)) {
|
||||
destinationPath /= path.filename();
|
||||
}
|
||||
|
||||
|
||||
deployFile(path, destinationPath);
|
||||
deployCopyrightFiles(path, logPrefix);
|
||||
|
||||
@@ -402,7 +380,7 @@ namespace linuxdeploy {
|
||||
rpathDestination = destination.parent_path().string();
|
||||
}
|
||||
|
||||
auto relPath = bf::relative(bf::absolute(appDirPath) / "usr/lib", bf::absolute(rpathDestination));
|
||||
auto relPath = bf::relative(bf::absolute(libraryDir), bf::absolute(rpathDestination));
|
||||
rpath = "$ORIGIN/" + relPath.string() + ":$ORIGIN";
|
||||
}
|
||||
|
||||
@@ -431,7 +409,7 @@ namespace linuxdeploy {
|
||||
deployFile(path, destinationPath);
|
||||
deployCopyrightFiles(path);
|
||||
|
||||
std::string rpath = "$ORIGIN/../lib";
|
||||
std::string rpath = "$ORIGIN/../" + getLibraryDirName(path);
|
||||
|
||||
if (!destination.empty()) {
|
||||
std::string rpathDestination = destination.string();
|
||||
@@ -445,7 +423,7 @@ namespace linuxdeploy {
|
||||
rpathDestination = destination.parent_path().string();
|
||||
}
|
||||
|
||||
auto relPath = bf::relative(bf::absolute(appDirPath) / "usr/lib", bf::absolute(rpathDestination));
|
||||
auto relPath = bf::relative(bf::absolute(appDirPath) / "usr" / getLibraryDirName(path), bf::absolute(rpathDestination));
|
||||
rpath = "$ORIGIN/" + relPath.string();
|
||||
}
|
||||
|
||||
@@ -701,7 +679,13 @@ namespace linuxdeploy {
|
||||
for (const auto& iconPath : foundIconPaths) {
|
||||
ldLog() << LD_DEBUG << "Icon found:" << iconPath << std::endl;
|
||||
|
||||
if (iconPath.stem() == iconName) {
|
||||
const bool matchesFilenameWithExtension = iconPath.filename() == iconName;
|
||||
|
||||
if (iconPath.stem() == iconName || matchesFilenameWithExtension) {
|
||||
if (matchesFilenameWithExtension) {
|
||||
ldLog() << LD_WARNING << "Icon= entry filename contains extension" << std::endl;
|
||||
}
|
||||
|
||||
ldLog() << "Deploying icon to AppDir root:" << iconPath << std::endl;
|
||||
|
||||
if (!d->symlinkFile(iconPath, path())) {
|
||||
@@ -786,20 +770,10 @@ namespace linuxdeploy {
|
||||
return d->deployFile(from, to, true);
|
||||
}
|
||||
|
||||
void AppDir::setAppName(const std::string& appName) {
|
||||
d->appName = appName;
|
||||
}
|
||||
|
||||
std::vector<bf::path> AppDir::listExecutables() {
|
||||
util::magic::Magic magic;
|
||||
|
||||
std::vector<bf::path> executables;
|
||||
|
||||
for (const auto& file : listFilesInDirectory(path() / "usr" / "bin", false)) {
|
||||
auto fileType = magic.fileType(bf::absolute(file).string());
|
||||
|
||||
ldLog() << LD_DEBUG << "Type of file" << file << LD_NO_SPACE << ":" << fileType << std::endl;
|
||||
|
||||
// make sure it's an ELF file
|
||||
try {
|
||||
elf::ElfFile elfFile(file);
|
||||
@@ -808,23 +782,16 @@ namespace linuxdeploy {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if (util::stringStartsWith(fileType, "application/x-executable"))
|
||||
executables.push_back(file);
|
||||
executables.push_back(file);
|
||||
}
|
||||
|
||||
return executables;
|
||||
}
|
||||
|
||||
std::vector<bf::path> AppDir::listSharedLibraries() {
|
||||
util::magic::Magic magic;
|
||||
|
||||
std::vector<bf::path> sharedLibraries;
|
||||
|
||||
for (const auto& file : listFilesInDirectory(path() / "usr" / "lib", true)) {
|
||||
auto fileType = magic.fileType(bf::absolute(file).string());
|
||||
|
||||
ldLog() << LD_DEBUG << "Type of file" << file << LD_NO_SPACE << ":" << fileType << std::endl;
|
||||
|
||||
// make sure it's an ELF file
|
||||
try {
|
||||
elf::ElfFile elfFile(file);
|
||||
@@ -833,8 +800,7 @@ namespace linuxdeploy {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if (util::stringStartsWith(fileType, "application/x-sharedlib"))
|
||||
sharedLibraries.push_back(file);
|
||||
sharedLibraries.push_back(file);
|
||||
}
|
||||
|
||||
return sharedLibraries;
|
||||
@@ -842,13 +808,21 @@ namespace linuxdeploy {
|
||||
|
||||
bool AppDir::deployDependenciesForExistingFiles() {
|
||||
for (const auto& executable : listExecutables()) {
|
||||
if (bf::is_symlink(executable))
|
||||
continue;
|
||||
|
||||
if (!d->deployElfDependencies(executable))
|
||||
return false;
|
||||
|
||||
d->setElfRPathOperations[executable] = "$ORIGIN/../lib";
|
||||
std::string rpath = "$ORIGIN/../" + PrivateData::getLibraryDirName(executable);
|
||||
|
||||
d->setElfRPathOperations[executable] = rpath;
|
||||
}
|
||||
|
||||
for (const auto& sharedLibrary : listSharedLibraries()) {
|
||||
if (bf::is_symlink(sharedLibrary))
|
||||
continue;
|
||||
|
||||
if (!d->deployElfDependencies(sharedLibrary))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// local includes
|
||||
#include "linuxdeploy/core/log.h"
|
||||
#include "copyright.h"
|
||||
|
||||
// specializations
|
||||
#include "copyright_dpkgquery.h"
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace copyright {
|
||||
using namespace log;
|
||||
|
||||
std::shared_ptr<ICopyrightFilesManager> ICopyrightFilesManager::getInstance() {
|
||||
auto check_command = [](const std::string& command) {
|
||||
auto p = subprocess::Popen(
|
||||
command,
|
||||
subprocess::output(subprocess::PIPE),
|
||||
subprocess::error(subprocess::PIPE)
|
||||
);
|
||||
|
||||
return p.wait();
|
||||
};
|
||||
|
||||
if (check_command("which dpkg-query") == 0) {
|
||||
ldLog() << LD_DEBUG << "Using dpkg-query to search for copyright files" << std::endl;
|
||||
return std::make_shared<DpkgQueryCopyrightFilesManager>();
|
||||
}
|
||||
|
||||
ldLog() << LD_DEBUG << "No usable copyright files manager implementation found" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// system includes
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// library includes
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace copyright {
|
||||
class CopyrightFilesManagerError : public std::runtime_error {
|
||||
explicit CopyrightFilesManagerError(const std::string& msg) : std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
class ICopyrightFilesManager {
|
||||
public:
|
||||
static std::shared_ptr<ICopyrightFilesManager> getInstance();
|
||||
|
||||
public:
|
||||
virtual std::vector<boost::filesystem::path> getCopyrightFilesForPath(const boost::filesystem::path& path) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// local includes
|
||||
#include "copyright_dpkgquery.h"
|
||||
#include "linuxdeploy/core/log.h"
|
||||
#include "linuxdeploy/util/util.h"
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace copyright {
|
||||
using namespace log;
|
||||
|
||||
std::vector<bf::path> DpkgQueryCopyrightFilesManager::getCopyrightFilesForPath(const bf::path& path) {
|
||||
auto call = [](const std::initializer_list<const char*>& args) {
|
||||
auto proc = subprocess::Popen(
|
||||
args,
|
||||
subprocess::output(subprocess::PIPE),
|
||||
subprocess::error(subprocess::PIPE)
|
||||
);
|
||||
|
||||
auto output = proc.communicate();
|
||||
return std::make_pair(proc.retcode(), output.first);
|
||||
};
|
||||
|
||||
auto dpkgQueryPackages = call({"dpkg-query", "-S", path.c_str()});
|
||||
|
||||
if (dpkgQueryPackages.first != 0
|
||||
|| dpkgQueryPackages.second.buf.empty()
|
||||
|| dpkgQueryPackages.second.buf.front() == '\0') {
|
||||
ldLog() << LD_WARNING << "Could not find copyright files for file" << path << "using dpkg-query" << std::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto packageName = util::split(util::splitLines(dpkgQueryPackages.second.buf.data())[0], ':')[0];
|
||||
|
||||
if (!packageName.empty()) {
|
||||
auto copyrightFilePath = bf::path("/usr/share/doc") / packageName / "copyright";
|
||||
|
||||
if (bf::is_regular_file(copyrightFilePath)) {
|
||||
return {copyrightFilePath};
|
||||
}
|
||||
} else {
|
||||
ldLog() << LD_WARNING << "Could not find copyright files for file" << path << "using dpkg-query" << std::endl;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// library includes
|
||||
#include <subprocess.hpp>
|
||||
|
||||
// local includes
|
||||
#include "copyright.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace bf = boost::filesystem;
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace copyright {
|
||||
class DpkgQueryCopyrightFilesManager : public ICopyrightFilesManager {
|
||||
private:
|
||||
class PrivateData;
|
||||
|
||||
public:
|
||||
std::vector<bf::path> getCopyrightFilesForPath(const bf::path& path) override;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+113
-6
@@ -1,14 +1,17 @@
|
||||
// system includes
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
// library includes
|
||||
#include <boost/regex.hpp>
|
||||
#include <subprocess.hpp>
|
||||
#include <sys/mman.h>
|
||||
|
||||
// local headers
|
||||
#include "linuxdeploy/core/elf.h"
|
||||
#include "linuxdeploy/core/log.h"
|
||||
#include "util.h"
|
||||
#include "linuxdeploy/util/util.h"
|
||||
|
||||
using namespace linuxdeploy::core::log;
|
||||
|
||||
@@ -20,9 +23,11 @@ namespace linuxdeploy {
|
||||
class ElfFile::PrivateData {
|
||||
public:
|
||||
const bf::path path;
|
||||
uint8_t elfClass = ELFCLASSNONE;
|
||||
uint8_t elfABI = 0;
|
||||
|
||||
public:
|
||||
explicit PrivateData(const bf::path& path) : path(path) {}
|
||||
explicit PrivateData(bf::path path) : path(std::move(path)) {}
|
||||
|
||||
public:
|
||||
static std::string getPatchelfPath() {
|
||||
@@ -40,6 +45,50 @@ namespace linuxdeploy {
|
||||
|
||||
return patchelfPath;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void readDataUsingElfAPI() {
|
||||
int fd = open(path.c_str(), O_RDONLY);
|
||||
auto map_size = static_cast<size_t>(lseek(fd, 0, SEEK_END));
|
||||
|
||||
std::shared_ptr<uint8_t> data(
|
||||
static_cast<uint8_t*>(mmap(nullptr, map_size, PROT_READ, MAP_SHARED, fd, 0)),
|
||||
[map_size](uint8_t* p) {
|
||||
if (munmap(static_cast<void*>(p), map_size) != 0) {
|
||||
int error = errno;
|
||||
throw ElfFileParseError(std::string("Failed to call munmap(): ") + strerror(error));
|
||||
}
|
||||
p = nullptr;
|
||||
}
|
||||
);
|
||||
close(fd);
|
||||
|
||||
// check which ELF "class" (32-bit or 64-bit) to use
|
||||
// the "class" is available at a specific constant offset in the section e_ident, which
|
||||
// happens to be the first section, so just reading one byte at EI_CLASS yields the data we're
|
||||
// looking for
|
||||
elfClass = data.get()[EI_CLASS];
|
||||
|
||||
switch (elfClass) {
|
||||
case ELFCLASS32: {
|
||||
auto* ehdr = reinterpret_cast<Elf32_Ehdr*>(data.get());
|
||||
auto* shdr = reinterpret_cast<Elf32_Shdr*>(data.get() + ehdr->e_shoff);
|
||||
|
||||
elfABI = ehdr->e_ident[EI_OSABI];
|
||||
}
|
||||
break;
|
||||
case ELFCLASS64: {
|
||||
auto* ehdr = reinterpret_cast<Elf32_Ehdr*>(data.get());
|
||||
auto* shdr = reinterpret_cast<Elf32_Shdr*>(data.get() + ehdr->e_shoff);
|
||||
|
||||
elfABI = ehdr->e_ident[EI_OSABI];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw ElfFileParseError("Unknown ELF class: " + std::to_string(elfClass));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ElfFile::ElfFile(const boost::filesystem::path& path) {
|
||||
@@ -59,6 +108,7 @@ namespace linuxdeploy {
|
||||
throw ElfFileParseError("Invalid magic bytes in file header");
|
||||
|
||||
d = new PrivateData(path);
|
||||
d->readDataUsingElfAPI();
|
||||
}
|
||||
|
||||
ElfFile::~ElfFile() {
|
||||
@@ -99,8 +149,13 @@ namespace linuxdeploy {
|
||||
util::trim(libraryPath);
|
||||
paths.push_back(bf::absolute(libraryPath));
|
||||
} else {
|
||||
if (util::stringContains("not found", line)) {
|
||||
ldLog() << LD_ERROR << "Could not find dependency:" << line << std::endl;
|
||||
if (util::stringContains(line, "not found")) {
|
||||
auto missingLib = line;
|
||||
static const std::string pattern = "=> not found";
|
||||
missingLib.erase(missingLib.find(pattern), pattern.size());
|
||||
util::trim(missingLib);
|
||||
util::trim(missingLib, '\t');
|
||||
throw DependencyNotFoundError("Could not find dependency: " + missingLib);
|
||||
} else {
|
||||
ldLog() << LD_DEBUG << "Invalid ldd output: " << line << std::endl;
|
||||
}
|
||||
@@ -113,7 +168,7 @@ namespace linuxdeploy {
|
||||
std::string ElfFile::getRPath() {
|
||||
try {
|
||||
subprocess::Popen patchelfProc(
|
||||
{d->getPatchelfPath().c_str(), "--print-rpath", d->path.c_str()},
|
||||
{PrivateData::getPatchelfPath().c_str(), "--print-rpath", d->path.c_str()},
|
||||
subprocess::output(subprocess::PIPE),
|
||||
subprocess::error(subprocess::PIPE)
|
||||
);
|
||||
@@ -148,7 +203,7 @@ namespace linuxdeploy {
|
||||
bool ElfFile::setRPath(const std::string& value) {
|
||||
try {
|
||||
subprocess::Popen patchelfProc(
|
||||
{d->getPatchelfPath().c_str(), "--set-rpath", value.c_str(), d->path.c_str()},
|
||||
{PrivateData::getPatchelfPath().c_str(), "--set-rpath", value.c_str(), d->path.c_str()},
|
||||
subprocess::output(subprocess::PIPE),
|
||||
subprocess::error(subprocess::PIPE)
|
||||
);
|
||||
@@ -167,6 +222,58 @@ namespace linuxdeploy {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t ElfFile::getSystemElfABI() {
|
||||
// the only way to get the system's ELF ABI is to read the own executable using the ELF header,
|
||||
// and get the ELFOSABI flag
|
||||
auto self = std::shared_ptr<char>(realpath("/proc/self/exe", nullptr), [](char* p) { free(p); });
|
||||
|
||||
if (self == nullptr)
|
||||
throw ElfFileParseError("Could not read /proc/self/exe");
|
||||
|
||||
std::ifstream ifs(self.get());
|
||||
|
||||
if (!ifs)
|
||||
throw ElfFileParseError("Could not open file: " + std::string(self.get()));
|
||||
|
||||
// the "class" is available at a specific constant offset in the section e_ident, which
|
||||
// happens to be the first section, so just reading one byte at EI_CLASS yields the data we're
|
||||
// looking for
|
||||
ifs.seekg(EI_OSABI);
|
||||
|
||||
char buf;
|
||||
ifs.read(&buf, 1);
|
||||
|
||||
return static_cast<uint8_t>(buf);
|
||||
}
|
||||
|
||||
uint8_t ElfFile::getSystemElfClass() {
|
||||
#if __SIZEOF_POINTER__ == 4
|
||||
return ELFCLASS32;
|
||||
#elif __SIZEOF_POINTER__ == 8
|
||||
return ELFCLASS64;
|
||||
#else
|
||||
#error "Invalid address size"
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t ElfFile::getSystemElfEndianness() {
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
return ELFDATA2LSB;
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
return ELFDATA2MSB;
|
||||
#else
|
||||
#error "Unknown machine endianness"
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t ElfFile::getElfClass() {
|
||||
return d->elfClass;
|
||||
}
|
||||
|
||||
uint8_t ElfFile::getElfABI() {
|
||||
return d->elfABI;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+57
-47
@@ -11,7 +11,7 @@
|
||||
#include "linuxdeploy/core/elf.h"
|
||||
#include "linuxdeploy/core/log.h"
|
||||
#include "linuxdeploy/plugin/plugin.h"
|
||||
#include "util.h"
|
||||
#include "linuxdeploy/util/util.h"
|
||||
|
||||
using namespace linuxdeploy::core;
|
||||
using namespace linuxdeploy::core::log;
|
||||
@@ -28,11 +28,10 @@ int main(int argc, char** argv) {
|
||||
args::Flag showVersion(parser, "", "Print version and exit", {'V', "version"});
|
||||
args::ValueFlag<int> verbosity(parser, "verbosity", "Verbosity of log output (0 = debug, 1 = info (default), 2 = warning, 3 = error)", {'v', "verbosity"});
|
||||
|
||||
args::Flag initAppDir(parser, "", "Create basic AppDir structure", {"init-appdir"});
|
||||
args::ValueFlag<std::string> appDirPath(parser, "appdir", "Path to target AppDir", {"appdir"});
|
||||
args::ValueFlag<std::string> appName(parser, "app-name", "Application name (used to initialize desktop file and name icons etc.)", {'n', "app-name"});
|
||||
args::ValueFlag<std::string> appName(parser, "app-name", "deprecated, please don't use it any more", {'n', "app-name"});
|
||||
|
||||
args::ValueFlagList<std::string> sharedLibraryPaths(parser, "library", "Shared library to deploy", {'l', "lib", "library"});
|
||||
args::ValueFlagList<std::string> sharedLibraryPaths(parser, "library", "Shared library to deploy", {'l', "library"});
|
||||
|
||||
args::ValueFlagList<std::string> executablePaths(parser, "executable", "Executable to deploy", {'e', "executable"});
|
||||
|
||||
@@ -59,16 +58,19 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// always show version statement
|
||||
std::cerr << "linuxdeploy version " << LINUXDEPLOY_VERSION << std::endl;
|
||||
std::cerr << "linuxdeploy version " << LD_VERSION
|
||||
<< " (git commit ID " << LD_GIT_COMMIT << "), "
|
||||
<< LD_BUILD_NUMBER << " built on " << LD_BUILD_DATE << std::endl;
|
||||
|
||||
// if only the version should be shown, we can exit now
|
||||
if (showVersion)
|
||||
return 0;
|
||||
|
||||
// set verbosity
|
||||
if (verbosity) {
|
||||
ldLog::setVerbosity((LD_LOGLEVEL) verbosity.Get());
|
||||
}
|
||||
|
||||
if (showVersion)
|
||||
return 0;
|
||||
|
||||
auto foundPlugins = linuxdeploy::plugin::findPlugins();
|
||||
|
||||
if (listPlugins) {
|
||||
@@ -91,16 +93,14 @@ int main(int argc, char** argv) {
|
||||
appdir::AppDir appDir(appDirPath.Get());
|
||||
|
||||
if (appName) {
|
||||
ldLog() << std::endl << "-- Deploying application \"" << LD_NO_SPACE << appName.Get() << LD_NO_SPACE << "\" --" << std::endl;
|
||||
appDir.setAppName(appName.Get());
|
||||
ldLog() << LD_WARNING << "--app-name parameter is deprecated, please don't use it any more" << std::endl;
|
||||
}
|
||||
|
||||
// initialize AppDir with common directories on request
|
||||
if (initAppDir) {
|
||||
ldLog() << std::endl << "-- Creating basic AppDir structure --" << std::endl;
|
||||
|
||||
if (!appDir.createBasicStructure())
|
||||
return 1;
|
||||
// initialize AppDir with common directories
|
||||
ldLog() << std::endl << "-- Creating basic AppDir structure --" << std::endl;
|
||||
if (!appDir.createBasicStructure()) {
|
||||
ldLog() << LD_ERROR << "Failed to create basic AppDir structure" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ldLog() << std::endl << "-- Deploying dependencies for existing files in AppDir --" << std::endl;
|
||||
@@ -143,6 +143,40 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
// run input plugins before deploying icons and desktop files
|
||||
// the input plugins might even fetch these resources somewhere into the AppDir, and this way, the user can make use of that
|
||||
if (inputPlugins) {
|
||||
for (const auto& pluginName : inputPlugins.Get()) {
|
||||
auto it = foundPlugins.find(std::string(pluginName));
|
||||
|
||||
ldLog() << std::endl << "-- Running input plugin:" << pluginName << "--" << std::endl;
|
||||
|
||||
if (it == foundPlugins.end()) {
|
||||
ldLog() << LD_ERROR << "Could not find plugin:" << pluginName;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto plugin = it->second;
|
||||
|
||||
if (plugin->pluginType() != linuxdeploy::plugin::INPUT_TYPE) {
|
||||
if (plugin->pluginType() == linuxdeploy::plugin::OUTPUT_TYPE) {
|
||||
ldLog() << LD_ERROR << "Plugin" << pluginName << "is an output plugin, please use like --output" << pluginName << std::endl;
|
||||
} else {
|
||||
ldLog() << LD_ERROR << "Plugin" << pluginName << "has unkown type:" << plugin->pluginType() << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto retcode = plugin->run(appDir.path());
|
||||
|
||||
if (retcode != 0) {
|
||||
ldLog() << LD_ERROR << "Failed to run plugin:" << pluginName << std::endl;
|
||||
ldLog() << LD_DEBUG << "Exited with return code:" << retcode << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iconPaths) {
|
||||
ldLog() << std::endl << "-- Deploying icons --" << std::endl;
|
||||
|
||||
@@ -213,34 +247,6 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (inputPlugins) {
|
||||
for (const auto& pluginName : inputPlugins.Get()) {
|
||||
auto it = foundPlugins.find(std::string(pluginName));
|
||||
|
||||
if (it == foundPlugins.end()) {
|
||||
ldLog() << LD_ERROR << "Could not find plugin:" << pluginName;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto plugin = it->second;
|
||||
|
||||
if (plugin->pluginType() != linuxdeploy::plugin::INPUT_TYPE) {
|
||||
ldLog() << LD_ERROR << "Plugin" << pluginName << "has wrong type:" << plugin->pluginType() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ldLog() << std::endl << "-- Running input plugin:" << pluginName << "--" << std::endl;
|
||||
|
||||
auto retcode = plugin->run(appDir.path());
|
||||
|
||||
if (retcode != 0) {
|
||||
ldLog() << LD_ERROR << "Failed to run plugin:" << pluginName << std::endl;
|
||||
ldLog() << LD_DEBUG << "Exited with return code:" << retcode << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search for desktop file and deploy it to AppDir root
|
||||
{
|
||||
ldLog() << std::endl << "-- Deploying files into AppDir root directory --" << std::endl;
|
||||
@@ -293,6 +299,8 @@ int main(int argc, char** argv) {
|
||||
for (const auto& pluginName : outputPlugins.Get()) {
|
||||
auto it = foundPlugins.find(std::string(pluginName));
|
||||
|
||||
ldLog() << std::endl << "-- Running output plugin:" << pluginName << "--" << std::endl;
|
||||
|
||||
if (it == foundPlugins.end()) {
|
||||
ldLog() << LD_ERROR << "Could not find plugin:" << pluginName;
|
||||
return 1;
|
||||
@@ -301,12 +309,14 @@ int main(int argc, char** argv) {
|
||||
auto plugin = it->second;
|
||||
|
||||
if (plugin->pluginType() != linuxdeploy::plugin::OUTPUT_TYPE) {
|
||||
ldLog() << LD_ERROR << "Plugin" << pluginName << "has wrong type:" << plugin->pluginType() << std::endl;
|
||||
if (plugin->pluginType() == linuxdeploy::plugin::INPUT_TYPE) {
|
||||
ldLog() << LD_ERROR << "Plugin" << pluginName << "is an input plugin, please use like --plugin" << pluginName << std::endl;
|
||||
} else {
|
||||
ldLog() << LD_ERROR << "Plugin" << pluginName << "has unknown type:" << plugin->pluginType() << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
ldLog() << std::endl << "-- Running output plugin:" << pluginName << "--" << std::endl;
|
||||
|
||||
auto retcode = plugin->run(appDir.path());
|
||||
|
||||
if (retcode != 0) {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "linuxdeploy/core/log.h"
|
||||
#include "linuxdeploy/plugin/base.h"
|
||||
#include "linuxdeploy/plugin/plugin.h"
|
||||
#include "util.h"
|
||||
#include "linuxdeploy/util/util.h"
|
||||
#include "plugin_type0.h"
|
||||
|
||||
using namespace linuxdeploy::core;
|
||||
@@ -46,7 +46,8 @@ namespace linuxdeploy {
|
||||
auto currentExeDir = bf::path(util::getOwnExecutablePath()).parent_path();
|
||||
paths.insert(paths.begin(), currentExeDir.string());
|
||||
|
||||
// if shipping as an AppImage, search for plugins in AppImage's location, too
|
||||
// if shipping as an AppImage, search for plugins in AppImage's location first
|
||||
// this way, plugins in the AppImage's directory take precedence over bundled ones
|
||||
if (getenv("APPIMAGE") != nullptr) {
|
||||
auto appImageDir = bf::path(getenv("APPIMAGE")).parent_path();
|
||||
paths.insert(paths.begin(), appImageDir.string());
|
||||
@@ -71,7 +72,12 @@ namespace linuxdeploy {
|
||||
ldLog() << LD_DEBUG << "Failed to create instance for plugin" << i->path();
|
||||
} else {
|
||||
ldLog() << LD_DEBUG << "Found plugin '" << LD_NO_SPACE << name << LD_NO_SPACE << "':" << plugin->path() << std::endl;
|
||||
foundPlugins[name] = plugin;
|
||||
|
||||
if (foundPlugins.find(name) != foundPlugins.end()) {
|
||||
ldLog() << LD_DEBUG << "Already found" << name << "plugin in" << foundPlugins[name]->path() << std::endl;
|
||||
} else {
|
||||
foundPlugins[name] = plugin;
|
||||
}
|
||||
}
|
||||
} catch (const PluginError& e) {
|
||||
ldLog() << LD_WARNING << "Could not load plugin" << i->path() << LD_NO_SPACE << ": " << e.what();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user