Skip to content

Commit

Permalink
Added support for .metainfo.xml files
Browse files Browse the repository at this point in the history
Previously, appimagetool had expected a .appdata.xml file, although that format had been deprecated and .metainfo.xml should be used for new projects.
This has been fixed. Support for .metainfo.xml files has been added. appimagetool now accepts both .metainfo.xml and .appdata.xml files.
A warning has also been added when using a .appdata.xml file that the file extension should be changed to .metainfo.xml.

Additionally, the link to the generator hadn't worked anymore. It has been replaced with a working link to an AppStream generator.
  • Loading branch information
Korne127 committed Jul 24, 2024
1 parent e8dadbb commit 825ef52
Showing 1 changed file with 82 additions and 59 deletions.
141 changes: 82 additions & 59 deletions src/appimagetool.c
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
/**************************************************************************
*
*
* Copyright (c) 2004-19 Simon Peter
*
*
* All Rights Reserved.
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*
**************************************************************************/

#ident "AppImage by Simon Peter, http://appimage.org/"
Expand Down Expand Up @@ -65,7 +65,7 @@ extern char runtime[];
extern unsigned int runtime_len;
#endif

enum fARCH {
enum fARCH {
fARCH_i386,
fARCH_x86_64,
fARCH_arm,
Expand Down Expand Up @@ -105,7 +105,7 @@ int sfs_ls(char* image) {
sqfs_err err = SQFS_OK;
sqfs_traverse trv;
sqfs fs;

ssize_t fs_offset = appimage_get_elf_size(image);

// error check
Expand All @@ -114,7 +114,7 @@ int sfs_ls(char* image) {

if ((err = sqfs_open_image(&fs, image, fs_offset)))
die("sqfs_open_image error");

if ((err = sqfs_traverse_open(&trv, &fs, sqfs_inode_root(&fs))))
die("sqfs_traverse_open error");
while (sqfs_traverse_next(&trv, &err)) {
Expand All @@ -125,12 +125,12 @@ int sfs_ls(char* image) {
if (err)
die("sqfs_traverse_next error");
sqfs_traverse_close(&trv);

sqfs_fd_close(fs.fd);
return 0;
}

/* Generate a squashfs filesystem using mksquashfs on the $PATH
/* Generate a squashfs filesystem using mksquashfs on the $PATH
* execlp(), execvp(), and execvpe() search on the $PATH */
int sfs_mksquashfs(char *source, char *destination, int offset) {
pid_t pid = fork();
Expand All @@ -146,13 +146,13 @@ int sfs_mksquashfs(char *source, char *destination, int offset) {
perror("sfs_mksquashfs waitpid() failed");
return(-1);
}

int retcode = WEXITSTATUS(status);
if (retcode) {
fprintf(stderr, "mksquashfs (pid %d) exited with code %d\n", pid, retcode);
return(-1);
}

return 0;
} else {
// we are the child
Expand Down Expand Up @@ -272,7 +272,7 @@ int validate_desktop_file(char *file) {
}

/* Generate a squashfs filesystem
* The following would work if we link to mksquashfs.o after we renamed
* The following would work if we link to mksquashfs.o after we renamed
* main() to mksquashfs_main() in mksquashfs.c but we don't want to actually do
* this because squashfs-tools is not under a permissive license
* i *nt sfs_mksquashfs(char *source, char *destination) {
Expand All @@ -291,19 +291,19 @@ int validate_desktop_file(char *file) {
static void replacestr(char *line, const char *search, const char *replace)
{
char *sp = NULL;

if ((sp = strstr(line, search)) == NULL) {
return;
}
int search_len = strlen(search);
int replace_len = strlen(replace);
int tail_len = strlen(sp+search_len);

memmove(sp+replace_len,sp+search_len,tail_len+1);
memcpy(sp, replace, replace_len);

/* Do it recursively again until no more work to do */

if ((sp = strstr(line, search))) {
replacestr(line, search, replace);
}
Expand Down Expand Up @@ -447,7 +447,7 @@ gchar* find_first_matching_file_nonrecursive(const gchar *real_path, const gchar
}
g_dir_close(dir);
}
else {
else {
g_warning("%s: %s", real_path, g_strerror(errno));
}
return NULL;
Expand Down Expand Up @@ -477,7 +477,7 @@ bool readFile(char* filename, int* size, char** buffer) {
fread(indata, fsize, 1, f);
fclose(f);
*size = (int)fsize;
*buffer = indata;
*buffer = indata;
return TRUE;
}

Expand Down Expand Up @@ -542,7 +542,7 @@ int
main (int argc, char *argv[])
{

/* Parse Travis CI environment variables.
/* Parse Travis CI environment variables.
* https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
* TRAVIS_COMMIT: The commit that the current build is testing.
* TRAVIS_REPO_SLUG: The slug (in form: owner_name/repo_name) of the repository currently being built.
Expand All @@ -563,18 +563,18 @@ main (int argc, char *argv[])
/* Parse GitLab CI environment variables.
* https://docs.gitlab.com/ee/ci/variables/#predefined-variables-environment-variables
* echo "${CI_PROJECT_URL}/-/jobs/artifacts/${CI_COMMIT_REF_NAME}/raw/QtQuickApp-x86_64.AppImage?job=${CI_JOB_NAME}"
*/
*/
char* CI_PROJECT_URL;
CI_PROJECT_URL = getenv("CI_PROJECT_URL");
char* CI_COMMIT_REF_NAME;
CI_COMMIT_REF_NAME = getenv("CI_COMMIT_REF_NAME"); // The branch or tag name for which project is built
char* CI_JOB_NAME;
CI_JOB_NAME = getenv("CI_JOB_NAME"); // The name of the job as defined in .gitlab-ci.yml

/* Parse OWD environment variable.
* If it is available then cd there. It is the original CWD prior to running AppRun */
char* owd_env = NULL;
owd_env = getenv("OWD");
owd_env = getenv("OWD");
if(NULL!=owd_env){
int ret;
ret = chdir(owd_env);
Expand All @@ -583,13 +583,13 @@ main (int argc, char *argv[])
exit(1);
}
}

GError *error = NULL;
GOptionContext *context;

// initialize help text of argument
sprintf(_exclude_file_desc, "Uses given file as exclude file for mksquashfs, in addition to %s.", APPIMAGEIGNORE);

context = g_option_context_new ("SOURCE [DESTINATION] - Generate, extract, and inspect AppImages");
g_option_context_add_main_entries (context, entries, NULL);
// g_option_context_add_group (context, gtk_get_option_group (TRUE));
Expand Down Expand Up @@ -646,16 +646,16 @@ main (int argc, char *argv[])
g_print("WARNING: gpg2 or gpg command is missing, please install it if you want to create digital signatures\n");
if(! g_find_program_in_path ("sha256sum") && ! g_find_program_in_path ("shasum"))
g_print("WARNING: sha256sum or shasum command is missing, please install it if you want to create digital signatures\n");

if(!&remaining_args[0])
die("SOURCE is missing");

/* If in list mode */
if (list){
sfs_ls(remaining_args[0]);
exit(0);
}

/* If the first argument is a directory, then we assume that we should package it */
if (g_file_test(remaining_args[0], G_FILE_TEST_IS_DIR)) {
/* Parse VERSION environment variable.
Expand Down Expand Up @@ -709,7 +709,7 @@ main (int argc, char *argv[])
char *destination;
char source[PATH_MAX];
realpath(remaining_args[0], source);

/* Check if *.desktop file is present in source AppDir */
gchar *desktop_file = find_first_matching_file_nonrecursive(source, "*.desktop");
if(desktop_file == NULL){
Expand All @@ -732,7 +732,7 @@ main (int argc, char *argv[])
die(".desktop file cannot be parsed");
if (!get_desktop_entry(kf, "Categories"))
die(".desktop file is missing a Categories= key");

if(verbose){
fprintf (stderr,"Name: %s\n", get_desktop_entry(kf, "Name"));
fprintf (stderr,"Icon: %s\n", get_desktop_entry(kf, "Icon"));
Expand Down Expand Up @@ -765,10 +765,10 @@ main (int argc, char *argv[])
char app_name_for_filename[PATH_MAX];
sprintf(app_name_for_filename, "%s", get_desktop_entry(kf, "Name"));
replacestr(app_name_for_filename, " ", "_");

if(verbose)
fprintf (stderr,"App name for filename: %s\n", app_name_for_filename);

if (remaining_args[1]) {
destination = remaining_args[1];
} else {
Expand Down Expand Up @@ -821,10 +821,10 @@ main (int argc, char *argv[])
fprintf (stderr, "%s\n", example_path);
exit(1);
}

/* Check if .DirIcon is present in source AppDir */
gchar *diricon_path = g_build_filename(source, ".DirIcon", NULL);

if (! g_file_test(diricon_path, G_FILE_TEST_EXISTS)){
fprintf (stderr, "Deleting pre-existing .DirIcon\n");
g_unlink(diricon_path);
Expand All @@ -835,20 +835,43 @@ main (int argc, char *argv[])
if(res)
die("Could not symlink .DirIcon");
}

/* Check if AppStream upstream metadata is present in source AppDir */
if(! no_appstream){
char application_id[PATH_MAX];
sprintf (application_id, "%s", basename(desktop_file));
replacestr(application_id, ".desktop", ".appdata.xml");
gchar *appdata_path = g_build_filename(source, "/usr/share/metainfo/", application_id, NULL);
if (! g_file_test(appdata_path, G_FILE_TEST_IS_REGULAR)){
fprintf (stderr, "WARNING: AppStream upstream metadata is missing, please consider creating it\n");
fprintf (stderr, " in usr/share/metainfo/%s\n", application_id);
fprintf (stderr, " Please see https://www.freedesktop.org/software/appstream/docs/chap-Quickstart.html#sect-Quickstart-DesktopApps\n");
fprintf (stderr, " for more information or use the generator at http://output.jsbin.com/qoqukof.\n");
} else {
fprintf (stderr, "AppStream upstream metadata found in usr/share/metainfo/%s\n", application_id);
bool appstream_found = false;
char *appstream_filename;
gchar **appstream_path;

char metainfo_filename[PATH_MAX];
sprintf(metainfo_filename, "%s", basename(desktop_file));
replacestr(metainfo_filename, ".desktop", ".metainfo.xml");
gchar *metainfo_path = g_build_filename(source, "/usr/share/metainfo/", metainfo_filename, NULL);
if (!g_file_test(metainfo_path, G_FILE_TEST_IS_REGULAR)) {
char appdata_filename[PATH_MAX];
sprintf(appdata_filename, "%s", basename(desktop_file));
replacestr(appdata_filename, ".desktop", ".appdata.xml");
gchar *appdata_path = g_build_filename(source, "/usr/share/metainfo/", appdata_filename, NULL);
if (!g_file_test(appdata_path, G_FILE_TEST_IS_REGULAR)) {
fprintf(stderr, "WARNING: AppStream upstream metadata is missing, please consider creating it\n");
fprintf(stderr, " in usr/share/metainfo/%s\n", metainfo_filename);
fprintf(stderr, " Please see https://www.freedesktop.org/software/appstream/docs/chap-Quickstart.html#sect-Quickstart-DesktopApps\n");
fprintf(stderr, " for more information or use the generator at https://docs.appimage.org/packaging-guide/optional/appstream.html.\n");
} else {
appstream_found = true;
appstream_filename = &appdata_filename;
appstream_path = &appdata_path;
fprintf(stderr, "WARNING: The appstream upstream metadata file should be named %s.\n", metainfo_filename);
fprintf(stderr, " .appdata.xml is a legacy file ending and shouldn't be used in new AppImages anymore.\n");
fprintf(stderr, " See https://www.freedesktop.org/software/appstream/docs/sect-Metadata-Application.html.\n");
}
} else {
appstream_found = true;
appstream_filename = &metainfo_filename;
appstream_path = &metainfo_path;
}

if (appstream_found) {
fprintf (stderr, "AppStream upstream metadata found in usr/share/metainfo/%s\n", *appstream_filename);
/* Use ximion's appstreamcli to make sure that desktop file and appdata match together */
if(g_find_program_in_path ("appstreamcli")) {
char *args[] = {
Expand All @@ -868,7 +891,7 @@ main (int argc, char *argv[])
char *args[] = {
"appstream-util",
"validate-relax",
appdata_path,
*appstream_path,
NULL
};
g_print("Trying to validate AppStream information with the appstream-util tool\n");
Expand All @@ -879,7 +902,7 @@ main (int argc, char *argv[])
}
}
}

/* Upstream mksquashfs can currently not start writing at an offset,
* so we need a patched one. https://github.com/plougher/squashfs-tools/pull/13
* should hopefully change that. */
Expand All @@ -904,11 +927,11 @@ main (int argc, char *argv[])
}
if (verbose)
printf("Size of the embedded runtime: %d bytes\n", size);

int result = sfs_mksquashfs(source, destination, size);
if(result != 0)
die("sfs_mksquashfs error");

fprintf (stderr, "Embedding ELF...\n");
FILE *fpdst = fopen(destination, "rb+");
if (fpdst == NULL) {
Expand All @@ -926,7 +949,7 @@ main (int argc, char *argv[])
printf("Could not set executable bit, aborting\n");
exit(1);
}

if(bintray_user != NULL){
if(bintray_repo != NULL){
char buf[1024];
Expand All @@ -935,7 +958,7 @@ main (int argc, char *argv[])
printf("%s\n", updateinformation);
}
}

/* If the user has not provided update information but we know this is a Travis CI build,
* then fill in update information based on TRAVIS_REPO_SLUG */
if(guess_update_information){
Expand All @@ -953,7 +976,7 @@ main (int argc, char *argv[])
if(zsyncmake_path){
char buf[1024];
gchar **parts = g_strsplit (travis_repo_slug, "/", 2);
/* https://github.com/AppImage/AppImageSpec/blob/master/draft.md#github-releases
/* https://github.com/AppImage/AppImageSpec/blob/master/draft.md#github-releases
* gh-releases-zsync|probono|AppImages|latest|Subsurface*-x86_64.AppImage.zsync */
gchar *channel = "continuous";
if(travis_tag != NULL){
Expand Down Expand Up @@ -983,22 +1006,22 @@ main (int argc, char *argv[])
}
}
}

/* If updateinformation was provided, then we check and embed it */
if(updateinformation != NULL){
if(!g_str_has_prefix(updateinformation,"zsync|"))
if(!g_str_has_prefix(updateinformation,"bintray-zsync|"))
if(!g_str_has_prefix(updateinformation,"gh-releases-zsync|"))
if(!g_str_has_prefix(updateinformation,"pling-v1-zsync|"))
die("The provided updateinformation is not in a recognized format");

gchar **ui_type = g_strsplit_set(updateinformation, "|", -1);

if(verbose)
printf("updateinformation type: %s\n", ui_type[0]);
/* TODO: Further checking of the updateinformation */


unsigned long ui_offset = 0;
unsigned long ui_length = 0;

Expand Down

0 comments on commit 825ef52

Please sign in to comment.