You've already forked linuxdeploy
mirror of
https://github.com/encounter/linuxdeploy.git
synced 2026-07-10 12:18:44 -07:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c58d9716b0 | |||
| 3e84f424e7 | |||
| a386ab19fd | |||
| 4f84082155 | |||
| 8aa1bc9d89 | |||
| 56d9d6b540 | |||
| 5165301a44 | |||
| c1452fa870 | |||
| 03cae41d55 | |||
| c10eafe823 | |||
| 4a7301eba5 | |||
| a3cc38d406 | |||
| 144fd2deb0 | |||
| b96389a064 | |||
| b156ffc0cb | |||
| bd052a5b1f | |||
| f06d5e5832 | |||
| a0ace9f75c | |||
| 2b7a5928a5 | |||
| ec862d0af9 | |||
| df61b059d3 | |||
| 5baac459b3 | |||
| c783be934b | |||
| ae6dfcfe21 |
+4
-3
@@ -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:
|
||||
|
||||
@@ -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 = "");
|
||||
|
||||
|
||||
@@ -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,56 @@ namespace linuxdeploy {
|
||||
}
|
||||
log << std::endl;
|
||||
|
||||
auto process = subprocess::Popen(args);
|
||||
auto process = subprocess::Popen(args, subprocess::output{subprocess::PIPE}, subprocess::error{subprocess::PIPE});
|
||||
|
||||
auto retcode = process.wait();
|
||||
return retcode;
|
||||
|
||||
std::vector<pollfd> pfds(2);
|
||||
auto* opfd = &pfds[0];
|
||||
auto* epfd = &pfds[1];
|
||||
|
||||
opfd->fd = fileno(process.output());
|
||||
opfd->events = POLLIN;
|
||||
|
||||
epfd->fd = fileno(process.error());
|
||||
epfd->events = POLLIN;
|
||||
|
||||
auto printOutput = [&pfds, opfd, epfd, this, &process]() {
|
||||
poll(pfds.data(), pfds.size(), -1);
|
||||
|
||||
if (opfd->revents & POLLIN) {
|
||||
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();
|
||||
}
|
||||
linuxdeploy::core::log::ldLog() << oss.str();
|
||||
}
|
||||
|
||||
if (epfd->revents & POLLIN) {
|
||||
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();
|
||||
}
|
||||
linuxdeploy::core::log::ldLog() << oss.str();
|
||||
}
|
||||
};
|
||||
|
||||
do {
|
||||
printOutput();
|
||||
} while (process.poll() < 0);
|
||||
|
||||
printOutput();
|
||||
|
||||
return process.retcode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
+45
-27
@@ -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) {
|
||||
|
||||
+5
-1
@@ -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
@@ -11,9 +11,11 @@
|
||||
#include "linuxdeploy/core/elf.h"
|
||||
#include "linuxdeploy/core/log.h"
|
||||
#include "linuxdeploy/plugin/plugin.h"
|
||||
#include "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;
|
||||
|
||||
|
||||
@@ -41,8 +41,6 @@ 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();
|
||||
@@ -65,7 +63,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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+17
-3
@@ -46,15 +46,29 @@ 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
|
||||
|
||||
# bundle Qt plugin
|
||||
|
||||
wget https://github.com/TheAssassin/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-"$ARCH".AppImage
|
||||
chmod +x linuxdeploy-plugin-qt-"$ARCH".AppImage
|
||||
./linuxdeploy-plugin-qt-"$ARCH".AppImage --appimage-extract
|
||||
mv squashfs-root/ AppDir/plugins/linuxdeploy-plugin-qt
|
||||
|
||||
ln -s ../../plugins/linuxdeploy-plugin-qt/AppRun AppDir/usr/bin/linuxdeploy-plugin-qt
|
||||
|
||||
# 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[@]}"
|
||||
|
||||
Reference in New Issue
Block a user