32 Commits

Author SHA1 Message Date
Alexis Lopez Zubieta 18f2d94241 Print the subprocess output directly to stdout and stderr. 2018-08-04 12:33:41 -05:00
TheAssassin 9743988c80 No longer bundle Qt plugin
It's almost always outdated, users will be better off downloading the
upstream AppImage.
2018-08-03 23:34:50 +02:00
TheAssassin 84b614c1ea Fix plugin finding 2018-08-03 23:31:25 +02:00
TheAssassin 1035ef726d Fix include path 2018-08-03 01:22:59 +02:00
TheAssassin 1c7e768b86 Provide interface to linuxdeploy_util 2018-08-03 00:56:16 +02:00
TheAssassin a28181d98c Fix CMake minimum required version 2018-07-31 21:35:06 +02:00
TheAssassin fcd3ad49db Link against shared libmagic library
This allows distro maintainers to build linuxdeploy more easily

CC @adrianschroeter
2018-07-31 16:21:46 +02:00
TheAssassin cc6310cbbd Allow icons with extension 2018-07-31 15:21:38 +02:00
TheAssassin c58d9716b0 Quick fix patchelf build 2018-07-31 15:06:19 +02:00
TheAssassin 3e84f424e7 Debug patchelf build, mk. 5 2018-07-31 14:51:22 +02:00
TheAssassin a386ab19fd Debug patchelf build, mk. 4 2018-07-31 14:35:25 +02:00
TheAssassin 4f84082155 Debug patchelf build, mk. 3 2018-07-31 14:32:01 +02:00
TheAssassin 8aa1bc9d89 Debug patchelf build, mk. 2 2018-07-31 14:25:08 +02:00
TheAssassin 56d9d6b540 Debug patchelf build 2018-07-31 14:20:42 +02:00
TheAssassin 5165301a44 Merge pull request #6 from linuxdeploy/fix_deploy_library_destinationPath
Fix deployLibrary destinationPath construction.
2018-07-18 21:52:05 +02:00
TheAssassin c1452fa870 Merge branch 'master' into fix_deploy_library_destinationPath 2018-07-18 21:46:25 +02:00
Alexis Lopez Zubieta 03cae41d55 Fix deployLibrary destinationPath construction. 2018-07-18 13:34:20 -05:00
TheAssassin c10eafe823 Bundle Qt plugin 2018-07-17 14:19:07 +02:00
TheAssassin 4a7301eba5 Use patchelf v0.8
Seems like 0.10 breaks Qt libraries which expect a symbol called
qt_version_tag in a specific section in the symbols. Patchelf breaks
this by apparently moving the symbols to another section
(section 15 vs. 13).
We found out by looking into the readelf outputs of libQt5Core.so.5.
@azubieta spotted the different numbers, and looked up their meaning.

linuxdeployqt uses the patchelf binaries Debian jessie provides, which
is 0.8. Therefore we try to build that version from source instead,
hoping that this version won't break libraries.

If that doesn't work either, we'll try with the pre-built binaries.
2018-07-13 01:01:53 +02:00
TheAssassin a3cc38d406 Show error message if dependency library cannot be found 2018-07-12 23:39:17 +02:00
TheAssassin 144fd2deb0 Deactivate strip calls if $NO_STRIP is set 2018-07-12 21:08:50 +02:00
TheAssassin b96389a064 Use -n parameter to enhance search for suitable desktop file
In case there's more than a desktop file, the user can specify the
app name using -n <app name>, and linuxdeploy will try to find a
suitable desktop file whose filename starts with the specified app
name.
2018-07-12 18:22:43 +02:00
TheAssassin b156ffc0cb Add stringEndsWith helper 2018-07-12 18:19:37 +02:00
TheAssassin bd052a5b1f Add prefix to every line in plugin output 2018-07-10 15:08:17 +02:00
TheAssassin f06d5e5832 Store plugin name in plugin class 2018-07-10 15:06:39 +02:00
TheAssassin a0ace9f75c Actually build 32-bit patchelf for 32-bit AppImage 2018-07-07 05:29:21 +02:00
TheAssassin 2b7a5928a5 Fix forceDeployLibrary, mk. 2 2018-07-01 02:28:44 +02:00
TheAssassin ec862d0af9 Fix forceDeployLibrary 2018-07-01 01:06:31 +02:00
TheAssassin df61b059d3 Force deploy libraries specified on the CLI 2018-06-30 23:27:02 +02:00
TheAssassin 5baac459b3 Don't upload AppImage plugin 2018-06-30 16:40:15 +02:00
TheAssassin c783be934b Fix build failure 2018-06-30 16:15:37 +02:00
TheAssassin ae6dfcfe21 Bundle AppImage plugin in Docker friendly way 2018-06-30 16:10:35 +02:00
15 changed files with 152 additions and 60 deletions
+4 -3
View File
@@ -27,10 +27,11 @@ matrix:
- libfuse2:i386
install:
- git clone https://github.com/NixOS/patchelf.git
- git clone https://github.com/NixOS/patchelf.git -b 0.8
- cd patchelf
- ./bootstrap.sh
- ./configure --prefix=/usr
- if [ "$ARCH" == "i386" ]; then ./configure --prefix=/usr --build=i686-pc-linux-gnu CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32; fi
- if [ "$ARCH" == "x86_64" ]; then ./configure --prefix=/usr; fi
- make -j$(nproc)
- sudo make install
- cd ..
@@ -44,7 +45,7 @@ after_success:
# make sure only pushes to rewrite create a new release, otherwise pretend PR and upload to transfer.sh
- if [ "$TRAVIS_BRANCH" != "master" ]; then export TRAVIS_EVENT_TYPE=pull_request; fi
- wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh
- bash upload.sh linuxdeploy*.AppImage*
- bash upload.sh linuxdeploy-"$ARCH".AppImage*
branches:
except:
+4 -7
View File
@@ -3,10 +3,10 @@ cmake_minimum_required(VERSION 3.3)
message(STATUS "Searching for libmagic")
find_library(LIBMAGIC_A libmagic.a)
find_library(LIBMAGIC_LIBRARIES magic)
if(NOT LIBMAGIC_A)
message(FATAL_ERROR "libmagic.a not found")
if(NOT LIBMAGIC_LIBRARIES)
message(FATAL_ERROR "libmagic not found")
endif()
find_path(LIBMAGIC_MAGIC_H_DIR
@@ -19,9 +19,6 @@ 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)
set_property(TARGET libmagic_static PROPERTY INTERFACE_LINK_LIBRARIES ${LIBMAGIC_LIBRARIES})
+7
View File
@@ -42,6 +42,13 @@ namespace linuxdeploy {
// the dependencies are copied to the normal destination, though
bool deployLibrary(const boost::filesystem::path& path, const boost::filesystem::path& destination = "");
// force deploy shared library
//
// works like deployLibrary, except that it doesn't check the excludelist
// this is useful to deploy libraries and their dependencies that are blacklisted and would otherwise not be deployed
// the excludelist check is only disabled for the current library, and will be enabled for the dependencies again
bool forceDeployLibrary(const boost::filesystem::path& path, const boost::filesystem::path& destination = "");
// deploy executable
bool deployExecutable(const boost::filesystem::path& path, const boost::filesystem::path& destination = "");
+9 -3
View File
@@ -2,6 +2,7 @@
#include <set>
#include <string>
#include <vector>
#include <poll.h>
// library headers
#include <boost/filesystem.hpp>
@@ -22,6 +23,7 @@ namespace linuxdeploy {
class PluginBase<API_LEVEL>::PrivateData {
public:
const boost::filesystem::path pluginPath;
std::string name;
int apiLevel;
PLUGIN_TYPE pluginType;
@@ -33,6 +35,10 @@ namespace linuxdeploy {
apiLevel = getApiLevelFromExecutable();
pluginType = getPluginTypeFromExecutable();
boost::cmatch res;
boost::regex_match(path.filename().c_str(), res, PLUGIN_EXPR);
name = res[1].str();
};
private:
@@ -128,10 +134,10 @@ namespace linuxdeploy {
}
log << std::endl;
auto process = subprocess::Popen(args);
auto process = subprocess::Popen(args, subprocess::output{stdout}, subprocess::error{stderr});
process.wait();
auto retcode = process.wait();
return retcode;
return process.retcode();
}
}
}
+6
View File
@@ -4,6 +4,7 @@
// library includes
#include <boost/filesystem.hpp>
#include <boost/regex.hpp>
// local includes
#include "linuxdeploy/core/log.h"
@@ -18,6 +19,11 @@ namespace linuxdeploy {
OUTPUT_TYPE,
};
/*
* Official regular expression to check filenames for plugins
*/
static const boost::regex PLUGIN_EXPR(R"(^linuxdeploy-plugin-([^\s\.-]+)(?:-[^\.]+)?(?:\..+)?$)");
/*
* Plugin interface.
*/
@@ -64,6 +64,14 @@ namespace linuxdeploy {
return strncmp(string.c_str(), prefix.c_str(), prefix.size()) == 0;
}
static bool stringEndsWith(const std::string& string, const std::string& suffix) {
// sanity check
if (string.size() < suffix.size())
return false;
return strcmp(string.c_str() + (string.size() - suffix.size()), suffix.c_str()) == 0;
}
static bool stringContains(const std::string& string, const std::string& part) {
return string.find(part) != std::string::npos;
}
+3
View File
@@ -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)
+1 -3
View File
@@ -1,6 +1,4 @@
# 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)
+53 -29
View File
@@ -13,7 +13,7 @@
#include "linuxdeploy/core/appdir.h"
#include "linuxdeploy/core/elf.h"
#include "linuxdeploy/core/log.h"
#include "util.h"
#include "linuxdeploy/util/util.h"
#include "excludelist.h"
using namespace linuxdeploy::core;
@@ -144,34 +144,40 @@ namespace linuxdeploy {
if (!success)
return false;
while (!stripOperations.empty()) {
const auto& filePath = *(stripOperations.begin());
if (getenv("NO_STRIP") != nullptr) {
ldLog() << LD_WARNING << "$NO_STRIP environment variable detected, not stripping binaries" << std::endl;
stripOperations.clear();
} else {
while (!stripOperations.empty()) {
const auto& filePath = *(stripOperations.begin());
if (util::stringStartsWith(elf::ElfFile(filePath).getRPath(), "$")) {
ldLog() << LD_WARNING << "Not calling strip on binary" << filePath << LD_NO_SPACE << ": rpath starts with $" << std::endl;
} else {
ldLog() << "Calling strip on library" << filePath << std::endl;
if (util::stringStartsWith(elf::ElfFile(filePath).getRPath(), "$")) {
ldLog() << LD_WARNING << "Not calling strip on binary" << filePath << LD_NO_SPACE
<< ": rpath starts with $" << std::endl;
} else {
ldLog() << "Calling strip on library" << filePath << std::endl;
std::map<std::string, std::string> env;
env.insert(std::make_pair(std::string("LC_ALL"), std::string("C")));
std::map<std::string, std::string> env;
env.insert(std::make_pair(std::string("LC_ALL"), std::string("C")));
subprocess::Popen proc(
{getStripPath().c_str(), filePath.c_str()},
subprocess::output(subprocess::PIPE),
subprocess::error(subprocess::PIPE),
subprocess::environment(env)
);
subprocess::Popen proc(
{getStripPath().c_str(), filePath.c_str()},
subprocess::output(subprocess::PIPE),
subprocess::error(subprocess::PIPE),
subprocess::environment(env)
);
std::string err = proc.communicate().second.buf.data();
std::string err = proc.communicate().second.buf.data();
if (proc.retcode() != 0 &&
!util::stringContains(err, "Not enough room for program headers")) {
ldLog() << LD_ERROR << "Strip call failed:" << err << std::endl;
success = false;
if (proc.retcode() != 0 &&
!util::stringContains(err, "Not enough room for program headers")) {
ldLog() << LD_ERROR << "Strip call failed:" << err << std::endl;
success = false;
}
}
}
stripOperations.erase(stripOperations.begin());
stripOperations.erase(stripOperations.begin());
}
}
if (!success)
@@ -327,14 +333,15 @@ namespace linuxdeploy {
return patchelfPath;
}
bool deployLibrary(const bf::path& path, int recursionLevel = 0, const bf::path& destination = "") {
bool deployLibrary(const bf::path& path, int recursionLevel = 0, bool forceDeploy = false,const bf::path &destination = bf::path()) {
auto logPrefix = getLogPrefix(recursionLevel);
if (hasBeenVisitedAlready(path)) {
if (!forceDeploy && hasBeenVisitedAlready(path)) {
ldLog() << LD_DEBUG << logPrefix << LD_NO_SPACE << "File has been visited already:" << path << std::endl;
return true;
}
static auto isInExcludelist = [&logPrefix](const bf::path& fileName) {
for (const auto& excludePattern : generatedExcludelist) {
// simple string match is faster than using fnmatch
@@ -356,7 +363,7 @@ namespace linuxdeploy {
return false;
};
if (isInExcludelist(path.filename())) {
if (!forceDeploy && isInExcludelist(path.filename())) {
ldLog() << logPrefix << LD_NO_SPACE << "Skipping deployment of blacklisted library" << path << std::endl;
// mark file as visited
@@ -372,6 +379,12 @@ namespace linuxdeploy {
auto destinationPath = destination.empty() ? appDirPath / "usr/lib/" : 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);
@@ -393,8 +406,9 @@ namespace linuxdeploy {
rpath = "$ORIGIN/" + relPath.string() + ":$ORIGIN";
}
setElfRPathOperations[destinationPath / path.filename()] = rpath;
stripOperations.insert(destinationPath / path.filename());
setElfRPathOperations[destinationPath] = rpath;
stripOperations.insert(destinationPath);
if (!deployElfDependencies(path, recursionLevel))
return false;
@@ -578,7 +592,11 @@ namespace linuxdeploy {
}
bool AppDir::deployLibrary(const bf::path& path, const bf::path& destination) {
return d->deployLibrary(path, 0, destination);
return d->deployLibrary(path, 0, false, destination);
}
bool AppDir::forceDeployLibrary(const bf::path& path, const bf::path& destination) {
return d->deployLibrary(path, 0, true, destination);
}
bool AppDir::deployExecutable(const bf::path& path, const boost::filesystem::path& destination) {
@@ -683,7 +701,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())) {
+6 -2
View File
@@ -8,7 +8,7 @@
// 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;
@@ -99,7 +99,11 @@ namespace linuxdeploy {
util::trim(libraryPath);
paths.push_back(bf::absolute(libraryPath));
} else {
ldLog() << LD_DEBUG << "Invalid ldd output: " << line << std::endl;
if (util::stringContains("not found", line)) {
ldLog() << LD_ERROR << "Could not find dependency:" << line << std::endl;
} else {
ldLog() << LD_DEBUG << "Invalid ldd output: " << line << std::endl;
}
}
}
+26 -2
View File
@@ -11,9 +11,11 @@
#include "linuxdeploy/core/elf.h"
#include "linuxdeploy/core/log.h"
#include "linuxdeploy/plugin/plugin.h"
#include "linuxdeploy/util/util.h"
using namespace linuxdeploy::core;
using namespace linuxdeploy::core::log;
using namespace linuxdeploy::util;
namespace bf = boost::filesystem;
@@ -117,7 +119,7 @@ int main(int argc, char** argv) {
return 1;
}
if (!appDir.deployLibrary(libraryPath)) {
if (!appDir.forceDeployLibrary(libraryPath)) {
std::cerr << "Failed to deploy library: " << libraryPath << std::endl;
return 1;
}
@@ -245,10 +247,32 @@ int main(int argc, char** argv) {
auto deployedDesktopFiles = appDir.deployedDesktopFiles();
desktopfile::DesktopFile desktopFile;
if (deployedDesktopFiles.empty()) {
ldLog() << LD_WARNING << "Could not find desktop file in AppDir, cannot create links for AppRun, desktop file and icon in AppDir root" << std::endl;
} else {
auto& desktopFile = deployedDesktopFiles[0];
if (!appName.Get().empty()) {
auto desktopFileMatchingName = std::find_if(
deployedDesktopFiles.begin(),
deployedDesktopFiles.end(),
[&appName](const desktopfile::DesktopFile& desktopFile) {
auto fileName = desktopFile.path().filename().string();
return stringStartsWith(fileName, appName.Get()) && stringEndsWith(fileName, ".desktop");
}
);
if (desktopFileMatchingName != deployedDesktopFiles.end()) {
desktopFile = *desktopFileMatchingName;
ldLog() << "Found desktop file matching app name:" << desktopFile.path() << std::endl;
} else {
desktopFile = deployedDesktopFiles[0];
ldLog() << LD_WARNING << "Could not find suitable desktop file for given app name" << appName << LD_NO_SPACE << ", using first desktop file found:" << desktopFile.path() << std::endl;
}
} else {
desktopFile = deployedDesktopFiles[0];
ldLog() << LD_WARNING << "App name not specified, using first desktop file found:" << desktopFile.path() << std::endl;
}
ldLog() << "Deploying desktop file:" << desktopFile.path() << std::endl;
+10 -6
View File
@@ -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;
@@ -41,14 +41,13 @@ namespace linuxdeploy {
const auto PATH = getenv("PATH");
const boost::regex expr(R"(^linuxdeploy-plugin-([^\s\.-]+)(?:-[^\.]+)?(?:\..+)?$)");
auto paths = util::split(PATH, ':');
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());
@@ -65,7 +64,7 @@ namespace linuxdeploy {
if (bf::status(*i).permissions() & (bf::owner_exe | bf::group_exe | bf::others_exe)) {
// ... and filename must match regular expression
boost::cmatch res;
if (boost::regex_match(i->path().filename().string().c_str(), res, expr)) {
if (boost::regex_match(i->path().filename().string().c_str(), res, PLUGIN_EXPR)) {
try {
auto name = res[1].str();
auto* plugin = createPluginInstance(*i);
@@ -73,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();
+7 -2
View File
@@ -1,2 +1,7 @@
add_library(linuxdeploy_util STATIC magicwrapper.cpp magicwrapper.h util.h misc.h)
target_include_directories(linuxdeploy_util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_library(linuxdeploy_util STATIC
magicwrapper.cpp
magicwrapper.h
${PROJECT_SOURCE_DIR}/include/linuxdeploy/util/util.h
${PROJECT_SOURCE_DIR}/include/linuxdeploy/util/misc.h
)
target_include_directories(linuxdeploy_util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include)
+8 -3
View File
@@ -46,15 +46,20 @@ LINUXDEPLOY_ARGS=("--init-appdir" "--appdir" "AppDir" "-e" "bin/linuxdeploy" "-i
bin/linuxdeploy "${LINUXDEPLOY_ARGS[@]}"
# bundle AppImage plugin
mkdir -p AppDir/plugins
wget https://github.com/TheAssassin/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-"$ARCH".AppImage
chmod +x linuxdeploy-plugin-appimage-"$ARCH".AppImage
mv linuxdeploy-plugin-appimage-"$ARCH".AppImage AppDir/usr/bin/
./linuxdeploy-plugin-appimage-"$ARCH".AppImage --appimage-extract
mv squashfs-root/ AppDir/plugins/linuxdeploy-plugin-appimage
ln -s ../../plugins/linuxdeploy-plugin-appimage/AppRun AppDir/usr/bin/linuxdeploy-plugin-appimage
# build AppImage using plugin
AppDir/usr/bin/linuxdeploy-plugin-appimage-"$ARCH".AppImage --appdir AppDir/
AppDir/usr/bin/linuxdeploy-plugin-appimage --appdir AppDir/
# rename AppImage to avoid "Text file busy" issues when using it to create another one
mv ./linuxdeploy*.AppImage test.AppImage
mv ./linuxdeploy-"$ARCH".AppImage test.AppImage
# verify that the resulting AppImage works
./test.AppImage "${LINUXDEPLOY_ARGS[@]}"