7 #include "includes/assert.h"
8 #include "includes/appdir.h"
9 #include "includes/args.hxx"
10 #include "includes/desktopfile.h"
11 #include "includes/elf_file.h"
12 #include "includes/log.h"
13 #include "includes/util.h"
14 #include "includes/core.h"
16 using namespace linuxdeploy;
17 using namespace linuxdeploy::core;
18 using namespace linuxdeploy::core::log;
19 using namespace linuxdeploy::util;
20 using namespace linuxdeploy::util::assert;
22 namespace bf = boost::filesystem;
24 bool isFile(const std::string& path) {
25 return access(path.c_str(), F_OK) == 0;
28 std::string findAppimagetool() {
29 std::vector<char> buf(PATH_MAX, '\0');
31 if (readlink("/proc/self/exe", buf.data(), buf.size()) < 0) {
35 auto currentDirPath = std::string(dirname(buf.data()));
37 // search next to current binary
38 std::vector<std::string> knownPaths = {
39 currentDirPath + "/appimagetool",
40 currentDirPath + "/appimagetool.AppImage",
41 currentDirPath + "/appimagetool-x86_64.AppImage",
42 currentDirPath + "/appimagetool-i686.AppImage",
48 // also search in PATH
49 std::string currentPath;
50 while (std::getline(ss, currentPath, ':')) {
51 knownPaths.push_back(currentPath + "/appimagetool");
52 knownPaths.push_back(currentPath + "/appimagetool.AppImage");
53 knownPaths.push_back(currentPath + "/appimagetool-x86_64.AppImage");
54 knownPaths.push_back(currentPath + "/appimagetool-i686.AppImage");
57 for (const auto& path : knownPaths) {
65 int main(int argc, char** argv) {
66 args::ArgumentParser parser(
67 "makeappimage -- create AppDir bundles with ease"
70 args::HelpFlag help(parser, "help", "Display this help text", {'h', "help"});
71 args::Flag showVersion(parser, "", "Print version and exit", {'V', "version"});
72 args::ValueFlag<int> verbosity(parser, "verbosity", "Verbosity of log output (0 = debug, 1 = info (default), 2 = warning, 3 = error)", {'v', "verbosity"});
74 args::ValueFlag<std::string> appDirPath(parser, "appdir", "Path to target AppDir", {"appdir"});
76 args::ValueFlagList<std::string> sharedLibraryPaths(parser, "library", "Shared library to deploy", {'l', "library"});
78 args::ValueFlagList<std::string> executablePaths(parser, "executable", "Executable to deploy", {'e', "executable"});
80 args::ValueFlagList<std::string> deployDepsOnlyPaths(parser, "path", "Path to ELF file or directory containing such files (libraries or executables) in the AppDir whose dependencies shall be deployed by makeappimage without copying them into the AppDir", {"deploy-deps-only"});
82 args::ValueFlagList<std::string> desktopFilePaths(parser, "desktop file", "Desktop file to deploy", {'d', "desktop-file"});
83 args::Flag createDesktopFile(parser, "", "Create basic desktop file that is good enough for some tests", {"create-desktop-file"});
85 args::ValueFlagList<std::string> iconPaths(parser, "icon file", "Icon to deploy", {'i', "icon-file"});
86 args::ValueFlag<std::string> iconTargetFilename(parser, "filename", "Filename all icons passed via -i should be renamed to", {"icon-filename"});
88 args::ValueFlag<std::string> customAppRunPath(parser, "AppRun path", "Path to custom AppRun script (makeappimage will not create a symlink but copy this file instead)", {"custom-apprun"});
91 parser.ParseCLI(argc, argv);
92 } catch (args::Help&) {
95 // license information
96 std::cerr << std::endl
97 << "===== library information =====" << std::endl
99 << "This software uses the great CImg library, as well as libjpeg and libpng as well as various Boost libraries." << std::endl
101 << "libjpeg license information: this software is based in part on the work of the Independent JPEG Group" << std::endl
103 << "CImg license information: This software is governed either by the CeCILL or the CeCILL-C "
104 "license under French law and abiding by the rules of distribution of free software. You can "
105 "use, modify and or redistribute the software under the terms of the CeCILL or CeCILL-C "
106 "licenses as circulated by CEA, CNRS and INRIA at the following URL: "
107 "\"http://cecill.info\"." << std::endl;
110 } catch (args::ParseError& e) {
111 std::cerr << e.what() << std::endl;
116 // always show version statement
117 // TODO pick up from what is specified in configure.ac
118 std::cerr << "MakeAppImage version " << "0.1.0" << std::endl;
120 // if only the version should be shown, we can exit now
126 ldLog::setVerbosity((LD_LOGLEVEL) verbosity.Get());
130 ldLog() << LD_ERROR << "--appdir parameter required" << std::endl;
131 std::cerr << std::endl << parser;
135 appdir::AppDir appDir(appDirPath.Get());
137 // allow disabling copyright files deployment via environment variable
138 if (getenv("DISABLE_COPYRIGHT_FILES_DEPLOYMENT") != nullptr) {
139 ldLog() << std::endl << LD_WARNING << "Copyright files deployment disabled" << std::endl;
140 appDir.setDisableCopyrightFilesDeployment(true);
143 // initialize AppDir with common directories
144 ldLog() << std::endl << "-- Creating basic AppDir structure --" << std::endl;
145 if (!appDir.createBasicStructure()) {
146 ldLog() << LD_ERROR << "Failed to create basic AppDir structure" << std::endl;
150 ldLog() << std::endl << "-- Deploying dependencies for existing files in AppDir --" << std::endl;
151 if (!appDir.deployDependenciesForExistingFiles()) {
152 ldLog() << LD_ERROR << "Failed to deploy dependencies for existing files" << std::endl;
156 // deploy shared libraries to usr/lib, and deploy their dependencies to usr/lib
157 if (sharedLibraryPaths) {
158 ldLog() << std::endl << "-- Deploying shared libraries --" << std::endl;
160 for (const auto& libraryPath : sharedLibraryPaths.Get()) {
161 if (!bf::exists(libraryPath)) {
162 ldLog() << LD_ERROR << "No such file or directory: " << libraryPath << std::endl;
166 if (!appDir.forceDeployLibrary(libraryPath)) {
167 ldLog() << LD_ERROR << "Failed to deploy library: " << libraryPath << std::endl;
173 // deploy executables to usr/bin, and deploy their dependencies to usr/lib
174 if (executablePaths) {
175 ldLog() << std::endl << "-- Deploying executables --" << std::endl;
177 for (const auto& executablePath : executablePaths.Get()) {
178 if (!bf::exists(executablePath)) {
179 ldLog() << LD_ERROR << "No such file or directory: " << executablePath << std::endl;
183 if (!appDir.deployExecutable(executablePath)) {
184 ldLog() << LD_ERROR << "Failed to deploy executable: " << executablePath << std::endl;
190 // deploy executables to usr/bin, and deploy their dependencies to usr/lib
191 if (deployDepsOnlyPaths) {
192 ldLog() << std::endl << "-- Deploying dependencies only for ELF files --" << std::endl;
194 for (const auto& path : deployDepsOnlyPaths.Get()) {
195 if (bf::is_directory(path)) {
196 ldLog() << "Deploying files in directory" << path << std::endl;
198 for (auto it = bf::directory_iterator{path}; it != bf::directory_iterator{}; ++it) {
199 if (!bf::is_regular_file(*it)) {
203 if (!appDir.deployDependenciesOnlyForElfFile(*it, true)) {
204 ldLog() << LD_WARNING << "Failed to deploy dependencies for ELF file" << *it << LD_NO_SPACE << ", skipping" << std::endl;
208 } else if (bf::is_regular_file(path)) {
209 if (!appDir.deployDependenciesOnlyForElfFile(path)) {
210 ldLog() << LD_ERROR << "Failed to deploy dependencies for ELF file: " << path << std::endl;
214 ldLog() << LD_ERROR << "No such file or directory: " << path << std::endl;
220 // perform deferred copy operations before running input plugins to make sure all files the plugins might expect
222 ldLog() << std::endl << "-- Copying files into AppDir --" << std::endl;
223 if (!appDir.executeDeferredOperations()) {
229 ldLog() << std::endl << "-- Deploying icons --" << std::endl;
231 for (const auto& iconPath : iconPaths.Get()) {
232 if (!bf::exists(iconPath)) {
233 ldLog() << LD_ERROR << "No such file or directory: " << iconPath << std::endl;
237 bool iconDeployedSuccessfully;
239 if (iconTargetFilename) {
240 iconDeployedSuccessfully = appDir.deployIcon(iconPath, iconTargetFilename.Get());
242 iconDeployedSuccessfully = appDir.deployIcon(iconPath);
245 if (!iconDeployedSuccessfully) {
246 ldLog() << LD_ERROR << "Failed to deploy icon: " << iconPath << std::endl;
252 if (desktopFilePaths) {
253 ldLog() << std::endl << "-- Deploying desktop files --" << std::endl;
255 for (const auto& desktopFilePath : desktopFilePaths.Get()) {
256 if (!bf::exists(desktopFilePath)) {
257 ldLog() << LD_ERROR << "No such file or directory: " << desktopFilePath << std::endl;
261 desktopfile::DesktopFile desktopFile(desktopFilePath);
263 if (!appDir.deployDesktopFile(desktopFile)) {
264 ldLog() << LD_ERROR << "Failed to deploy desktop file: " << desktopFilePath << std::endl;
270 // perform deferred copy operations before creating other files here before trying to copy the files to the AppDir root
271 ldLog() << std::endl << "-- Copying files into AppDir --" << std::endl;
272 if (!appDir.executeDeferredOperations()) {
276 if (createDesktopFile) {
277 if (!executablePaths) {
278 ldLog() << LD_ERROR << "--create-desktop-file requires at least one executable to be passed" << std::endl;
282 ldLog() << std::endl << "-- Creating desktop file --" << std::endl;
283 ldLog() << LD_WARNING << "Please beware the created desktop file is of low quality and should be edited or replaced before using it for production releases!" << std::endl;
285 auto executableName = bf::path(executablePaths.Get().front()).filename().string();
287 auto desktopFilePath = appDir.path() / "usr/share/applications" / (executableName + ".desktop");
289 if (bf::exists(desktopFilePath)) {
290 ldLog() << LD_WARNING << "Working on existing desktop file:" << desktopFilePath << std::endl;
292 ldLog() << "Creating new desktop file:" << desktopFilePath << std::endl;
295 desktopfile::DesktopFile desktopFile;
296 if (!addDefaultKeys(desktopFile, executableName)) {
297 ldLog() << LD_WARNING << "Tried to overwrite existing entries in desktop file:" << desktopFilePath << std::endl;
300 if (!desktopFile.save(desktopFilePath.string())) {
301 ldLog() << LD_ERROR << "Failed to save desktop file:" << desktopFilePath << std::endl;
306 if (!linuxdeploy::deployAppDirRootFiles(desktopFilePaths.Get(), customAppRunPath.Get(), appDir))
309 // Find the external appimagetool and call it with the AppDir parameter.
310 // This executable file or AppImage should be called "appimagetool".
312 auto pathToAppimagetool = findAppimagetool();
314 if (pathToAppimagetool.empty()) {
315 std::cerr << "Could not find appimagetool in PATH" << std::endl;
319 std::cout << "Found appimagetool: " << pathToAppimagetool << std::endl;
321 // Push program name to arg 0, and the appdir path to arg 1.
322 // The program name in arg 0 is always "appimagetool", even though
323 // the actual executable name might be different.
324 std::vector<char*> his_args;
325 his_args.push_back(strdup("appimagetool"));
326 his_args.push_back(strdup(appDirPath.Get().c_str()));
327 his_args.push_back(nullptr);
329 // This should put out two values.
330 std::cerr << "Running command: " << pathToAppimagetool;
331 for (auto it = his_args.begin() + 0; it != his_args.end(); it++) {
332 std::cerr << " " << "\"" << *it << "\"";
335 std::cerr << std::endl;
336 std::cerr << "execv called with " << pathToAppimagetool.c_str() << std::endl;
337 std::cerr << std::endl << std::endl;
339 // separate appimagetool output from my output
340 std::cout << std::endl;
342 execv(pathToAppimagetool.c_str(), his_args.data());
345 std::cerr << "execl() failed: " << strerror(error) << std::endl;