Support for AppRun hooks installed by plugins, mk. 1

This commit is contained in:
TheAssassin
2019-08-14 04:13:30 +02:00
parent 5bc8840003
commit 58845cee8f

View File

@@ -15,6 +15,9 @@ namespace linuxdeploy {
class AppDirRootSetup::Private {
public:
static constexpr auto APPRUN_HOOKS_DIRNAME = "apprun-hooks";
public:
const AppDir& appDir;
@@ -137,6 +140,94 @@ namespace linuxdeploy {
}
}
}
bool deployAppRunWrapperIfNecessary() const {
const bf::path appRunPath(appDir.path() / "AppRun");
const bf::path wrappedAppRunPath(appRunPath.string() + ".wrapped");
const bf::path appRunHooksPath(appDir.path() / APPRUN_HOOKS_DIRNAME);
// first, we check whether there's that special directory containing hooks
if (!bf::is_directory(appRunHooksPath)) {
ldLog() << LD_DEBUG << "Could not find apprun-hooks dir, no need to deploy the AppRun wrapper" << std::endl;
return true;
}
// if there's no files in there we don't have to do anything
bf::directory_iterator firstRegularFile = std::find_if(
bf::directory_iterator(appRunHooksPath),
bf::directory_iterator{},
[](const bf::path& p) {
return bf::is_regular_file(p);
}
);
if (firstRegularFile == bf::directory_iterator{}) {
ldLog() << LD_WARNING << "Found an empty apprun-hooks directory, assuming there is no need to deploy the AppRun wrapper" << std::endl;
return true;
}
// any file within that directory is considered to be a script
// we can't perform any validity checks, that would be way too much complexity and even tools which
// claim they can, like e.g., shellcheck, aren't perfect, they only aid in avoiding bugs but cannot
// prevent them completely
// let's put together the wrapper script's contents
std::ostringstream oss;
oss << "#! /usr/bin/env bash" << std::endl
<< std::endl
<< "# autogenerated by linuxdeploy" << std::endl
<< std::endl
<< "export APPDIR=${APPDIR:-$(readlink -f $(dirname \"$0\"))}" << std::endl
<< std::endl;
std::for_each(bf::directory_iterator(appRunHooksPath), bf::directory_iterator{}, [&oss](const bf::path& p) {
if (!bf::is_regular_file(p))
return;
oss << "source \"$APPDIR\"/" << APPRUN_HOOKS_DIRNAME << "/" << p.filename();
});
oss << std::endl
<< "exec \"$APPDIR\"/AppRun.wrapped" << std::endl;
// first we need to make sure we're not running this more than once
// this might cause more harm than good
// we require the user to clean up the mess at first
// FIXME: try to find a way how to rewrap AppRun on subsequent runs or, even better, become idempotent
if (bf::exists(wrappedAppRunPath)) {
ldLog() << LD_WARNING << "Already found wrapped AppRun, using existing file/symlink" << std::endl;
}
// in case the above check triggered a warning, it's possible that there is another AppRun in the AppDir
// this one has to be cleaned up in that case
if (bf::exists(appRunPath)) {
ldLog() << LD_WARNING << "Found an AppRun file/symlink, possibly due to re-run of linuxdeploy, "
"overwriting" << std::endl;
bf::remove(appRunPath);
}
// backup original AppRun
bf::rename(appRunPath, wrappedAppRunPath);
// install new script
std::ofstream ofs(appRunPath.string());
ofs << oss.str();
// make sure data is written to disk
ofs.flush();
ofs.close();
// make new file executable
bf::permissions(appRunPath,
bf::perms::owner_all | bf::perms::group_read | bf::perms::others_read |
bf::perms::group_exe | bf::perms::others_exe
);
// we're done!
return true;
}
};
AppDirRootSetup::AppDirRootSetup(const AppDir& appDir) : d(new Private(appDir)) {}
@@ -160,6 +251,15 @@ namespace linuxdeploy {
return false;
}
// plugins might need to run some initializing code to make certain features work
// these involve setting environment variables because libraries or frameworks don't support any other
// way of pointing them to resources inside the AppDir instead of looking into config files in locations
// inside the AppImage, etc.
// the linuxdeploy plugin specification states that if plugins put files into a specified directory in
// the AppImage, linuxdeploy will make sure they're run before running the regular AppRun
if (!d->deployAppRunWrapperIfNecessary())
return false;
return true;
}
}