2 Commits

Author SHA1 Message Date
TheAssassin 6a952b57f9 Make custom AppRun files executable
This eliminates the need for users to make the files executable
before running linuxdeploy.
2019-06-28 21:40:14 +02:00
TheAssassin 8fbe191d7a Fix APPIMAGE_EXTRACT_AND_RUN support for plugins, mk. 1 2019-06-28 20:18:51 +02:00
55 changed files with 706 additions and 1820 deletions
+3
View File
@@ -1,3 +1,6 @@
[submodule "lib/cpp-subprocess"]
path = lib/cpp-subprocess
url = https://github.com/arun11299/cpp-subprocess.git
[submodule "lib/args"]
path = lib/args
url = https://github.com/Taywee/args.git
+36 -25
View File
@@ -1,10 +1,9 @@
language: cpp
sudo: required
matrix:
include:
- name: Ubuntu bionic x86_64 release build
env: ARCH=x86_64
dist: bionic
- env: ARCH=x86_64
addons:
apt:
update: true
@@ -13,15 +12,8 @@ matrix:
- libjpeg-dev
- libpng-dev
- cimg-dev
script: travis/build.sh
after_success:
- if [ "$TRAVIS_BRANCH" != "$TRAVIS_TAG" ] && [ "$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-"$ARCH".AppImage*
- name: Ubuntu bionic i386 build
env: ARCH=i386
dist: bionic
- automake # required for patchelf
- env: ARCH=i386
addons:
apt:
update: true
@@ -31,17 +23,10 @@ matrix:
- libpng-dev:i386
- gcc-multilib
- g++-multilib
- automake # required for patchelf
- libfuse2:i386
- libcairo2:i386
script: travis/build.sh
after_success:
- if [ "$TRAVIS_BRANCH" != "$TRAVIS_TAG" ] && [ "$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-"$ARCH".AppImage*
- name: Ubuntu xenial x86_64 coverage build (no binaries uploaded)
env: ARCH=x86_64 BUILD_TYPE=coverage
dist: xenial
- env: ARCH=x86_64 BUILD_TYPE=coverage
addons:
apt:
update: true
@@ -54,10 +39,7 @@ matrix:
- sudo pip install gcovr
script: travis/test-coverage.sh
after_success: true # also, we don't intend to upload release binaries
- name: Ubuntu xenial i386 coverage build (no binaries uploaded)
env: ARCH=i386 BUILD_TYPE=coverage
dist: xenial
- env: ARCH=i386 BUILD_TYPE=coverage
addons:
apt:
update: true
@@ -73,6 +55,35 @@ matrix:
- sudo pip install gcovr
script: travis/test-coverage.sh
after_success: true # also, we don't intend to upload release binaries
- env: ARCH=x86_64 BUILD_TYPE=centos6
services:
- docker
script: travis/build-centos6-docker.sh
after_success:
- if [ "$TRAVIS_BRANCH" != "$TRAVIS_TAG" ] && [ "$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-centos6-"$ARCH".AppImage*
install:
- git clone https://github.com/NixOS/patchelf.git -b 0.8
- cd patchelf
- ./bootstrap.sh
- 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 ..
- rm -rf patchelf
script:
- travis/build.sh
after_success:
- ls -lh
# make sure only pushes to rewrite create a new release, otherwise pretend PR and upload to transfer.sh
- if [ "$TRAVIS_BRANCH" != "$TRAVIS_TAG" ] && [ "$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-"$ARCH".AppImage*
branches:
except:
+5
View File
@@ -10,6 +10,11 @@ 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")
set(USE_SYSTEM_CIMG ON CACHE BOOL "Set to OFF to use CImg library bundled in lib directory")
if (EXISTS "${PROJECT_SOURCE_DIR}/lib/cpp-subprocess/subprocess.hpp")
else()
message (FATAL_ERROR "Missing submodule(s), please 'git submodule update --init --recursive'...")
endif()
# support for ccache
# call CMake with -DUSE_CCACHE=ON to make use of it
set(USE_CCACHE ON CACHE BOOL "")
+1 -3
View File
@@ -16,7 +16,7 @@ linuxdeploy was greatly influenced by [linuxdeployqt](https://github.com/probono
## User guides and examples
Please see the [linuxdeploy user guide](https://docs.appimage.org/packaging-guide/from-source/linuxdeploy-user-guide.html) and the [native binaries packaging guide](https://docs.appimage.org/packaging-guide/from-source/native-binaries.html) in the [AppImage documentation](https://docs.appimage.org). There's also an [examples section](https://docs.appimage.org/packaging-guide/from-source/native-binaries.html#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).
## Projects using linuxdeploy
@@ -28,8 +28,6 @@ This is an incomplete list of projects using linuxdeploy. You might want to read
- [OpenRCT2](https://github.com/OpenRCT2/OpenRCT2)
- [AppImageUpdate](https://github.com/AppImage/AppImageUpdate)
- [appimaged](https://github.com/AppImage/appimaged)
- [MediaElch](https://github.com/Komet/MediaElch/)
- [CPU-X](https://github.com/X0rg/CPU-X) (details available [here](https://github.com/AppImage/AppImageKit/wiki/Bundling-GTK3-apps#fully-automated-deployment-from-sources-with-github-actions-cpu-x))
## Plugins
+5 -41
View File
@@ -1,35 +1,8 @@
# required for PNG imported target
cmake_minimum_required(VERSION 3.6)
cmake_minimum_required(VERSION 3.5)
find_package(PkgConfig)
pkg_check_modules(libpng REQUIRED IMPORTED_TARGET libpng)
# build custom static version of libjpeg for static builds
if(STATIC_BUILD)
include(ExternalProject)
if($ENV{ARCH} MATCHES "i[356]86")
set(configure_command_prefix env CFLAGS=-m32 CXXFLAGS=-m32)
endif()
ExternalProject_Add(libjpeg_static_extproj
URL https://www.ijg.org/files/jpegsrc.v9d.tar.gz
URL_HASH SHA256=99cb50e48a4556bc571dadd27931955ff458aae32f68c4d9c39d624693f69c32
BUILD_IN_SOURCE ON
EXCLUDE_FROM_ALL ON
CONFIGURE_COMMAND ${configure_command_prefix} ./configure --prefix=/usr
INSTALL_COMMAND ""
)
ExternalProject_Get_property(libjpeg_static_extproj SOURCE_DIR)
add_library(libjpeg_static INTERFACE IMPORTED)
set_property(TARGET libjpeg_static PROPERTY INTERFACE_LINK_LIBRARIES ${SOURCE_DIR}/.libs/libjpeg.a)
add_dependencies(libjpeg_static libjpeg_static_extproj)
set(JPEG_LIBRARIES libjpeg_static)
else()
find_package(JPEG REQUIRED)
endif()
find_package(PNG REQUIRED)
find_package(JPEG REQUIRED)
if(NOT USE_SYSTEM_CIMG)
message(STATUS "Using bundled CImg library")
@@ -49,16 +22,7 @@ else()
endif()
endif()
set(PNG_INCLUDE_DIR ${libpng_INCLUDE_DIRS})
if(NOT STATIC_BUILD)
set(PNG_LIBRARY ${libpng_LIBRARIES})
else()
set(PNG_LIBRARY ${libpng_STATIC_LIBRARIES})
endif()
#message(FATAL_ERROR "${PNG_LIBRARY}")
add_library(CImg INTERFACE)
set_property(TARGET CImg PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CIMG_H_DIR};${PNG_INCLUDE_DIR};${JPEG_INCLUDE_DIR}")
set_property(TARGET CImg PROPERTY INTERFACE_LINK_LIBRARIES "${PNG_LIBRARY};${JPEG_LIBRARIES}")
set_property(TARGET CImg PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CIMG_H_DIR};${JPEG_INCLUDE_DIR}")
set_property(TARGET CImg PROPERTY INTERFACE_LINK_LIBRARIES "PNG::PNG;${JPEG_LIBRARIES}")
set_property(TARGET CImg PROPERTY INTERFACE_COMPILE_DEFINITIONS "cimg_display=0;cimg_use_png=1;cimg_use_jpeg=1")
+10 -21
View File
@@ -39,7 +39,7 @@ namespace linuxdeploy {
explicit AppDir(const std::string& path);
// creates basic directory structure of an AppDir in "FHS" mode
bool createBasicStructure() const;
bool createBasicStructure();
// deploy shared library
//
@@ -57,59 +57,48 @@ namespace linuxdeploy {
// deploy executable
bool deployExecutable(const boost::filesystem::path& path, const boost::filesystem::path& destination = "");
// deploy dependencies for ELF file in AppDir, without copying it into the library dir
//
// the dependencies end up in the regular location
bool deployDependenciesOnlyForElfFile(const boost::filesystem::path& elfFilePath, bool failSilentForNonElfFile = false);
// deploy desktop file
bool deployDesktopFile(const desktopfile::DesktopFile& desktopFile);
// deploy icon
bool deployIcon(const boost::filesystem::path& path);
// deploy icon, changing its name to <target filename>.<ext>
bool deployIcon(const boost::filesystem::path& path, const std::string& targetFilename);
// deploy arbitrary file
boost::filesystem::path deployFile(const boost::filesystem::path& from, const boost::filesystem::path& to);
// copy arbitrary file (immediately)
bool copyFile(const boost::filesystem::path& from, const boost::filesystem::path& to, bool overwrite = false) const;
// create an <AppDir> relative symlink to <target> at <symlink>.
bool createRelativeSymlink(const boost::filesystem::path& target, const boost::filesystem::path& symlink) const;
bool createRelativeSymlink(const boost::filesystem::path& target, const boost::filesystem::path& symlink);
// execute deferred copy operations
bool executeDeferredOperations();
// return path to AppDir
boost::filesystem::path path() const;
boost::filesystem::path path();
// create a list of all icon paths in the AppDir
std::vector<boost::filesystem::path> deployedIconPaths() const;
std::vector<boost::filesystem::path> deployedIconPaths();
// create a list of all executable paths in the AppDir
std::vector<boost::filesystem::path> deployedExecutablePaths() const;
std::vector<boost::filesystem::path> deployedExecutablePaths();
// create a list of all desktop file paths in the AppDir
std::vector<desktopfile::DesktopFile> deployedDesktopFiles() const;
std::vector<desktopfile::DesktopFile> deployedDesktopFiles();
// create symlinks for AppRun, desktop file and icon in the AppDir root directory
bool setUpAppDirRoot(const desktopfile::DesktopFile& desktopFile, boost::filesystem::path customAppRunPath = "");
bool createLinksInAppDirRoot(const desktopfile::DesktopFile& desktopFile, boost::filesystem::path customAppRunPath = "");
// 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() const;
std::vector<boost::filesystem::path> listExecutables();
// list all shared libraries in <AppDir>/usr/lib
// this function recursively searches the entire lib directory for shared libraries
std::vector<boost::filesystem::path> listSharedLibraries() const;
std::vector<boost::filesystem::path> listSharedLibraries();
// search for executables and libraries and deploy their dependencies
// calling this function can turn sure file trees created by make install commands into working
// AppDirs
bool deployDependenciesForExistingFiles() const;
bool deployDependenciesForExistingFiles();
// disable deployment of copyright files for this instance
void setDisableCopyrightFilesDeployment(bool disable);
-2
View File
@@ -65,8 +65,6 @@ namespace linuxdeploy {
ldLog operator<<(stdEndlType strm);
ldLog operator<<(const LD_LOGLEVEL logLevel);
ldLog operator<<(const LD_STREAM_CONTROL streamControl);
void write(const char* s, const size_t n);
};
}
}
+149 -21
View File
@@ -2,19 +2,16 @@
#include <set>
#include <string>
#include <vector>
#include <fcntl.h>
#include <poll.h>
// library headers
#include <boost/filesystem.hpp>
#include <fnmatch.h>
#include <thread>
#include <subprocess.hpp>
// local headers
#include "linuxdeploy/core/log.h"
#include "linuxdeploy/util/util.h"
#include "linuxdeploy/subprocess/process.h"
#include "linuxdeploy/plugin/plugin_process_handler.h"
#pragma once
@@ -49,20 +46,19 @@ namespace linuxdeploy {
private:
int getApiLevelFromExecutable() {
const auto arg = "--plugin-api-version";
auto output = subprocess::check_output({pluginPath.c_str(), "--plugin-api-version"});
std::string stdoutOutput = output.buf.data();
const subprocess::subprocess proc({pluginPath.string(), arg});
const auto stdoutOutput = proc.check_output();
if (stdoutOutput.empty()) {
ldLog() << LD_WARNING << "received empty response from plugin" << pluginPath << "while trying to fetch data for" << "--plugin-api-version" << std::endl;
return -1;
}
// only interpret last line (fixes issues with --appimage-extract-and-run, where the runtime
// prints all the paths it extracts out to stdout)
const auto& lines = util::splitLines(stdoutOutput);
const auto& lastLine = lines.back();
try {
auto apiLevel = std::stoi(stdoutOutput);
return apiLevel;
} catch (const std::exception&) {
const auto pluginApiLevel = std::stoi(lastLine);
return pluginApiLevel;
} catch (const std::invalid_argument&) {
ldLog() << LD_DEBUG << "Failed to understand plugin response:" << lastLine << std::endl;
return -1;
}
}
@@ -73,8 +69,9 @@ namespace linuxdeploy {
// check whether plugin implements --plugin-type
try {
const subprocess::subprocess proc({pluginPath.c_str(), "--plugin-type"});
const auto stdoutOutput = proc.check_output();
auto output = subprocess::check_output({pluginPath.c_str(), "--plugin-type"});
std::string stdoutOutput = output.buf.data();
// the specification requires a single line, but we'll silently accept more than that, too
if (std::count(stdoutOutput.begin(), stdoutOutput.end(), '\n') >= 1) {
@@ -85,7 +82,7 @@ namespace linuxdeploy {
else if (firstLine == "output")
type = OUTPUT_TYPE;
}
} catch (const std::logic_error&) {}
} catch (const subprocess::CalledProcessError&) {}
return type;
}
@@ -97,7 +94,12 @@ namespace linuxdeploy {
if (d->apiLevel != API_LEVEL) {
std::stringstream msg;
msg << "This class only supports API level " << API_LEVEL << ", not " << d->apiLevel;
if (d->apiLevel < 0) {
msg << "Could not read plugin API level";
} else {
msg << "This class only supports API level " << API_LEVEL << ", not " << d->apiLevel;
}
throw WrongApiLevelError(msg.str());
}
}
@@ -136,8 +138,134 @@ namespace linuxdeploy {
template<int API_LEVEL>
int PluginBase<API_LEVEL>::run(const boost::filesystem::path& appDirPath) {
plugin_process_handler handler(d->name, path());
return handler.run(appDirPath);
auto pluginPath = path();
auto args = {pluginPath.c_str(), "--appdir", appDirPath.string().c_str()};
auto log = ldLog();
log << "Running process:";
for (const auto& arg : args) {
log << "" << arg;
}
log << std::endl;
auto process = subprocess::Popen(args, subprocess::output{subprocess::PIPE}, subprocess::error{subprocess::PIPE});
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;
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);
auto printUntilLastLine = [this](std::vector<char>& buf, size_t& bufSize, const std::string& streamType) {
std::ostringstream oss;
while (true) {
const auto firstLineFeed = std::find(buf.begin(), buf.end(), '\n');
const auto firstCarriageReturn = std::find(buf.begin(), buf.end(), '\r');
if (firstLineFeed == buf.end() && firstCarriageReturn == buf.end())
break;
const auto endOfLine = std::min(firstLineFeed, firstCarriageReturn);
std::string line(buf.begin(), endOfLine+1);
oss << "[" << d->name << "/" << streamType << "] " << line;
bufSize -= std::distance(buf.begin(), endOfLine+1);
buf.erase(buf.begin(), endOfLine+1);
}
auto messages = oss.str();
if (!messages.empty())
ldLog() << messages;
};
std::vector<char> stdoutBuf(16 * 1024);
std::vector<char> stderrBuf(16 * 1024);
size_t stdoutBufSize = 0;
size_t stderrBufSize = 0;
if (opfd->revents & POLLIN) {
if (stdoutBufSize >= stdoutBuf.size())
throw std::runtime_error("Buffer overflow");
while (true) {
auto bytesRead = fread(
reinterpret_cast<void*>(stdoutBuf.data() + stdoutBufSize),
sizeof(char),
static_cast<size_t>(stdoutBuf.size() - stdoutBufSize),
process.output()
);
if (bytesRead == 0)
break;
stdoutBufSize += bytesRead;
printUntilLastLine(stdoutBuf, stdoutBufSize, "stdout");
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
if (epfd->revents & POLLIN) {
if (stderrBufSize >= stderrBuf.size())
throw std::runtime_error("Buffer overflow");
while (true) {
auto bytesRead = fread(
reinterpret_cast<void*>(stderrBuf.data() + stderrBufSize),
sizeof(char),
static_cast<size_t>(stderrBuf.size() - stderrBufSize),
process.error()
);
if (bytesRead == 0)
break;
stderrBufSize += bytesRead;
printUntilLastLine(stderrBuf, stderrBufSize, "stderr");
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
return true;
};
int retcode;
do {
if (!printOutput()) {
ldLog() << LD_ERROR << "Failed to communicate with process" << std::endl;
process.kill();
return -1;
}
} while ((retcode = process.poll()) < 0);
if (!printOutput()) {
ldLog() << LD_ERROR << "Failed to communicate with process" << std::endl;
process.kill();
return -1;
}
return retcode;
}
}
}
@@ -1,19 +0,0 @@
#pragma once
// library headers
#include <boost/filesystem/path.hpp>
namespace linuxdeploy {
namespace plugin {
class plugin_process_handler {
private:
std::string name_;
boost::filesystem::path path_;
public:
plugin_process_handler(std::string name, boost::filesystem::path path);
int run(const boost::filesystem::path& appDir) const;
};
}
}
@@ -1,36 +0,0 @@
#pragma once
#include <string>
#include <set>
#include <vector>
#include <poll.h>
/**
* Reads from a pipe when data is available, and hands data to registered callbacks.
*/
class pipe_reader {
private:
const int pipe_fd_;
public:
/**
* Construct new instance from pipe file descriptor.
* @param pipe_fd file descriptor for pipe we will read from (e.g., a subprocess's stdout, stderr pipes)
*/
explicit pipe_reader(int pipe_fd);
/**
* Read from pipe file descriptor and store data in buffer.
* This method is non-blocking, i.e., if there is no data to read, it returns immediately.
* Otherwise, it will read until either of the following conditions is met:
*
* - no more data left in the pipe to be read
* - buffer is completely filled
*
* On errors, a subprocess_error is thrown.
*
* @param buffer buffer to store read data into
* @returns amount of characters read from the pipe
*/
size_t read(std::vector<std::string::value_type>& buffer) const;
};
-86
View File
@@ -1,86 +0,0 @@
// system headers
#include <unordered_map>
#include <vector>
#include <signal.h>
// local headers
#include "linuxdeploy/subprocess/subprocess.h"
namespace linuxdeploy {
namespace subprocess {
class process {
private:
// child process ID
int child_pid_ = -1;
// pipes to child process's stdout/stderr
int stdout_fd_ = -1;
int stderr_fd_ = -1;
// process exited
bool exited_ = false;
// exit code -- will be initialized by close()
int exit_code_ = -1;
// these constants help make the pipe code more readable
static constexpr int READ_END_ = 0, WRITE_END_ = 1;
static std::vector<char*> make_args_vector_(const std::vector<std::string>& args);
static std::vector<char*> make_env_vector_(const subprocess_env_map_t& env);
static int check_waitpid_status_(int status);
public:
/**
* Create a child process.
* @param args parameters for process
* @param env additional environment variables (current environment will be copied)
*/
process(std::initializer_list<std::string> args, const subprocess_env_map_t& env);
/**
* Create a child process.
* @param args parameters for process
* @param env additional environment variables (current environment will be copied)
*/
process(const std::vector<std::string>& args, const subprocess_env_map_t& env);
~process();
/**
* @return child process's ID
*/
int pid() const;
/**
* @return child process's stdout file descriptor ID
*/
int stdout_fd() const;
/**
* @return child process's stderr file descriptor ID
*/
int stderr_fd() const;
/**
* Close all pipes and wait for process to exit.
* If process is not running any more, just returns exit code.
* @return child process's exit code
*/
int close();
/**
* Kill underlying process with given signal. By default, SIGTERM is used to end the process.
*/
void kill(int signal = SIGTERM) const;
/**
* Check whether process is still alive. Use close() to fetch exit code.
* @return true while process is alive, false otherwise
*/
bool is_running();
};
}
}
@@ -1,32 +0,0 @@
#pragma once
// system headers
#include <cstdio>
#include <unordered_map>
#include <string>
#include <utility>
#include <vector>
// local headers
#include "subprocess_result.h"
namespace linuxdeploy {
namespace subprocess {
typedef std::unordered_map<std::string, std::string> subprocess_env_map_t;
class subprocess {
private:
std::vector<std::string> args_{};
std::unordered_map<std::string, std::string> env_{};
public:
subprocess(std::initializer_list<std::string> args, subprocess_env_map_t env = {});
explicit subprocess(std::vector<std::string> args, subprocess_env_map_t env = {});
subprocess_result run() const;
std::string check_output() const;
};
}
}
@@ -1,37 +0,0 @@
#pragma once
// system headers
#include <cstdlib>
#include <string>
#include <utility>
#include <vector>
namespace linuxdeploy {
namespace subprocess {
typedef std::vector<std::string::value_type> subprocess_result_buffer_t;
/**
* Result of subprocess execution. Follows Value Object design pattern.
*/
class subprocess_result {
private:
int exit_code_;
subprocess_result_buffer_t stdout_contents_;
subprocess_result_buffer_t stderr_contents_;
public:
subprocess_result(int exit_code, subprocess_result_buffer_t stdout_contents,
subprocess_result_buffer_t stderr_contents);
int exit_code() const;
const subprocess_result_buffer_t& stdout_contents() const;
std::string stdout_string() const;
const subprocess_result_buffer_t& stderr_contents() const;
std::string stderr_string() const;
};
}
}
-17
View File
@@ -1,17 +0,0 @@
#pragma once
// system headers
#include <stdexcept>
namespace linuxdeploy {
namespace util {
namespace assert {
template<typename T>
void assert_not_empty(T container) {
if (container.empty()) {
throw std::invalid_argument{"container must not be empty"};
}
}
}
}
}
-31
View File
@@ -1,6 +1,5 @@
#pragma once
// system headers
#include <algorithm>
#include <climits>
#include <cstring>
@@ -9,9 +8,6 @@
#include <unistd.h>
#include <vector>
// libraries
#include <boost/filesystem.hpp>
namespace linuxdeploy {
namespace util {
namespace misc {
@@ -91,33 +87,6 @@ namespace linuxdeploy {
return buf.data();
}
// very simple but for our purposes good enough which like algorithm to find binaries in $PATH
static boost::filesystem::path which(const std::string& name) {
const auto* path = getenv("PATH");
namespace bf = boost::filesystem;
if (path == nullptr)
return "";
for (const auto& binDir : split(path, ':')) {
if (!bf::is_directory(binDir)) {
continue;
}
for (bf::directory_iterator it(binDir); it != bf::directory_iterator{}; ++it) {
const auto binary = it->path();
if (binary.filename() == name) {
// TODO: check if file is executable (skip otherwise)
return binary;
}
}
}
return {};
};
}
}
}
+19
View File
@@ -0,0 +1,19 @@
/**
* Wrapper for cpp-subprocess. Provides additional convenience functions.
*/
#pragma once
// library includes
#include <subprocess.hpp>
namespace linuxdeploy {
namespace util {
namespace subprocess {
using namespace ::subprocess;
// Reads output channels of existing Popen object into buffers and returns the contents
std::pair<std::string, std::string> check_output_error(Popen& proc);
}
}
}
+2 -3
View File
@@ -1,11 +1,10 @@
// local includes
#include "linuxdeploy/util/assert.h"
#include "linuxdeploy/util/misc.h"
#include "misc.h"
#include "subprocess.h"
// import functions from misc module for convenience
namespace linuxdeploy {
namespace util {
using namespace misc;
using namespace assert;
}
}
+5
View File
@@ -1,5 +1,10 @@
include(CTest)
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)
target_include_directories(args INTERFACE args)
Submodule lib/cpp-subprocess added at 05c76a5311

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