Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for .metainfo.xml files #1341

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stderr is not needed here?

/* 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