You've already forked linuxdeploy
mirror of
https://github.com/encounter/linuxdeploy.git
synced 2026-07-10 12:18:44 -07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b86ccd46ee |
@@ -2,4 +2,5 @@ cmake-build-*/
|
||||
*build*/
|
||||
.idea/
|
||||
squashfs-root/
|
||||
tests/
|
||||
*.AppImage
|
||||
|
||||
+6
-9
@@ -1,6 +1,12 @@
|
||||
[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
|
||||
[submodule "lib/cpp-feather-ini-parser"]
|
||||
path = lib/cpp-feather-ini-parser
|
||||
url = https://github.com/Turbine1991/cpp-feather-ini-parser.git
|
||||
[submodule "lib/boost-filesystem"]
|
||||
path = lib/boost-filesystem
|
||||
url = https://github.com/boostorg/filesystem.git
|
||||
@@ -70,12 +76,3 @@
|
||||
[submodule "lib/CImg"]
|
||||
path = lib/CImg
|
||||
url = https://github.com/dtschump/CImg.git
|
||||
[submodule "lib/googletest"]
|
||||
path = lib/googletest
|
||||
url = https://github.com/google/googletest
|
||||
[submodule "lib/boost-container"]
|
||||
path = lib/boost-container
|
||||
url = https://github.com/boostorg/container
|
||||
[submodule "lib/linuxdeploy-desktopfile"]
|
||||
path = lib/linuxdeploy-desktopfile
|
||||
url = https://github.com/linuxdeploy/linuxdeploy-desktopfile
|
||||
|
||||
+24
-50
@@ -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,48 +23,30 @@ 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
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
packages:
|
||||
- libmagic-dev
|
||||
- libjpeg-dev
|
||||
- libpng-dev
|
||||
- cimg-dev
|
||||
before_script:
|
||||
- sudo pip install gcovr
|
||||
script: travis/test-coverage.sh
|
||||
after_success: true # also, we don't intend to upload release binaries
|
||||
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
|
||||
|
||||
- name: Ubuntu xenial i386 coverage build (no binaries uploaded)
|
||||
env: ARCH=i386 BUILD_TYPE=coverage
|
||||
dist: xenial
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
packages:
|
||||
- libmagic-dev:i386
|
||||
- libjpeg-dev:i386
|
||||
- libpng-dev:i386
|
||||
- gcc-multilib
|
||||
- g++-multilib
|
||||
- libfuse2:i386
|
||||
- libcairo2:i386
|
||||
before_script:
|
||||
- sudo pip install gcovr
|
||||
script: travis/test-coverage.sh
|
||||
after_success: true # also, we don't intend to upload release binaries
|
||||
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" != "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:
|
||||
|
||||
@@ -10,44 +10,6 @@ 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")
|
||||
|
||||
# support for ccache
|
||||
# call CMake with -DUSE_CCACHE=ON to make use of it
|
||||
set(USE_CCACHE ON CACHE BOOL "")
|
||||
if(USE_CCACHE)
|
||||
find_program(CCACHE ccache)
|
||||
if(CCACHE)
|
||||
message(STATUS "Using ccache")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
|
||||
else()
|
||||
message(WARNING "USE_CCACHE set, but could not find ccache")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(ENABLE_COVERAGE OFF CACHE BOOL "Enable coverage measurements")
|
||||
if(ENABLE_COVERAGE)
|
||||
include(CodeCoverage)
|
||||
message(WARNING "Enabling code coverage measurements -> disables optimizations and embeds debug information!")
|
||||
|
||||
append_coverage_compiler_flags()
|
||||
|
||||
set(COVERAGE_GCOVR_EXCLUDES ${PROJECT_SOURCE_DIR}/lib ${PROJECT_BINARY_DIR})
|
||||
|
||||
include(ProcessorCount)
|
||||
ProcessorCount(processor_count)
|
||||
|
||||
set(command cmake --build . --target all -- -j ${processor_count} && ctest -V)
|
||||
|
||||
setup_target_for_coverage_gcovr_html(NAME coverage EXECUTABLE "${command}")
|
||||
setup_target_for_coverage_gcovr_xml(NAME coverage_xml EXECUTABLE "${command}")
|
||||
setup_target_for_coverage_gcovr_text(NAME coverage_text EXECUTABLE "${command}")
|
||||
endif()
|
||||
|
||||
add_subdirectory(lib)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
include(CTest)
|
||||
if(BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
@@ -14,22 +14,58 @@ linuxdeploy is designed to be an AppDir maintenance tool. It provides extensive
|
||||
linuxdeploy was greatly influenced by [linuxdeployqt](https://github.com/probonopd/linuxdeployqt), and while it employs stricter rules on AppDirs, it's more flexible in use. If you use linuxdeployqt at the moment, consider switching to linuxdeploy today!
|
||||
|
||||
|
||||
## User guides and examples
|
||||
## Usage
|
||||
|
||||
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).
|
||||
linuxdeploy, being a tool for creating AppDirs and eventually AppImages, is released as an AppImage itself. Please download and use the AppImage for normal use. If you encounter errors, e.g., when building the tool from source, please try with the AppImage first before [creating an issue](https://github.com/TheAssassin/linuxdeploy/issues/new).
|
||||
|
||||
There are two main ways to use linuxdeploy: Provide the paths to all files you want to bundle manually, or create an FHS-like directory and run linuxdeploy on it.
|
||||
|
||||
## Projects using linuxdeploy
|
||||
Most build systems provide some kind of `make install` functionality that is capable of producing FHS-like directory trees that can be turned into AppDirs:
|
||||
|
||||
This is an incomplete list of projects using linuxdeploy. You might want to read their build scripts to see how they use linuxdeploy.
|
||||
```sh
|
||||
# automake
|
||||
./configure --prefix=/usr
|
||||
make install DESTDIR=AppDir
|
||||
|
||||
- [Pext](https://github.com/Pext/Pext)
|
||||
- [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher)
|
||||
- [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))
|
||||
# cmake
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make install DESTDIR=AppDir
|
||||
|
||||
# qmake
|
||||
qmake CONFIG+=release PREFIX=/usr .
|
||||
make install INSTALL_ROOT=AppDir
|
||||
```
|
||||
|
||||
Now, simply run linuxdeploy like `./linuxdeploy*.AppImage --appdir AppDir`.
|
||||
|
||||
Please use `./linuxdeploy*.AppImage --help` to get a list of options supported by linuxdeploy.
|
||||
|
||||
If your build system cannot produce such install trees or you prefer to bundle everything manually, you can also specify the paths to the resources yourself. linuxdeploy provides the following options for this use case:
|
||||
|
||||
```
|
||||
-l[library...],
|
||||
--lib=[library...],
|
||||
--library=[library...] Shared library to deploy
|
||||
-e[executable...],
|
||||
--executable=[executable...] Executable to deploy
|
||||
-d[desktop file...],
|
||||
--desktop-file=[desktop file...] Desktop file to deploy
|
||||
enough for some tests
|
||||
-i[icon file...],
|
||||
--icon-file=[icon file...] Icon to deploy
|
||||
```
|
||||
|
||||
An example run could look like this:
|
||||
|
||||
```bash
|
||||
./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 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).
|
||||
|
||||
|
||||
## Plugins
|
||||
@@ -50,11 +86,14 @@ linuxdeploy currently ships with some plugins. These are likely out of date. In
|
||||
|
||||
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>`.
|
||||
|
||||
**A list of official and community plugins can be found in the [awesome-linuxdeploy](http://github.com/linuxdeploy/awesome-linuxdeploy) project.**
|
||||
|
||||
**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
|
||||
|
||||
> I bundled additional resources, but when I try to run them, either the system binary is called or the file is not found.
|
||||
|
||||
@@ -1,362 +0,0 @@
|
||||
# Copyright (c) 2012 - 2017, Lars Bilke
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# CHANGES:
|
||||
#
|
||||
# 2012-01-31, Lars Bilke
|
||||
# - Enable Code Coverage
|
||||
#
|
||||
# 2013-09-17, Joakim Söderberg
|
||||
# - Added support for Clang.
|
||||
# - Some additional usage instructions.
|
||||
#
|
||||
# 2016-02-03, Lars Bilke
|
||||
# - Refactored functions to use named parameters
|
||||
#
|
||||
# 2017-06-02, Lars Bilke
|
||||
# - Merged with modified version from github.com/ufz/ogs
|
||||
#
|
||||
#
|
||||
# USAGE:
|
||||
#
|
||||
# 1. Copy this file into your cmake modules path.
|
||||
#
|
||||
# 2. Add the following line to your CMakeLists.txt:
|
||||
# include(CodeCoverage)
|
||||
#
|
||||
# 3. Append necessary compiler flags:
|
||||
# APPEND_COVERAGE_COMPILER_FLAGS()
|
||||
#
|
||||
# 4. If you need to exclude additional directories from the report, specify them
|
||||
# using the COVERAGE_LCOV_EXCLUDES variable before calling SETUP_TARGET_FOR_COVERAGE_LCOV.
|
||||
# Example:
|
||||
# set(COVERAGE_LCOV_EXCLUDES 'dir1/*' 'dir2/*')
|
||||
#
|
||||
# 5. Use the functions described below to create a custom make target which
|
||||
# runs your test executable and produces a code coverage report.
|
||||
#
|
||||
# 6. Build a Debug build:
|
||||
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
# make
|
||||
# make my_coverage_target
|
||||
#
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
# Check prereqs
|
||||
find_program( GCOV_PATH gcov )
|
||||
find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
|
||||
find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
|
||||
find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
|
||||
find_program( SIMPLE_PYTHON_EXECUTABLE python )
|
||||
|
||||
if(NOT GCOV_PATH)
|
||||
message(FATAL_ERROR "gcov not found! Aborting...")
|
||||
endif() # NOT GCOV_PATH
|
||||
|
||||
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
|
||||
if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
|
||||
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
|
||||
endif()
|
||||
elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
|
||||
message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
|
||||
endif()
|
||||
|
||||
set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage"
|
||||
CACHE INTERNAL "")
|
||||
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the C++ compiler during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_C_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the C compiler during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used for linking binaries during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
|
||||
FORCE )
|
||||
mark_as_advanced(
|
||||
CMAKE_CXX_FLAGS_COVERAGE
|
||||
CMAKE_C_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
|
||||
endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
link_libraries(gcov)
|
||||
else()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
|
||||
endif()
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# SETUP_TARGET_FOR_COVERAGE_LCOV(
|
||||
# NAME testrunner_coverage # New target name
|
||||
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES testrunner # Dependencies to build first
|
||||
# )
|
||||
function(SETUP_TARGET_FOR_COVERAGE_LCOV)
|
||||
|
||||
set(options NONE)
|
||||
set(oneValueArgs NAME)
|
||||
set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT LCOV_PATH)
|
||||
message(FATAL_ERROR "lcov not found! Aborting...")
|
||||
endif() # NOT LCOV_PATH
|
||||
|
||||
if(NOT GENHTML_PATH)
|
||||
message(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
endif() # NOT GENHTML_PATH
|
||||
|
||||
# Setup target
|
||||
add_custom_target(${Coverage_NAME}
|
||||
|
||||
# Cleanup lcov
|
||||
COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . --zerocounters
|
||||
# Create baseline to make sure untouched files show up in the report
|
||||
COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base
|
||||
|
||||
# Run tests
|
||||
COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||
|
||||
# Capturing lcov counters and generating report
|
||||
COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info
|
||||
# add baseline counters
|
||||
COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info --output-file ${Coverage_NAME}.total
|
||||
COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${COVERAGE_LCOV_EXCLUDES} --output-file ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
|
||||
COMMAND ${GENHTML_PATH} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
|
||||
COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.total ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
|
||||
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
# Show where to find the lcov info report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
endfunction() # SETUP_TARGET_FOR_COVERAGE_LCOV
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# SETUP_TARGET_FOR_COVERAGE_GCOVR_XML(
|
||||
# NAME ctest_coverage # New target name
|
||||
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES executable_target # Dependencies to build first
|
||||
# )
|
||||
function(SETUP_TARGET_FOR_COVERAGE_GCOVR_XML)
|
||||
|
||||
set(options NONE)
|
||||
set(oneValueArgs NAME)
|
||||
set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT SIMPLE_PYTHON_EXECUTABLE)
|
||||
message(FATAL_ERROR "python not found! Aborting...")
|
||||
endif() # NOT SIMPLE_PYTHON_EXECUTABLE
|
||||
|
||||
if(NOT GCOVR_PATH)
|
||||
message(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
endif() # NOT GCOVR_PATH
|
||||
|
||||
# Combine excludes to several -e arguments
|
||||
set(GCOVR_EXCLUDES "")
|
||||
foreach(EXCLUDE ${COVERAGE_GCOVR_EXCLUDES})
|
||||
list(APPEND GCOVR_EXCLUDES "-e")
|
||||
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
|
||||
add_custom_target(${Coverage_NAME}
|
||||
# Clean old coverage data
|
||||
COMMAND find ${PROJECT_BINARY_DIR} -type f -iname '*.gcno' -delete
|
||||
|
||||
# Run tests
|
||||
COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||
|
||||
# Running gcovr
|
||||
COMMAND ${GCOVR_PATH} --xml
|
||||
-r ${PROJECT_SOURCE_DIR} ${GCOVR_EXCLUDES}
|
||||
--object-directory=${PROJECT_BINARY_DIR}
|
||||
-o ${Coverage_NAME}.xml
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
COMMENT "Running gcovr to produce Cobertura code coverage report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
|
||||
)
|
||||
|
||||
endfunction() # SETUP_TARGET_FOR_COVERAGE_GCOVR_XML
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML(
|
||||
# NAME ctest_coverage # New target name
|
||||
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES executable_target # Dependencies to build first
|
||||
# )
|
||||
function(SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML)
|
||||
|
||||
set(options NONE)
|
||||
set(oneValueArgs NAME)
|
||||
set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT SIMPLE_PYTHON_EXECUTABLE)
|
||||
message(FATAL_ERROR "python not found! Aborting...")
|
||||
endif() # NOT SIMPLE_PYTHON_EXECUTABLE
|
||||
|
||||
if(NOT GCOVR_PATH)
|
||||
message(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
endif() # NOT GCOVR_PATH
|
||||
|
||||
# Combine excludes to several -e arguments
|
||||
set(GCOVR_EXCLUDES "")
|
||||
foreach(EXCLUDE ${COVERAGE_GCOVR_EXCLUDES})
|
||||
list(APPEND GCOVR_EXCLUDES "-e")
|
||||
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
|
||||
add_custom_target(${Coverage_NAME}
|
||||
# Clean old coverage data
|
||||
COMMAND find ${PROJECT_BINARY_DIR} -type f -iname '*.gcno' -delete
|
||||
|
||||
# Run tests
|
||||
COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||
|
||||
# Create folder
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
|
||||
|
||||
# Running gcovr
|
||||
COMMAND ${GCOVR_PATH} --html --html-details
|
||||
-r ${PROJECT_SOURCE_DIR} ${GCOVR_EXCLUDES}
|
||||
--object-directory=${PROJECT_BINARY_DIR}
|
||||
-o ${Coverage_NAME}/index.html
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
COMMENT "Running gcovr to produce HTML code coverage report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
endfunction() # SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# SETUP_TARGET_FOR_COVERAGE_GCOVR_TEXT(
|
||||
# NAME ctest_coverage # New target name
|
||||
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES executable_target # Dependencies to build first
|
||||
# )
|
||||
function(SETUP_TARGET_FOR_COVERAGE_GCOVR_TEXT)
|
||||
|
||||
set(options NONE)
|
||||
set(oneValueArgs NAME)
|
||||
set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT SIMPLE_PYTHON_EXECUTABLE)
|
||||
message(FATAL_ERROR "python not found! Aborting...")
|
||||
endif() # NOT SIMPLE_PYTHON_EXECUTABLE
|
||||
|
||||
if(NOT GCOVR_PATH)
|
||||
message(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
endif() # NOT GCOVR_PATH
|
||||
|
||||
# Combine excludes to several -e arguments
|
||||
set(GCOVR_EXCLUDES "")
|
||||
foreach(EXCLUDE ${COVERAGE_GCOVR_EXCLUDES})
|
||||
list(APPEND GCOVR_EXCLUDES "-e")
|
||||
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
|
||||
add_custom_target(${Coverage_NAME}
|
||||
# Clean old coverage data
|
||||
COMMAND find ${PROJECT_BINARY_DIR} -type f -iname '*.gcno' -delete
|
||||
|
||||
# Run tests
|
||||
COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||
|
||||
# Create folder
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
|
||||
|
||||
# Running gcovr
|
||||
COMMAND ${GCOVR_PATH}
|
||||
-r ${PROJECT_SOURCE_DIR} ${GCOVR_EXCLUDES}
|
||||
--object-directory=${PROJECT_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
COMMENT "Running gcovr to produce HTML code coverage report."
|
||||
)
|
||||
|
||||
endfunction() # SETUP_TARGET_FOR_COVERAGE_GCOVR_TEXT
|
||||
|
||||
function(APPEND_COVERAGE_COMPILER_FLAGS)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
|
||||
endfunction() # APPEND_COVERAGE_COMPILER_FLAGS
|
||||
@@ -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")
|
||||
|
||||
@@ -3,6 +3,3 @@ set(CMAKE_SYSTEM_PROCESSOR i386 CACHE STRING "" FORCE)
|
||||
|
||||
set(CMAKE_C_FLAGS "-m32" CACHE STRING "" FORCE)
|
||||
set(CMAKE_CXX_FLAGS "-m32" CACHE STRING "" FORCE)
|
||||
|
||||
# https://gitlab.kitware.com/cmake/cmake/issues/16920#note_299077
|
||||
set(THREADS_PTHREAD_ARG "2" CACHE STRING "Forcibly set by CMakeLists.txt" FORCE)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// system includes
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// library includes
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// local includes
|
||||
#include "linuxdeploy/desktopfile/desktopfile.h"
|
||||
#include "linuxdeploy/core/desktopfile.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -20,7 +19,7 @@ namespace linuxdeploy {
|
||||
private:
|
||||
// private data class pattern
|
||||
class PrivateData;
|
||||
std::shared_ptr<PrivateData> d;
|
||||
PrivateData* d;
|
||||
|
||||
public:
|
||||
// default constructor
|
||||
@@ -28,18 +27,14 @@ namespace linuxdeploy {
|
||||
// the directory will be created if it doesn't exist
|
||||
explicit AppDir(const boost::filesystem::path& path);
|
||||
|
||||
// we don't want any copy/move(-assignment) behavior, therefore we delete those operators/constructors
|
||||
AppDir(const AppDir&) = delete;
|
||||
AppDir(AppDir&&) = delete;
|
||||
void operator=(const AppDir&) = delete;
|
||||
void operator=(AppDir&&) = delete;
|
||||
~AppDir();
|
||||
|
||||
// alternative constructor
|
||||
// shortcut for using a normal string instead of a path
|
||||
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,62 +52,45 @@ 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;
|
||||
void deployFile(const boost::filesystem::path& from, const boost::filesystem::path& to);
|
||||
|
||||
// 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;
|
||||
|
||||
// disable deployment of copyright files for this instance
|
||||
void setDisableCopyrightFilesDeployment(bool disable);
|
||||
bool deployDependenciesForExistingFiles();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// system includes
|
||||
|
||||
// library includes
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace desktopfile {
|
||||
/*
|
||||
* Parse and read desktop files.
|
||||
*/
|
||||
class DesktopFile {
|
||||
private:
|
||||
// private data class pattern
|
||||
class PrivateData;
|
||||
PrivateData* d;
|
||||
|
||||
public:
|
||||
// default constructor
|
||||
DesktopFile();
|
||||
|
||||
// construct from existing desktop file
|
||||
// file must exist
|
||||
explicit DesktopFile(const boost::filesystem::path& path);
|
||||
|
||||
// read desktop file
|
||||
// sets path associated with this file
|
||||
bool read(const boost::filesystem::path& path);
|
||||
|
||||
// get path associated with this file
|
||||
boost::filesystem::path path() const;
|
||||
|
||||
// sets the path associated with this desktop file
|
||||
// used to e.g., save the desktop file
|
||||
void setPath(const boost::filesystem::path& path);
|
||||
|
||||
// clear contents of desktop file
|
||||
void clear();
|
||||
|
||||
// save desktop file
|
||||
bool save() const;
|
||||
|
||||
// save desktop file to path
|
||||
// does not change path associated with desktop file
|
||||
bool save(const boost::filesystem::path& path) const;
|
||||
|
||||
// check if entry exists in given section and key
|
||||
bool entryExists(const std::string& section, const std::string& key) const;
|
||||
|
||||
// get key from desktop file
|
||||
// an std::string passed as value parameter will be populated with the contents
|
||||
// returns true (and populates value) if the key exists, false otherwise
|
||||
bool getEntry(const std::string& section, const std::string& key, std::string& value) const;
|
||||
|
||||
// add key to section in desktop file
|
||||
// the section will be created if it doesn't exist already
|
||||
// returns true if an existing key was overwritten, false otherwise
|
||||
bool setEntry(const std::string& section, const std::string& key, const std::string& value);
|
||||
|
||||
// create common application entries in desktop file
|
||||
// returns false if one of the keys exists and was left unmodified
|
||||
bool addDefaultKeys(const std::string& executableFileName);
|
||||
|
||||
// validate desktop file
|
||||
bool validate() const;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,15 @@
|
||||
#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 +45,13 @@ namespace linuxdeploy {
|
||||
|
||||
private:
|
||||
int getApiLevelFromExecutable() {
|
||||
const auto arg = "--plugin-api-version";
|
||||
|
||||
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;
|
||||
}
|
||||
auto output = subprocess::check_output({pluginPath.c_str(), "--plugin-api-version"});
|
||||
std::string stdoutOutput = output.buf.data();
|
||||
|
||||
try {
|
||||
auto apiLevel = std::stoi(stdoutOutput);
|
||||
return apiLevel;
|
||||
} catch (const std::exception&) {
|
||||
} catch (const std::invalid_argument&) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -73,8 +62,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 +75,7 @@ namespace linuxdeploy {
|
||||
else if (firstLine == "output")
|
||||
type = OUTPUT_TYPE;
|
||||
}
|
||||
} catch (const std::logic_error&) {}
|
||||
} catch (const subprocess::CalledProcessError&) {}
|
||||
|
||||
return type;
|
||||
}
|
||||
@@ -136,8 +126,90 @@ 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);
|
||||
|
||||
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;
|
||||
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;
|
||||
while (std::getline(ss, currentLine)) {
|
||||
oss << "[" << d->name << "/stderr] " << currentLine << std::endl;
|
||||
}
|
||||
linuxdeploy::core::log::ldLog() << oss.str();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
do {
|
||||
if (!printOutput()) {
|
||||
ldLog() << LD_ERROR << "Failed to communicate with process" << std::endl;
|
||||
process.kill();
|
||||
return -1;
|
||||
}
|
||||
} while (process.poll() < 0);
|
||||
|
||||
if (!printOutput()) {
|
||||
ldLog() << LD_ERROR << "Failed to communicate with process" << std::endl;
|
||||
process.kill();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return process.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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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"};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
// system headers
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
// libraries
|
||||
#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();
|
||||
@@ -91,33 +109,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 {};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// local includes
|
||||
#include "linuxdeploy/util/assert.h"
|
||||
#include "linuxdeploy/util/misc.h"
|
||||
#include "misc.h"
|
||||
|
||||
// import functions from misc module for convenience
|
||||
namespace linuxdeploy {
|
||||
namespace util {
|
||||
using namespace misc;
|
||||
using namespace assert;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user