update ffmpeg.git files for newer versions
[goodguy/cinelerra.git] / cinelerra-5.1 / tools / makeappimagetool / main.cpp
1 // system includes
2 #include <glob.h>
3 #include <iostream>
4 #include <libgen.h>
5
6 // local includes
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"
15
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;
21
22 namespace bf = boost::filesystem;
23
24 bool isFile(const std::string& path) {
25     return access(path.c_str(), F_OK) == 0;
26 }
27
28 std::string findAppimagetool() {
29     std::vector<char> buf(PATH_MAX, '\0');
30
31     if (readlink("/proc/self/exe", buf.data(), buf.size()) < 0) {
32         return "";
33     }
34
35     auto currentDirPath = std::string(dirname(buf.data()));
36
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",
43     };
44
45     std::stringstream ss;
46     ss << getenv("PATH");
47
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");
55     }
56
57     for (const auto& path : knownPaths) {
58         if (isFile(path))
59             return path;
60     }
61
62     return "";
63 }
64
65 int main(int argc, char** argv) {
66     args::ArgumentParser parser(
67         "makeappimage -- create AppDir bundles with ease"
68     );
69
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"});
73
74     args::ValueFlag<std::string> appDirPath(parser, "appdir", "Path to target AppDir", {"appdir"});
75
76     args::ValueFlagList<std::string> sharedLibraryPaths(parser, "library", "Shared library to deploy", {'l', "library"});
77
78     args::ValueFlagList<std::string> executablePaths(parser, "executable", "Executable to deploy", {'e', "executable"});
79
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"});
81
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"});
84
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"});
87
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"});
89
90     try {
91         parser.ParseCLI(argc, argv);
92     } catch (args::Help&) {
93         std::cerr << parser;
94
95         // license information
96         std::cerr << std::endl
97                   << "===== library information =====" << std::endl
98                   << std::endl
99                   << "This software uses the great CImg library, as well as libjpeg and libpng as well as various Boost libraries." << std::endl
100                   << std::endl
101                   << "libjpeg license information: this software is based in part on the work of the Independent JPEG Group" << std::endl
102                   << 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;
108
109         return 0;
110     } catch (args::ParseError& e) {
111         std::cerr << e.what() << std::endl;
112         std::cerr << parser;
113         return 1;
114     }
115
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;
119
120     // if only the version should be shown, we can exit now
121     if (showVersion)
122         return 0;
123
124     // set verbosity
125     if (verbosity) {
126         ldLog::setVerbosity((LD_LOGLEVEL) verbosity.Get());
127     }
128
129     if (!appDirPath) {
130         ldLog() << LD_ERROR << "--appdir parameter required" << std::endl;
131         std::cerr << std::endl << parser;
132         return 1;
133     }
134
135     appdir::AppDir appDir(appDirPath.Get());
136
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);
141     }
142
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;
147         return 1;
148     }
149
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;
153         return 1;
154     }
155
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;
159
160         for (const auto& libraryPath : sharedLibraryPaths.Get()) {
161             if (!bf::exists(libraryPath)) {
162                 ldLog() << LD_ERROR << "No such file or directory: " << libraryPath << std::endl;
163                 return 1;
164             }
165
166             if (!appDir.forceDeployLibrary(libraryPath)) {
167                 ldLog() << LD_ERROR << "Failed to deploy library: " << libraryPath << std::endl;
168                 return 1;
169             }
170         }
171     }
172
173     // deploy executables to usr/bin, and deploy their dependencies to usr/lib
174     if (executablePaths) {
175         ldLog() << std::endl << "-- Deploying executables --" << std::endl;
176
177         for (const auto& executablePath : executablePaths.Get()) {
178             if (!bf::exists(executablePath)) {
179                 ldLog() << LD_ERROR << "No such file or directory: " << executablePath << std::endl;
180                 return 1;
181             }
182
183             if (!appDir.deployExecutable(executablePath)) {
184                 ldLog() << LD_ERROR << "Failed to deploy executable: " << executablePath << std::endl;
185                 return 1;
186             }
187         }
188     }
189
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;
193
194         for (const auto& path : deployDepsOnlyPaths.Get()) {
195             if (bf::is_directory(path)) {
196                 ldLog() << "Deploying files in directory" << path << std::endl;
197
198                 for (auto it = bf::directory_iterator{path}; it != bf::directory_iterator{}; ++it) {
199                     if (!bf::is_regular_file(*it)) {
200                         continue;
201                     }
202
203                     if (!appDir.deployDependenciesOnlyForElfFile(*it, true)) {
204                         ldLog() << LD_WARNING << "Failed to deploy dependencies for ELF file" << *it << LD_NO_SPACE << ", skipping" << std::endl;
205                         continue;
206                     }
207                 }
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;
211                     return 1;
212                 }
213             } else {
214                 ldLog() << LD_ERROR << "No such file or directory: " << path << std::endl;
215                 return 1;
216             }
217         }
218     }
219
220     // perform deferred copy operations before running input plugins to make sure all files the plugins might expect
221     // are in place
222     ldLog() << std::endl << "-- Copying files into AppDir --" << std::endl;
223     if (!appDir.executeDeferredOperations()) {
224         return 1;
225     }
226
227
228     if (iconPaths) {
229         ldLog() << std::endl << "-- Deploying icons --" << std::endl;
230
231         for (const auto& iconPath : iconPaths.Get()) {
232             if (!bf::exists(iconPath)) {
233                 ldLog() << LD_ERROR << "No such file or directory: " << iconPath << std::endl;
234                 return 1;
235             }
236
237             bool iconDeployedSuccessfully;
238
239             if (iconTargetFilename) {
240                 iconDeployedSuccessfully = appDir.deployIcon(iconPath, iconTargetFilename.Get());
241             } else {
242                 iconDeployedSuccessfully = appDir.deployIcon(iconPath);
243             }
244
245             if (!iconDeployedSuccessfully) {
246                 ldLog() << LD_ERROR << "Failed to deploy icon: " << iconPath << std::endl;
247                 return 1;
248             }
249         }
250     }
251
252     if (desktopFilePaths) {
253         ldLog() << std::endl << "-- Deploying desktop files --" << std::endl;
254
255         for (const auto& desktopFilePath : desktopFilePaths.Get()) {
256             if (!bf::exists(desktopFilePath)) {
257                 ldLog() << LD_ERROR << "No such file or directory: " << desktopFilePath << std::endl;
258                 return 1;
259             }
260
261             desktopfile::DesktopFile desktopFile(desktopFilePath);
262
263             if (!appDir.deployDesktopFile(desktopFile)) {
264                 ldLog() << LD_ERROR << "Failed to deploy desktop file: " << desktopFilePath << std::endl;
265                 return 1;
266             }
267         }
268     }
269
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()) {
273         return 1;
274     }
275
276     if (createDesktopFile) {
277         if (!executablePaths) {
278             ldLog() << LD_ERROR << "--create-desktop-file requires at least one executable to be passed" << std::endl;
279             return 1;
280         }
281
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;
284
285         auto executableName = bf::path(executablePaths.Get().front()).filename().string();
286
287         auto desktopFilePath = appDir.path() / "usr/share/applications" / (executableName + ".desktop");
288
289         if (bf::exists(desktopFilePath)) {
290             ldLog() << LD_WARNING << "Working on existing desktop file:" << desktopFilePath << std::endl;
291         } else {
292             ldLog() << "Creating new desktop file:" << desktopFilePath << std::endl;
293         }
294
295         desktopfile::DesktopFile desktopFile;
296         if (!addDefaultKeys(desktopFile, executableName)) {
297             ldLog() << LD_WARNING << "Tried to overwrite existing entries in desktop file:" << desktopFilePath << std::endl;
298         }
299
300         if (!desktopFile.save(desktopFilePath.string())) {
301             ldLog() << LD_ERROR << "Failed to save desktop file:" << desktopFilePath << std::endl;
302             return 1;
303         }
304     }
305
306     if (!linuxdeploy::deployAppDirRootFiles(desktopFilePaths.Get(), customAppRunPath.Get(), appDir))
307         return 1;
308
309     // Find the external appimagetool and call it with the AppDir parameter.
310     // This executable file or AppImage should be called "appimagetool".
311
312     auto pathToAppimagetool = findAppimagetool();
313
314     if (pathToAppimagetool.empty()) {
315         std::cerr << "Could not find appimagetool in PATH" << std::endl;
316         return 1;
317     }
318
319     std::cout << "Found appimagetool: " << pathToAppimagetool << std::endl;
320
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);
328
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 << "\"";
333     }
334
335     std::cerr << std::endl;
336     std::cerr << "execv called with " << pathToAppimagetool.c_str() << std::endl;
337     std::cerr << std::endl << std::endl;
338
339     // separate appimagetool output from my output
340     std::cout << std::endl;
341
342     execv(pathToAppimagetool.c_str(), his_args.data());
343
344     auto error = errno;
345     std::cerr << "execl() failed: " << strerror(error) << std::endl;
346
347     return 0;
348 }
349