Support for AppImage.

develop
Roman Telezhynskyi 2024-02-10 15:53:02 +02:00
parent 63292fa235
commit 1ac5a4167f
30 changed files with 238 additions and 985 deletions

View File

@ -111,6 +111,34 @@ linux_qt5_qbs_task_template: &LINUX_QT5_QBS_TASK_TEMPLATE
- qbs -p autotest-runner -d build profile:qt5 config:release
- ccache -s
linuxdeployqt_task_template: &LINUXDEPLOYQT_TASK_TEMPLATE
- bash -c "$PACKAGE_MANAGER_INSTALL qt515base qt515svg qt515tools qt515xmlpatterns qt515translations qt515doc qt515imageformats poppler-utils git xvfb ccache build-essential"
build_script:
- uname -a
- mkdir -pm 0700 $XDG_RUNTIME_DIR
- Xvfb $DISPLAY -ac -screen 0 1600x1200x24+32 -nolisten tcp -nolisten unix &
- sleep 1
- ccache --set-config sloppiness=pch_defines,time_macros max_size="$CCACHE_SIZE"
- echo $PATH
- which qmake
- which qbs
- pwd
- ls -l /usr/bin/ | grep -E "${COMPILER_REGEX}"
- ${COMPILER} --version
- qmake --version
- qbs --version
- qbs setup-toolchains /usr/bin/${COMPILER} ${COMPILER}
- qbs setup-qt /opt/qt515/bin/qmake qt5
- qbs config defaultProfile qt5
- qbs config profiles.qt5.baseProfile ${COMPILER}
- qbs build -f valentina.qbs -d $CIRRUS_WORKING_DIR/build --jobs $(nproc) profile:qt5 config:release modules.buildconfig.enableCcache:${ENABLE_CCACHE} modules.cpp.linkerVariant:mold qbs.installRoot:$CIRRUS_WORKING_DIR/build/install-root modules.buildconfig.enableAppImage:true
- qbs -p autotest-runner -d build profile:qt5 config:release
- linuxdeployqt $CIRRUS_WORKING_DIR/build/install-root/usr/local/share/applications/ua.com.smart-pattern.valentina.desktop -appimage -qmake='/opt/qt515/bin/qmake' -executable-dir=$CIRRUS_WORKING_DIR/build/install-root/usr/local/bin -extra-plugins=iconengines,platformthemes/libqgtk3.so
- ccache -s
deploy_script:
- pwd
- $CIRRUS_WORKING_DIR/scripts/appimage-deploy.sh
linux_task:
<< : *REGULER_TASK_TEMPLATE
env:
@ -174,6 +202,28 @@ linux_task:
container:
cpu: 2
memory: 8G # Set to 8GB to avoid OOM. https://cirrus-ci.org/guide/linux/#linux-container
- container:
image: dismine/linuxdeployqt:latest
env:
COMPILER: gcc
QMAKE_SPEC: "linux-g++"
GCC_COLORS: 'error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
COMPILER_REGEX: "gcc|g\\+\\+"
matrix:
- name: 'AppImage'
<< : *LINUXDEPLOYQT_TASK_TEMPLATE
env:
ACCESS_TOKEN: ENCRYPTED[81e0b2381ffb628b73f5c94f834010e6631191e0ad03cdd0850d440fb2737a74b68131d842030f010c1bf73ab4cdc1ae]
DEPLOY: "true"
QT_SELECT: "qt5"
ENABLE_CCACHE: true
VERSION: "continuous"
QT_VERSION: Qt6
ARCH: x86_64
TARGET_PLATFORM: "Linux"
container:
cpu: 4
memory: 8G # Set to 8GB to avoid OOM. https://cirrus-ci.org/guide/linux/#linux-container
- container:
image: dismine/gcc-ubuntu:latest
env:

View File

@ -2,7 +2,7 @@
Type=Application
Name=Puzzle
Exec=puzzle %F
Version=1.1
Version=1.0
GenericName=Valentina's layout editor
GenericName[uk]=Редактор розкладки Валентини
Comment=Create and edit layouts
@ -12,5 +12,5 @@ Terminal=false
MimeType=application/x-valentina-layout;
Categories=Qt;Utility;FileTools;
TryExec=puzzle
Keywords=layout;nesting
Keywords=layout;nesting;
StartupWMClass=puzzle

View File

@ -2,7 +2,7 @@
Type=Application
Name=Tape
Exec=tape %F
Version=1.1
Version=1.0
GenericName=Valentina's measurements editor
GenericName[uk]=Редактор мірок Валентини
Comment=Create and edit measurements
@ -12,5 +12,5 @@ Terminal=false
MimeType=application/x-valentina-s-measurements;application/x-valentina-i-measurements;application/x-valentina-k-measurements
Categories=Qt;Utility;FileTools;
TryExec=tape
Keywords=measurements
Keywords=measurements;
StartupWMClass=tape

View File

@ -2,7 +2,7 @@
Type=Application
Name=Valentina
Exec=valentina %F
Version=1.1
Version=1.0
GenericName=Pattern making program
GenericName[uk]=Програма створення викрійок
Comment=Creating pattern of clothes
@ -10,7 +10,7 @@ Comment[uk]=Створеня викрійок одягу
Icon=valentina
Terminal=false
MimeType=application/x-valentina-pattern;
Categories=Graphics;VectorGraphics;2DGraphics;Qt;Engineering;
Categories=Graphics;VectorGraphics;2DGraphics;Qt;Engineering;FileTools;
TryExec=valentina
Keywords=pattern
Keywords=pattern;
StartupWMClass=valentina

View File

@ -33,21 +33,6 @@ VApp {
cpp.dynamicLibraries: ["icudata", "icui18n", "icuuc"]
}
Group {
name: "freedesktop"
prefix: project.sourceDirectory + "/dist/"
files: [
"ua.com.smart-pattern." + product.targetName + ".desktop"
]
}
freedesktop2.desktopKeys: ({
'Exec': FileInfo.joinPaths(qbs.installPrefix,
product.installDir,
product.targetName) + ' %F',
'X-Application-Version': product.version,
})
Group {
name: "Translations"
condition: product.primaryApp || (qbs.targetOS.contains("macos") && (!bundle.isBundle || (bundle.isBundle && buildconfig.enableMultiBundle)))

View File

@ -132,6 +132,7 @@ Module {
if (enableAppImage && qbs.targetOS.contains("unix") && !qbs.targetOS.contains("macos"))
defines.push('APPIMAGE');
defines.push('APPIMAGE_QT_TRANSLATIONS="' + FileInfo.joinPaths(qbs.installPrefix, "translations") + '"');
if (enableMultiBundle)
defines.push('MULTI_BUNDLE');

34
scripts/appimage-deploy.sh Executable file
View File

@ -0,0 +1,34 @@
print_error() {
echo "[CI] ERROR: $1"
}
print_info() {
echo "[CI] INFO: $1"
}
check_failure() {
if [ $? -ne 0 ] ; then
if [ -z $1 ] ; then
print_error $1
else
print_error "Failure exit code is detected."
fi
exit 1
fi
}
if [[ "$DEPLOY" == "true" ]]; then
print_info "Start cleaning.";
python3 $CIRRUS_WORKING_DIR/scripts/deploy.py clean $ACCESS_TOKEN;
check_failure "Unable to clean stale artifacts.";
print_info "Start uploading.";
python3 $CIRRUS_WORKING_DIR/scripts/deploy.py upload $ACCESS_TOKEN $CIRRUS_WORKING_DIR/Valentina-continuous-x86_64.AppImage "/0.7.x/Linux/valentina-${TARGET_PLATFORM}-${ARCH}-${CIRRUS_BRANCH}-${CIRRUS_CHANGE_IN_REPO}.AppImage";
check_failure "Unable to upload Valentina's AppImage.";
print_info "Successfully uploaded.";
else
print_info "No deployment needed.";
fi

View File

@ -141,7 +141,8 @@ def run_clean(refresh_token):
r'^valentina-macOS_10.13\+-Qt.*-x64-develop-[a-f0-9]{40}\.dmg$',
r'^valentina-macOS_10.13\+-Qt.*-x64-develop-multibundle-[a-f0-9]{40}\.dmg$',
r'^valentina-macOS.*\+-Qt.*-arm.*-develop-[a-f0-9]{40}\.dmg$',
r'^valentina-macOS.*\+-Qt.*-arm.*-develop-multibundle-[a-f0-9]{40}\.dmg$']
r'^valentina-macOS.*\+-Qt.*-arm.*-develop-multibundle-[a-f0-9]{40}\.dmg$',
r'^valentina-Linux-x86_64-develop-[a-f0-9]{40}\.AppImage$']
item_types = {}

View File

@ -346,8 +346,13 @@ auto VPPiece::StickyPosition(qreal &dx, qreal &dy) const -> bool
return false;
}
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wnoexcept")
VStickyDistance match;
QT_WARNING_POP
if (!StickySheet(match))
{
return false;

View File

@ -31,11 +31,6 @@
#include "vpapplication.h"
#if defined(APPIMAGE) && defined(Q_OS_LINUX)
#include "../vmisc/appimage.h"
#include <QScopeGuard>
#endif // defined(APPIMAGE) && defined(Q_OS_LINUX)
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <xercesc/util/PlatformUtils.hpp>
#endif
@ -71,12 +66,6 @@
auto main(int argc, char *argv[]) -> int
{
#if defined(APPIMAGE) && defined(Q_OS_LINUX)
/* Fix path to ICU_DATA when run AppImage.*/
char *exe_dir = IcuDataPath("/../share/icu");
auto FreeMemory = qScopeGuard([exe_dir] { free(exe_dir); });
#endif // defined(APPIMAGE) && defined(Q_OS_LINUX)
Q_INIT_RESOURCE(puzzleicon); // NOLINT
Q_INIT_RESOURCE(icon); // NOLINT
Q_INIT_RESOURCE(schema); // NOLINT
@ -123,12 +112,5 @@ auto main(int argc, char *argv[]) -> int
QTimer::singleShot(0, &app, &VPApplication::ProcessCMD);
#if defined(APPIMAGE) && defined(Q_OS_LINUX)
if (exe_dir)
{
qDebug() << "Path to ICU folder:" << exe_dir;
}
#endif // defined(APPIMAGE) && defined(Q_OS_LINUX)
return VPApplication::exec();
}

View File

@ -337,6 +337,22 @@ VToolApp {
fileTags:["svg_fonts"]
}
Group {
name: "freedesktop"
condition: !buildconfig.enableAppImage && qbs.targetOS.contains("unix") && !qbs.targetOS.contains("macos")
prefix: project.sourceDirectory + "/dist/"
files: [
"ua.com.smart-pattern." + product.targetName + ".desktop"
]
}
freedesktop2.desktopKeys: ({
'Exec': FileInfo.joinPaths(qbs.installPrefix,
product.installDir,
product.targetName) + ' %F',
'X-Application-Version': product.version,
})
Rule {
condition: product.qbs.targetOS.contains("macos") && product.buildconfig.enableMultiBundle
inputs: ["svg_fonts"]

View File

@ -31,11 +31,6 @@
#include <QMessageBox> // For QT_REQUIRE_VERSION
#include <QTimer>
#if defined(APPIMAGE) && defined(Q_OS_LINUX)
#include "../vmisc/appimage.h"
#include <QScopeGuard>
#endif // defined(APPIMAGE) && defined(Q_OS_LINUX)
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <xercesc/util/PlatformUtils.hpp>
#endif
@ -71,12 +66,6 @@
auto main(int argc, char *argv[]) -> int
{
#if defined(APPIMAGE) && defined(Q_OS_LINUX)
/* Fix path to ICU_DATA when run AppImage.*/
char *exe_dir = IcuDataPath("/../share/icu");
auto FreeMemory = qScopeGuard([exe_dir] { free(exe_dir); });
#endif // defined(APPIMAGE) && defined(Q_OS_LINUX)
Q_INIT_RESOURCE(tapeicon); // NOLINT
Q_INIT_RESOURCE(icon); // NOLINT
Q_INIT_RESOURCE(schema); // NOLINT
@ -121,12 +110,5 @@ auto main(int argc, char *argv[]) -> int
QTimer::singleShot(0, &app, &MApplication::ProcessCMD);
#if defined(APPIMAGE) && defined(Q_OS_LINUX)
if (exe_dir)
{
qDebug() << "Path to ICU folder:" << exe_dir;
}
#endif // defined(APPIMAGE) && defined(Q_OS_LINUX)
return MApplication::exec();
}

View File

@ -74,10 +74,6 @@
#define BUILD_REVISION VCS_REPO_STATE_REVISION
#endif
#if defined(APPIMAGE) && defined(Q_OS_LINUX)
#include "../vmisc/appimage.h"
#endif // defined(APPIMAGE) && defined(Q_OS_LINUX)
#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
#include "../vmisc/compatibility.h"
#endif

View File

@ -245,4 +245,20 @@ VToolApp {
"k-measurements.iconset"
]
}
Group {
name: "freedesktop"
condition: !buildconfig.enableAppImage && qbs.targetOS.contains("unix") && !qbs.targetOS.contains("macos")
prefix: project.sourceDirectory + "/dist/"
files: [
"ua.com.smart-pattern." + product.targetName + ".desktop"
]
}
freedesktop2.desktopKeys: ({
'Exec': FileInfo.joinPaths(qbs.installPrefix,
product.installDir,
product.targetName) + ' %F',
'X-Application-Version': product.version,
})
}

View File

@ -35,11 +35,6 @@
#include <QMessageBox> // For QT_REQUIRE_VERSION
#include <QTimer>
#if defined(APPIMAGE) && defined(Q_OS_LINUX)
#include "../vmisc/appimage.h"
#include <QScopeGuard>
#endif // defined(APPIMAGE) && defined(Q_OS_LINUX)
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <xercesc/util/PlatformUtils.hpp>
#endif
@ -77,12 +72,6 @@
auto main(int argc, char *argv[]) -> int
{
#if defined(APPIMAGE) && defined(Q_OS_LINUX)
/* Fix path to ICU_DATA when run AppImage.*/
char *exe_dir = IcuDataPath("/../share/icu");
auto FreeMemory = qScopeGuard([exe_dir] { free(exe_dir); });
#endif // defined(APPIMAGE) && defined(Q_OS_LINUX)
Q_INIT_RESOURCE(cursor); // NOLINT
Q_INIT_RESOURCE(toolcursor); // NOLINT
Q_INIT_RESOURCE(icon); // NOLINT
@ -158,12 +147,5 @@ auto main(int argc, char *argv[]) -> int
QTimer::singleShot(msec, &w, &MainWindow::ProcessCMD);
#if defined(APPIMAGE) && defined(Q_OS_LINUX)
if (exe_dir)
{
qDebug() << "Path to ICU folder:" << exe_dir;
}
#endif // defined(APPIMAGE) && defined(Q_OS_LINUX)
return VApplication::exec();
}

View File

@ -261,10 +261,33 @@ VToolApp {
name: "freedesktop"
prefix: project.sourceDirectory + "/dist/"
files: [
"ua.com.smart-pattern." + product.targetName + ".metainfo.xml"
"ua.com.smart-pattern." + product.targetName + ".metainfo.xml",
"ua.com.smart-pattern." + product.targetName + ".desktop"
]
}
freedesktop2.desktopKeys: {
var desktopKeys = {
'Exec': FileInfo.joinPaths(qbs.installPrefix,
product.installDir,
product.targetName) + ' %F',
'X-Application-Version': product.version,
};
if (buildconfig.enableAppImage) {
var mimeTypes = [
'application/x-valentina-pattern',
'application/x-valentina-s-measurements',
'application/x-valentina-i-measurements',
'application/x-valentina-k-measurements',
'application/x-valentina-layout'
];
desktopKeys['MimeType'] = mimeTypes.join(';');
}
return (desktopKeys);
}
Group {
name: "48x48/apps"
prefix: project.sourceDirectory + "/share/icons/48x48/apps/"

View File

@ -223,9 +223,13 @@ auto VFoldLine::FoldLineMarkPoints() const -> QVector<QVector<QPointF>>
//---------------------------------------------------------------------------------------------------------------------
auto VFoldLine::LabelPosition(bool &ok) const -> FoldLabelPosData
{
FoldLabelPosData posData;
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wnoexcept")
FoldLabelPosData posData;
std::unique_ptr<TextPosData> data;
QT_WARNING_POP
if (m_type == FoldLineType::Text)
{
data = std::make_unique<TextPosData>(TextData());

View File

@ -585,11 +585,16 @@ auto operator>>(QDataStream &dataStream, VLayoutPiece &piece) -> QDataStream &
}
//---------------------------------------------------------------------------------------------------------------------
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wnoexcept")
VLayoutPiece::VLayoutPiece()
: d(new VLayoutPieceData)
{
}
QT_WARNING_POP
//---------------------------------------------------------------------------------------------------------------------
COPY_CONSTRUCTOR_IMPL_2(VLayoutPiece, VAbstractPiece)

View File

@ -46,12 +46,7 @@ QT_WARNING_DISABLE_CLANG("-Wnon-virtual-dtor")
class VSAPoint final : public VLayoutPoint
{
public:
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wnoexcept")
Q_DECL_CONSTEXPR VSAPoint() = default;
QT_WARNING_POP
Q_DECL_CONSTEXPR VSAPoint() noexcept;
Q_DECL_CONSTEXPR VSAPoint(qreal xpos, qreal ypos);
Q_DECL_CONSTEXPR explicit VSAPoint(QPointF p);
@ -114,6 +109,11 @@ private:
Q_DECLARE_METATYPE(VSAPoint) // NOLINT
Q_DECLARE_TYPEINFO(VSAPoint, Q_MOVABLE_TYPE); // NOLINT
//---------------------------------------------------------------------------------------------------------------------
Q_DECL_CONSTEXPR inline VSAPoint::VSAPoint() noexcept // NOLINT(hicpp-use-equals-default)
{
}
//---------------------------------------------------------------------------------------------------------------------
Q_DECL_CONSTEXPR inline VSAPoint::VSAPoint(qreal xpos, qreal ypos)
: VLayoutPoint(xpos, ypos)

View File

@ -27,45 +27,15 @@
*************************************************************************/
#include "appimage.h"
#include <stdlib.h>
#include <unicode/putil.h>
#include <QString>
#include <QCoreApplication>
#include <cstring>
#include <QString>
#include <QVector>
extern "C" {
#include "binreloc.h"
}
#include "../vmisc/def.h"
#include <unicode/putil.h>
#include "compatibility.h"
#include "def.h"
//---------------------------------------------------------------------------------------------------------------------
/* When deploying with AppImage based on OpenSuse, the ICU library has a hardcoded path to the icudt*.dat file.
* This prevents the library from using shared in memory data. There are few ways to resolve this issue. According
* to documentation we can either use ICU_DATA environment variable or the function u_setDataDirectory().
*/
char* IcuDataPath(const char* correction)
{
char * data_path = nullptr;
BrInitError error;
if (br_init (&error))
{
char *path = br_find_exe_dir(nullptr);
if (path)
{
data_path = static_cast<char *> (malloc(strlen(path) + strlen(correction) + 1));
if(data_path)
{
strcpy(data_path, path);
strcat(data_path, correction);
u_setDataDirectory(data_path);
}
free(path);
}
}
return data_path;
}
using namespace Qt::Literals::StringLiterals;
//---------------------------------------------------------------------------------------------------------------------
/**
@ -89,7 +59,7 @@ QString AppImageRoot(const QString &applicationDir, const QString &defaultAppDir
if (appSub.isEmpty() || defaultSub.isEmpty() || appSub.size() <= defaultSub.size())
{
return QString();
return {};
}
appSub = Reverse(appSub);
@ -99,12 +69,12 @@ QString AppImageRoot(const QString &applicationDir, const QString &defaultAppDir
{
if (defaultSub.at(i) != appSub.at(i))
{
return QString();
return {};
}
}
QStringList rootSub = appSub.mid(defaultSub.size());
rootSub = Reverse(rootSub);
return '/' + rootSub.join('/');
return '/'_L1 + rootSub.join('/');
}

View File

@ -30,8 +30,6 @@
class QString;
char* IcuDataPath(const char* correction);
QString AppImageRoot();
QString AppImageRoot(const QString &applicationDir, const QString &defaultAppDir);

View File

@ -1,758 +0,0 @@
/*
* BinReloc - a library for creating relocatable executables
*
* Originally written by: Hongli Lai <h.lai@chello.nl>
* http://autopackage.org/ (defunct)
*
* Maintained by: Wolfgang 'datenwolf' Draxinger <coding@datenwolf.net>
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/limits.h>
#include "binreloc.h"
#ifndef SSIZE_MAX
#define SSIZE_MAX ((ssize_t)(SIZE_MAX>>1))
#endif
/** @internal
* Find the canonical filename of the executable. Returns the filename
* (which must be freed) or NULL on error. If the parameter 'error' is
* not NULL, the error code will be stored there, if an error occured.
*/
static char *
_br_find_exe (BrInitError *error)
{
char *path, *path2, *line, *result;
size_t buf_size;
struct stat stat_buf;
FILE *f;
/* Read from /proc/self/exe (symlink) */
if (sizeof (path) > SSIZE_MAX)
buf_size = SSIZE_MAX - 1;
else
buf_size = PATH_MAX - 1;
path = (char *) malloc (buf_size);
if (path == NULL) {
/* Cannot allocate memory. */
if (error)
*error = BR_INIT_ERROR_NOMEM;
return NULL;
}
path2 = (char *) malloc (buf_size);
if (path2 == NULL) {
/* Cannot allocate memory. */
if (error)
*error = BR_INIT_ERROR_NOMEM;
free (path);
return NULL;
}
strncpy (path2, "/proc/self/exe", buf_size - 1);
while (1) {
int i;
ssize_t size = readlink (path2, path, buf_size - 1);
if (size == -1) {
/* Error. */
free (path2);
break;
}
/* readlink() success. */
path[size] = '\0';
/* Check whether the symlink's target is also a symlink.
* We want to get the final target. */
i = stat (path, &stat_buf);
if (i == -1) {
/* Error. */
free (path2);
break;
}
/* stat() success. */
if (!S_ISLNK (stat_buf.st_mode)) {
/* path is not a symlink. Done. */
free (path2);
return path;
}
/* path is a symlink. Continue loop and resolve this. */
strncpy (path, path2, buf_size - 1);
}
/* readlink() or stat() failed; this can happen when the program is
* running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
buf_size = PATH_MAX + 128;
line = (char *) realloc (path, buf_size);
if (line == NULL) {
/* Cannot allocate memory. */
free (path);
if (error)
*error = BR_INIT_ERROR_NOMEM;
return NULL;
}
f = fopen ("/proc/self/maps", "r");
if (f == NULL) {
free (line);
if (error)
*error = BR_INIT_ERROR_OPEN_MAPS;
return NULL;
}
/* The first entry should be the executable name. */
result = fgets (line, (int) buf_size, f);
if (result == NULL) {
fclose (f);
free (line);
if (error)
*error = BR_INIT_ERROR_READ_MAPS;
return NULL;
}
/* Get rid of newline character. */
buf_size = strlen (line);
if (buf_size == 0) {
/* Huh? An empty string? */
fclose (f);
free (line);
if (error)
*error = BR_INIT_ERROR_INVALID_MAPS;
return NULL;
}
if (line[buf_size - 1] == 10)
line[buf_size - 1] = 0;
/* Extract the filename; it is always an absolute path. */
path = strchr (line, '/');
/* Sanity check. */
if (strstr (line, " r-xp ") == NULL || path == NULL) {
fclose (f);
free (line);
if (error)
*error = BR_INIT_ERROR_INVALID_MAPS;
return NULL;
}
path = strdup (path);
free (line);
fclose (f);
return path;
}
/** @internal
* Find the canonical filename of the executable which owns symbol.
* Returns a filename which must be freed, or NULL on error.
*/
static char *
_br_find_exe_for_symbol (const void *symbol, BrInitError *error)
{
#define SIZE PATH_MAX + 100
FILE *f;
size_t address_string_len;
char *address_string, line[SIZE], *found;
if (symbol == NULL)
return (char *) NULL;
f = fopen ("/proc/self/maps", "r");
if (f == NULL)
return (char *) NULL;
address_string_len = 4;
address_string = (char *) malloc (address_string_len);
found = (char *) NULL;
while (!feof (f)) {
char *start_addr, *end_addr, *end_addr_end, *file;
void *start_addr_p, *end_addr_p;
size_t len;
if (fgets (line, SIZE, f) == NULL)
break;
/* Sanity check. */
if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
continue;
/* Parse line. */
start_addr = line;
end_addr = strchr (line, '-');
file = strchr (line, '/');
/* More sanity check. */
if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
continue;
end_addr[0] = '\0';
end_addr++;
end_addr_end = strchr (end_addr, ' ');
if (end_addr_end == NULL)
continue;
end_addr_end[0] = '\0';
len = strlen (file);
if (len == 0)
continue;
if (file[len - 1] == '\n')
file[len - 1] = '\0';
/* Get rid of "(deleted)" from the filename. */
len = strlen (file);
if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
file[len - 10] = '\0';
/* I don't know whether this can happen but better safe than sorry. */
len = strlen (start_addr);
if (len != strlen (end_addr))
continue;
/* Transform the addresses into a string in the form of 0xdeadbeef,
* then transform that into a pointer. */
if (address_string_len < len + 3) {
address_string_len = len + 3;
char *new_address_string = (char *) realloc (address_string, address_string_len);
if (new_address_string == NULL){
*error = BR_INIT_ERROR_NOMEM;
free (address_string);
fclose (f);
return (char *) NULL;
}
else
address_string = new_address_string;
}
memcpy (address_string, "0x", 2);
memcpy (address_string + 2, start_addr, len);
address_string[2 + len] = '\0';
sscanf (address_string, "%p", &start_addr_p);
memcpy (address_string, "0x", 2);
memcpy (address_string + 2, end_addr, len);
address_string[2 + len] = '\0';
sscanf (address_string, "%p", &end_addr_p);
if (symbol >= start_addr_p && symbol < end_addr_p) {
found = file;
break;
}
}
free (address_string);
fclose (f);
if (found == NULL)
return (char *) NULL;
else
return strdup (found);
}
#ifndef BINRELOC_RUNNING_DOXYGEN
#undef NULL
#define NULL ((void *) 0) /* typecasted as char* for C++ type safeness */
#endif
static char *exe = (char *) NULL;
/** Initialize the BinReloc library (for applications).
*
* This function must be called before using any other BinReloc functions.
* It attempts to locate the application's canonical filename.
*
* @note If you want to use BinReloc for a library, then you should call
* br_init_lib() instead.
*
* @param error If BinReloc failed to initialize, then the error code will
* be stored in this variable. Set to NULL if you want to
* ignore this. See #BrInitError for a list of error codes.
*
* @returns 1 on success, 0 if BinReloc failed to initialize.
*/
int
br_init (BrInitError *error)
{
exe = _br_find_exe (error);
return exe != NULL;
}
/** Initialize the BinReloc library (for libraries).
*
* This function must be called before using any other BinReloc functions.
* It attempts to locate the calling library's canonical filename.
*
* @note The BinReloc source code MUST be included in your library, or this
* function won't work correctly.
*
* @param error If BinReloc failed to initialize, then the error code will
* be stored in this variable. Set to NULL if you want to
* ignore this. See #BrInitError for a list of error codes.
*
* @returns 1 on success, 0 if a filename cannot be found.
*/
int
br_init_lib (BrInitError *error)
{
exe = _br_find_exe_for_symbol ((const void *) "", error);
return exe != NULL;
}
/** Find the canonical filename of the current application.
*
* @param default_exe A default filename which will be used as fallback.
* @returns A string containing the application's canonical filename,
* which must be freed when no longer necessary. If BinReloc is
* not initialized, or if br_init() failed, then a copy of
* default_exe will be returned. If default_exe is NULL, then
* NULL will be returned.
*/
char *
br_find_exe (const char *default_exe)
{
if (exe == (char *) NULL) {
/* BinReloc is not initialized. */
if (default_exe != (const char *) NULL)
return strdup (default_exe);
else
return (char *) NULL;
}
return strdup (exe);
}
/** Locate the directory in which the current application is installed.
*
* The prefix is generated by the following pseudo-code evaluation:
* \code
* dirname(exename)
* \endcode
*
* @param default_dir A default directory which will used as fallback.
* @return A string containing the directory, which must be freed when no
* longer necessary. If BinReloc is not initialized, or if the
* initialization function failed, then a copy of default_dir
* will be returned. If default_dir is NULL, then NULL will be
* returned.
*/
char *
br_find_exe_dir (const char *default_dir)
{
if (exe == NULL) {
/* BinReloc not initialized. */
if (default_dir != NULL)
return strdup (default_dir);
else
return NULL;
}
return br_dirname (exe);
}
/** Locate the prefix in which the current application is installed.
*
* The prefix is generated by the following pseudo-code evaluation:
* \code
* dirname(dirname(exename))
* \endcode
*
* @param default_prefix A default prefix which will used as fallback.
* @return A string containing the prefix, which must be freed when no
* longer necessary. If BinReloc is not initialized, or if
* the initialization function failed, then a copy of default_prefix
* will be returned. If default_prefix is NULL, then NULL will be returned.
*/
char *
br_find_prefix (const char *default_prefix)
{
char *dir1, *dir2;
if (exe == (char *) NULL) {
/* BinReloc not initialized. */
if (default_prefix != (const char *) NULL)
return strdup (default_prefix);
else
return (char *) NULL;
}
dir1 = br_dirname (exe);
dir2 = br_dirname (dir1);
free (dir1);
return dir2;
}
/** Locate the application's binary folder.
*
* The path is generated by the following pseudo-code evaluation:
* \code
* prefix + "/bin"
* \endcode
*
* @param default_bin_dir A default path which will used as fallback.
* @return A string containing the bin folder's path, which must be freed when
* no longer necessary. If BinReloc is not initialized, or if
* the initialization function failed, then a copy of default_bin_dir will
* be returned. If default_bin_dir is NULL, then NULL will be returned.
*/
char *
br_find_bin_dir (const char *default_bin_dir)
{
char *prefix, *dir;
prefix = br_find_prefix ((const char *) NULL);
if (prefix == (char *) NULL) {
/* BinReloc not initialized. */
if (default_bin_dir != (const char *) NULL)
return strdup (default_bin_dir);
else
return (char *) NULL;
}
dir = br_build_path (prefix, "bin");
free (prefix);
return dir;
}
/** Locate the application's superuser binary folder.
*
* The path is generated by the following pseudo-code evaluation:
* \code
* prefix + "/sbin"
* \endcode
*
* @param default_sbin_dir A default path which will used as fallback.
* @return A string containing the sbin folder's path, which must be freed when
* no longer necessary. If BinReloc is not initialized, or if the
* initialization function failed, then a copy of default_sbin_dir will
* be returned. If default_bin_dir is NULL, then NULL will be returned.
*/
char *
br_find_sbin_dir (const char *default_sbin_dir)
{
char *prefix, *dir;
prefix = br_find_prefix ((const char *) NULL);
if (prefix == (char *) NULL) {
/* BinReloc not initialized. */
if (default_sbin_dir != (const char *) NULL)
return strdup (default_sbin_dir);
else
return (char *) NULL;
}
dir = br_build_path (prefix, "sbin");
free (prefix);
return dir;
}
/** Locate the application's data folder.
*
* The path is generated by the following pseudo-code evaluation:
* \code
* prefix + "/share"
* \endcode
*
* @param default_data_dir A default path which will used as fallback.
* @return A string containing the data folder's path, which must be freed when
* no longer necessary. If BinReloc is not initialized, or if the
* initialization function failed, then a copy of default_data_dir
* will be returned. If default_data_dir is NULL, then NULL will be
* returned.
*/
char *
br_find_data_dir (const char *default_data_dir)
{
char *prefix, *dir;
prefix = br_find_prefix ((const char *) NULL);
if (prefix == (char *) NULL) {
/* BinReloc not initialized. */
if (default_data_dir != (const char *) NULL)
return strdup (default_data_dir);
else
return (char *) NULL;
}
dir = br_build_path (prefix, "share");
free (prefix);
return dir;
}
/** Locate the application's localization folder.
*
* The path is generated by the following pseudo-code evaluation:
* \code
* prefix + "/share/locale"
* \endcode
*
* @param default_locale_dir A default path which will used as fallback.
* @return A string containing the localization folder's path, which must be freed when
* no longer necessary. If BinReloc is not initialized, or if the
* initialization function failed, then a copy of default_locale_dir will be returned.
* If default_locale_dir is NULL, then NULL will be returned.
*/
char *
br_find_locale_dir (const char *default_locale_dir)
{
char *data_dir, *dir;
data_dir = br_find_data_dir ((const char *) NULL);
if (data_dir == (char *) NULL) {
/* BinReloc not initialized. */
if (default_locale_dir != (const char *) NULL)
return strdup (default_locale_dir);
else
return (char *) NULL;
}
dir = br_build_path (data_dir, "locale");
free (data_dir);
return dir;
}
/** Locate the application's library folder.
*
* The path is generated by the following pseudo-code evaluation:
* \code
* prefix + "/lib"
* \endcode
*
* @param default_lib_dir A default path which will used as fallback.
* @return A string containing the library folder's path, which must be freed when
* no longer necessary. If BinReloc is not initialized, or if the initialization
* function failed, then a copy of default_lib_dir will be returned.
* If default_lib_dir is NULL, then NULL will be returned.
*/
char *
br_find_lib_dir (const char *default_lib_dir)
{
char *prefix, *dir;
prefix = br_find_prefix ((const char *) NULL);
if (prefix == (char *) NULL) {
/* BinReloc not initialized. */
if (default_lib_dir != (const char *) NULL)
return strdup (default_lib_dir);
else
return (char *) NULL;
}
dir = br_build_path (prefix, "lib");
free (prefix);
return dir;
}
/** Locate the application's libexec folder.
*
* The path is generated by the following pseudo-code evaluation:
* \code
* prefix + "/libexec"
* \endcode
*
* @param default_libexec_dir A default path which will used as fallback.
* @return A string containing the libexec folder's path, which must be freed when
* no longer necessary. If BinReloc is not initialized, or if the initialization
* function failed, then a copy of default_libexec_dir will be returned.
* If default_libexec_dir is NULL, then NULL will be returned.
*/
char *
br_find_libexec_dir (const char *default_libexec_dir)
{
char *prefix, *dir;
prefix = br_find_prefix ((const char *) NULL);
if (prefix == (char *) NULL) {
/* BinReloc not initialized. */
if (default_libexec_dir != (const char *) NULL)
return strdup (default_libexec_dir);
else
return (char *) NULL;
}
dir = br_build_path (prefix, "libexec");
free (prefix);
return dir;
}
/** Locate the application's configuration files folder.
*
* The path is generated by the following pseudo-code evaluation:
* \code
* prefix + "/etc"
* \endcode
*
* @param default_etc_dir A default path which will used as fallback.
* @return A string containing the etc folder's path, which must be freed when
* no longer necessary. If BinReloc is not initialized, or if the initialization
* function failed, then a copy of default_etc_dir will be returned.
* If default_etc_dir is NULL, then NULL will be returned.
*/
char *
br_find_etc_dir (const char *default_etc_dir)
{
char *prefix, *dir;
prefix = br_find_prefix ((const char *) NULL);
if (prefix == (char *) NULL) {
/* BinReloc not initialized. */
if (default_etc_dir != (const char *) NULL)
return strdup (default_etc_dir);
else
return (char *) NULL;
}
dir = br_build_path (prefix, "etc");
free (prefix);
return dir;
}
/***********************
* Utility functions
***********************/
/** Concatenate str1 and str2 to a newly allocated string.
*
* @param str1 A string.
* @param str2 Another string.
* @returns A newly-allocated string. This string should be freed when no longer needed.
*/
char *
br_strcat (const char *str1, const char *str2)
{
char *result;
size_t len1, len2;
if (str1 == NULL)
str1 = "";
if (str2 == NULL)
str2 = "";
len1 = strlen (str1);
len2 = strlen (str2);
result = (char *) malloc (len1 + len2 + 1);
memcpy (result, str1, len1);
memcpy (result + len1, str2, len2);
result[len1 + len2] = '\0';
return result;
}
char *
br_build_path (const char *dir, const char *file)
{
char *result;
size_t len;
len = strlen (dir);
if (len > 0 && dir[len - 1] != '/') {
char *dir2 = br_strcat (dir, "/");
result = br_strcat (dir2, file);
free (dir2);
} else
result = br_strcat (dir, file);
return result;
}
/* Emulates glibc's strndup() */
static char *
br_strndup (const char *str, size_t size)
{
char *result = (char *) NULL;
size_t len;
if (str == (const char *) NULL)
return (char *) NULL;
len = strlen (str);
if (len == 0)
return strdup ("");
if (size > len)
size = len;
result = (char *) malloc (len + 1);
memcpy (result, str, size);
result[size] = '\0';
return result;
}
/** Extracts the directory component of a path.
*
* Similar to g_dirname() or the dirname commandline application.
*
* Example:
* \code
* br_dirname ("/usr/local/foobar"); --> Returns: "/usr/local"
* \endcode
*
* @param path A path.
* @returns A directory name. This string should be freed when no longer needed.
*/
char *
br_dirname (const char *path)
{
char *end, *result;
if (path == (const char *) NULL)
return (char *) NULL;
end = strrchr (path, '/');
if (end == (const char *) NULL)
return strdup (".");
while (end > path && *end == '/')
end--;
result = br_strndup (path, (size_t)(end - path + 1));
if (result[0] == 0) {
free (result);
return strdup ("/");
} else
return result;
}

View File

@ -1,89 +0,0 @@
/*
* BinReloc - a library for creating relocatable executables
*
* Originally written by: Hongli Lai <h.lai@chello.nl>
* http://autopackage.org/ (defunct)
*
* Maintained by: Wolfgang 'datenwolf' Draxinger <coding@datenwolf.net>
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*/
#ifndef __BINRELOC_H__
#define __BINRELOC_H__
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/** These error codes can be returned by br_init(), br_init_lib(), gbr_init() or gbr_init_lib(). */
typedef enum {
/** Cannot allocate memory. */
BR_INIT_ERROR_NOMEM,
/** Unable to open /proc/self/maps; see errno for details. */
BR_INIT_ERROR_OPEN_MAPS,
/** Unable to read from /proc/self/maps; see errno for details. */
BR_INIT_ERROR_READ_MAPS,
/** The file format of /proc/self/maps is invalid; kernel bug? */
BR_INIT_ERROR_INVALID_MAPS,
/** BinReloc is disabled (the ENABLE_BINRELOC macro is not defined). */
BR_INIT_ERROR_DISABLED
} BrInitError;
#ifndef BINRELOC_RUNNING_DOXYGEN
/* Mangle symbol names to avoid symbol collisions with other ELF objects. */
#define br_init SbCJ22537442193159_br_init
#define br_init_lib SbCJ22537442193159_br_init_lib
#define br_find_exe SbCJ22537442193159_br_find_exe
#define br_find_exe_dir SbCJ22537442193159_br_find_exe_dir
#define br_find_prefix SbCJ22537442193159_br_find_prefix
#define br_find_bin_dir SbCJ22537442193159_br_find_bin_dir