From a868a03a25bd829757776120f199ea27f43144e3 Mon Sep 17 00:00:00 2001 From: Chun-wei Fan Date: Wed, 18 Oct 2023 10:18:46 +0800 Subject: [PATCH 01/10] build: Also include CMake dep names as needed The Windows build systems of some dependencies (i.e. CMake) may not generate pkg-config files for us, but almost certainly generate CMake config files, so look for them as well, as their names differ from the .pc files. --- meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 1b78799d..61657e48 100644 --- a/meson.build +++ b/meson.build @@ -165,9 +165,9 @@ add_project_arguments('-DAS_COMPILATION', language: 'c') glib_dep = dependency('glib-2.0', version: '>= 2.62') gobject_dep = dependency('gobject-2.0', version: '>= 2.62') gio_dep = dependency('gio-2.0', version: '>= 2.62') -curl_dep = dependency('libcurl', version: '>= 7.62') -xml2_dep = dependency('libxml-2.0') -yaml_dep = dependency('yaml-0.1') +curl_dep = dependency(['libcurl', 'CURL'], version: '>= 7.62') +xml2_dep = dependency(['libxml-2.0', 'LibXML2']) +yaml_dep = dependency(['yaml-0.1', 'yaml']) xmlb_dep = dependency('xmlb', version: '>= 0.3.14', fallback: ['libxmlb', 'libxmlb_dep'], default_options: ['gtkdoc=false', 'introspection=false']) From a391ba83e9545c6eb3f17adafb1da79686f58cdf Mon Sep 17 00:00:00 2001 From: Chun-wei Fan Date: Wed, 18 Oct 2023 11:40:47 +0800 Subject: [PATCH 02/10] meson.build: Improve stemmer/SnowBall dep search Don't try to add '/usr/include' to the include directories as it may not exist on Windows, without checking for its presence. Likewise, check for 'libstemmer/libstemmer.h' only if '/usr/include' is found. Also, streamline finding the libstemmer libraries so that we look for libstemmer.h at the same time as looking for the stemmer library, to ensure that things are properly set up. --- meson.build | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/meson.build b/meson.build index 61657e48..f0476603 100644 --- a/meson.build +++ b/meson.build @@ -188,17 +188,21 @@ if get_option ('gir') dependency('gobject-introspection-1.0', version: '>=1.56') endif +stemmer_header_found = false stemmer_inc_dirs = include_directories() if get_option('stemming') - stemmer_lib = cc.find_library('stemmer', required: true) - stemmer_inc_dirs = include_directories(['/usr/include']) - if not cc.has_header('libstemmer.h') - if cc.has_header('libstemmer/libstemmer.h') - stemmer_inc_dirs = include_directories('/usr/include/libstemmer') - else - error('Unable to find Snowball header "libstemmer.h". Please ensure libstemmer/Snowball is installed properly in order to continue.') - endif + stemmer_h = 'libstemmer.h' + usr_include_dir = '/usr/include' + fs = import('fs') + if fs.is_dir(usr_include_dir) + stemmer_inc_dirs = include_directories([usr_include_dir]) + if fs.is_file(join_paths(usr_include_dir, 'libstemmer', stemmer_h)) + stemmer_inc_dirs = include_directories( + [join_paths(usr_include_dir, 'libstemmer')] + ) + endif endif + stemmer_lib = cc.find_library('stemmer', has_headers: [stemmer_h]) endif # use gperf for faster string -> enum matching From a55a6f4c82bbc6d09d528ba1b1c040bb28f2fa49 Mon Sep 17 00:00:00 2001 From: Chun-wei Fan Date: Wed, 18 Oct 2023 10:23:39 +0800 Subject: [PATCH 03/10] meson.build: Add variable indicating Windows builds ... and use it as applicable, as we are going to disable deps and features that are clearly not applicable for Windows. --- meson.build | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index f0476603..a06ab44f 100644 --- a/meson.build +++ b/meson.build @@ -18,6 +18,9 @@ as_minor_version = varr[1] as_micro_version = varr[2] as_data_installdir = get_option('prefix') / get_option('datadir') / 'appstream' +# Are we building for Windows? +is_windows = host_machine.system() == 'windows' + # # Configure files # @@ -78,7 +81,7 @@ if get_option('maintainer') add_project_arguments(maintainer_c_args, language: 'cpp') endif -if get_option('static-analysis') and host_machine.system() != 'windows' +if get_option('static-analysis') and not is_windows if cc.get_id() != 'gcc' error('You need to compile with GCC to run the static analyzer!') endif From 8e9e438e644cbb4d1c1a7d9d19d4518a164da5fc Mon Sep 17 00:00:00 2001 From: Chun-wei Fan Date: Wed, 18 Oct 2023 11:56:42 +0800 Subject: [PATCH 04/10] as-system-info.c: Only include dirent.h on Linux The code using things from this header only applies to Linux. --- src/as-system-info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/as-system-info.c b/src/as-system-info.c index de9b731d..0dd9ec5b 100644 --- a/src/as-system-info.c +++ b/src/as-system-info.c @@ -44,10 +44,10 @@ #else #include #endif -#include #include #if defined(__linux__) +#include #include #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) #include From deb9c83e39b375064e8a4a7df085bbe4f5503f00 Mon Sep 17 00:00:00 2001 From: Chun-wei Fan Date: Wed, 18 Oct 2023 12:05:41 +0800 Subject: [PATCH 05/10] src/*.c: Allow build on clang-cl clang-cl uses the headers from the Visual Studio and Windows SDK installation, so make sure that we are including the right headers on Windows for the things we are using in the code. In particular, unistd.h is a wrapper header on MinGW builds which include the various Windows system headers for the things that are actually done. Also, for calling umask(), just use S_IREAD | S_IWRITE for the default permissions on Windows. --- src/as-cache.c | 8 +++++++- src/as-distro-extras.c | 3 +++ src/as-pool.c | 4 ++++ src/as-utils.c | 8 +++++++- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/as-cache.c b/src/as-cache.c index c1808181..425476aa 100644 --- a/src/as-cache.c +++ b/src/as-cache.c @@ -34,9 +34,15 @@ #include #include -#include #include #include + +#ifdef G_OS_WIN32 +#include +#else +#include +#endif + #include #include "as-utils-private.h" diff --git a/src/as-distro-extras.c b/src/as-distro-extras.c index 25e2c5ee..c236b329 100644 --- a/src/as-distro-extras.c +++ b/src/as-distro-extras.c @@ -34,7 +34,10 @@ #include #include #include + +#ifndef G_OS_WIN32 #include +#endif #include "as-utils.h" #include "as-utils-private.h" diff --git a/src/as-pool.c b/src/as-pool.c index 9b5c8bf9..a38f7460 100644 --- a/src/as-pool.c +++ b/src/as-pool.c @@ -48,7 +48,11 @@ #include #include #include + +#ifndef G_OS_WIN32 #include +#endif + #include #include "as-utils.h" diff --git a/src/as-utils.c b/src/as-utils.c index 218f1774..6ba096cd 100644 --- a/src/as-utils.c +++ b/src/as-utils.c @@ -32,10 +32,12 @@ #include #include #include -#include #ifdef G_OS_WIN32 #include +#include +#include #else +#include #include #endif #include @@ -967,7 +969,11 @@ as_touch_location (const gchar *fname) void as_reset_umask (void) { +#ifdef G_OS_WIN32 + umask (S_IREAD | S_IWRITE); +#else umask (0022); +#endif } /** From 51b54a2cad220bf154e82c9fe3fccc7fbe026695 Mon Sep 17 00:00:00 2001 From: Chun-wei Fan Date: Mon, 30 Oct 2023 10:38:03 +0800 Subject: [PATCH 06/10] build: Build an intermediate static appstream library This is modelled like what is done in gtksourceview, so that we can use extract_objects() on the intermediate target so that we can use dumpbin to extract the symbols that is built in the appstream DLL. We might be able to make tests and utilities depend on this intermediate target, if needed, instead of exporting a bunch of private symbols in the shared library. --- src/meson.build | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/meson.build b/src/meson.build index ef5c6288..4a772573 100644 --- a/src/meson.build +++ b/src/meson.build @@ -196,18 +196,37 @@ if get_option ('stemming') aslib_deps += [stemmer_lib] endif +# We need this target to use dumpbin to generate a .def file +as_core_lib = static_library('appstream-@0@-'.format(as_api_level) + 'core', [ + aslib_src, + aslib_pub_headers, + aslib_gir_headers, + aslib_priv_headers, + aslib_res, + aslib_pub_enums, + ], + include_directories: [stemmer_inc_dirs, root_inc_dir], + dependencies: [aslib_deps], + install: false +) + +as_core_dep = declare_dependency( + link_with: as_core_lib, + include_directories: [stemmer_inc_dirs, root_inc_dir], + dependencies: [aslib_deps], +) + +as_deps = [ + as_core_dep, +] + +as_libs = [as_core_lib] + appstream_lib = library ('appstream', - [aslib_src, - aslib_pub_headers, - aslib_gir_headers, - aslib_priv_headers, - aslib_res, - aslib_pub_enums], + link_whole: as_libs, soversion: as_api_level, version: as_version, - dependencies: [aslib_deps], - include_directories: [stemmer_inc_dirs, - root_inc_dir], + dependencies: as_deps, install: true ) From 846371f4db8621f4239b321a2726d50fe240bd93 Mon Sep 17 00:00:00 2001 From: Chun-wei Fan Date: Mon, 30 Oct 2023 12:36:49 +0800 Subject: [PATCH 07/10] build: Generate a .def file for Visual Studio-like builds This way, we can export all the non-static symbols from the appstream DLL that we are trying to build, without touching the headers too much, by using the dumpbin tool that comes with every installation of Visual Studio, which clang-cl uses. Sadly, this approach does not allow us to effectively filter out those we mark with the GCC hidden visibility atribute, so this is the best we can do. --- src/gen-def.py | 126 ++++++++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 18 +++++++ 2 files changed, 144 insertions(+) create mode 100644 src/gen-def.py diff --git a/src/gen-def.py b/src/gen-def.py new file mode 100644 index 00000000..6202aeb2 --- /dev/null +++ b/src/gen-def.py @@ -0,0 +1,126 @@ +#!/usr/bin/python3 +# pylint: disable=invalid-name,missing-docstring +# +# Copyright (C) 2023 Chun-wei Fan +# +# SPDX-License-Identifier: LGPL-2.1+ + +# Script to generate .def file from object files/static library + +import argparse +import os +import re +import subprocess +import sys + +from io import StringIO + + +def run_dumpbin(objs): + dumpbin_results = [] + for o in objs: + command = ['dumpbin', '/symbols', o] + p = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=None, + stdin=subprocess.PIPE, + universal_newlines=True, + ) + stdout, blah = p.communicate() + if p.returncode != 0: + sys.exit(p.returncode) + dumpbin_results.append(stdout) + + return dumpbin_results + +''' +Notice output from dumpbin /symbols is like the following: + +Microsoft (R) COFF/PE Dumper Version 14.29.30152.0 +Copyright (C) Microsoft Corporation. All rights reserved. + + +Dump of file src\libappstream-5-core.a + +File Type: LIBRARY + +COFF SYMBOL TABLE +000 00000000 SECT1 notype Static | .text + Section length 0, #relocs 0, #linenums 0, checksum 0 +002 00000000 SECT2 notype Static | .data + Section length 0, #relocs 0, #linenums 0, checksum 0 +004 00000000 SECT3 notype Static | .bss + Section length 18, #relocs 0, #linenums 0, checksum 0 +006 00000000 SECT4 notype Static | .text + Section length 36, #relocs 6, #linenums 0, checksum 11750A74, selection 1 (pick no duplicates) +008 00000000 SECT4 notype () External | as_video_get_type +009 00000000 SECT32 notype Static | .xdata +... + +So, we are only more interested in items like: + +008 00000000 SECT4 notype () External | as_video_get_type + +and we need to note that these can also involve symbols that we use from +other libraries, so we throw these out when we go through the output +''' + +def extract_symbols(namespace, tokens): + if tokens[4] == '()': + check_idx = 5 + target_idx = 7 + else: + check_idx = 4 + target_idx = 6 + + if tokens[check_idx] == 'External': + target_plat = None + if 'Platform' in os.environ: + target_plat = os.environ['Platform'] + use_sym_prefix = False + if target_plat is not None and target_plat == 'x86': + use_sym_prefix = True + if use_sym_prefix: + if re.match(r'^' + '_' + namespace + '_', tokens[target_idx]) is not None: + return tokens[target_idx][1:] + else: + if re.match(r'^' + namespace + '_', tokens[target_idx]) is not None: + return tokens[target_idx] + +def process_dumpbin_outputs(namespace, dumpbin_results): + extracted_symbols = [] + for res in dumpbin_results: + dumpbin_lines = StringIO(res).readlines() + for l in dumpbin_lines: + tokens = l.split() + if (len(tokens) == 7 or len(tokens) == 8) and tokens[3] == 'notype': + result = extract_symbols(namespace, tokens) + if result is not None: + extracted_symbols.append(result) + extracted_symbols.sort() + return extracted_symbols + + +def output_def_file(def_file, symbols): + with open(def_file, "w") as out: + out.write("EXPORTS\n") + for s in symbols: + out.write("%s\n" % s) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Generate .def from object files or static libs.') + parser.add_argument('-n', '--namespace', dest='namespace', + help='namespace of symbols', required=True) + parser.add_argument('--def', dest='def_file', required=True, + help='output def file') + parser.add_argument('objects', nargs='+', + help='objects files/static libraries to process') + + args = parser.parse_args() + namespace = args.namespace + def_file = args.def_file + objs = args.objects + dumpbin_results = run_dumpbin(objs) + output_def_file(def_file, process_dumpbin_outputs(namespace, dumpbin_results)) diff --git a/src/meson.build b/src/meson.build index 4a772573..bf66df79 100644 --- a/src/meson.build +++ b/src/meson.build @@ -222,11 +222,29 @@ as_deps = [ as_libs = [as_core_lib] +if is_windows and cc.get_argument_syntax() == 'msvc' + gen_def = find_program('gen-def.py') + as_def = custom_target( + 'Generate Windows .def file', + input: as_core_lib, + output: 'appstream.def', + command: [ + gen_def, + '-n', 'as', + '--def', '@OUTPUT@', + '@INPUT@', + ] + ) +else + as_def = [] +endif + appstream_lib = library ('appstream', link_whole: as_libs, soversion: as_api_level, version: as_version, dependencies: as_deps, + vs_module_defs: as_def, install: true ) From 3297afa629db39b21c38ac1d9164a2474097bc64 Mon Sep 17 00:00:00 2001 From: Chun-wei Fan Date: Fri, 20 Oct 2023 10:30:21 +0800 Subject: [PATCH 08/10] ascli-utils.c: Don't include unistd.h on Windows It's not used there, and it's really a wrapper header in MinGW that include various Windows system headers and define things to ease porting POSIX items to Windows. --- tools/ascli-utils.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/ascli-utils.c b/tools/ascli-utils.c index 99e8617b..b947460b 100644 --- a/tools/ascli-utils.c +++ b/tools/ascli-utils.c @@ -22,9 +22,12 @@ #include #include -#include #include +#ifndef G_OS_WIN32 +#include +#endif + #include "as-pool-private.h" /** From 8d3aa65c8d00aab236c241328e74914c52b621e6 Mon Sep 17 00:00:00 2001 From: Chun-wei Fan Date: Fri, 20 Oct 2023 10:31:46 +0800 Subject: [PATCH 09/10] ascli-actions-pkgmgr.c: "Fix" build on Windows Like previous commits, avoid including unistd.h on Windows, and just bail out trying to run package managers or flatpak as they are more or less concepts that are foreign to Windows, at least for now. --- tools/ascli-actions-pkgmgr.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/ascli-actions-pkgmgr.c b/tools/ascli-actions-pkgmgr.c index 690ae31b..1938c9a4 100644 --- a/tools/ascli-actions-pkgmgr.c +++ b/tools/ascli-actions-pkgmgr.c @@ -22,7 +22,11 @@ #include #include + +#ifndef G_OS_WIN32 #include +#endif + #include #include "ascli-utils.h" @@ -41,6 +45,12 @@ exec_pm_action (const gchar *action, gchar **pkgnames) const gchar *exe = NULL; g_auto(GStrv) cmd = NULL; +#ifdef G_OS_WIN32 + g_printerr ("%s\n", + _("No supported package managers are available on Windows.")); + return ASCLI_EXIT_CODE_FAILED; +#else + #ifdef HAVE_APT_SUPPORT if (g_file_test ("/usr/bin/apt", G_FILE_TEST_EXISTS)) exe = "/usr/bin/apt"; @@ -66,6 +76,7 @@ exec_pm_action (const gchar *action, gchar **pkgnames) if (ret != 0) ascli_print_stderr (_("Unable to spawn package manager: %s"), g_strerror (errno)); return ret; +#endif } /** @@ -80,6 +91,12 @@ exec_flatpak_action (const gchar *action, const gchar *bundle_id) const gchar *exe = NULL; g_auto(GStrv) cmd = NULL; +#ifdef G_OS_WIN32 + g_printerr ("%s\n", + _("Flatpak is unavailable on Windows.")); + return ASCLI_EXIT_CODE_FAILED; +#else + exe = "/usr/bin/flatpak"; if (!g_file_test (exe, G_FILE_TEST_EXISTS)) { g_printerr ("%s\n", _("Flatpak was not found! Please install it to continue.")); @@ -95,6 +112,7 @@ exec_flatpak_action (const gchar *action, const gchar *bundle_id) if (ret != 0) ascli_print_stderr (_("Unable to spawn Flatpak process: %s"), g_strerror (errno)); return ret; +#endif } static int From c1a3e82213e3de7820ab0949d814b78109a6bf28 Mon Sep 17 00:00:00 2001 From: Chun-wei Fan Date: Fri, 20 Oct 2023 10:46:42 +0800 Subject: [PATCH 10/10] as-macros-private.h: Define macros for visibility for clang-cl On clang-cl (i.e. Visual Studio-like meaning _MSC_VER is defined) builds, symbols in shared builds have visibility to be hidden by default, and decorating symbols with GCC's default visibility is not enough to export the symbol. Consequently, use the macros in as-desktop-entry.h so that we don't fall into the situation where clang-cl complains as we are attempting to do a __declspec(dllexport) in a hidden visibility section. --- src/as-desktop-entry.h | 6 ++---- src/as-macros-private.h | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/as-desktop-entry.h b/src/as-desktop-entry.h index 7b957317..bdfd76f7 100644 --- a/src/as-desktop-entry.h +++ b/src/as-desktop-entry.h @@ -27,8 +27,7 @@ #include "as-metadata.h" #include "as-utils-private.h" -G_BEGIN_DECLS -#pragma GCC visibility push(hidden) +AS_BEGIN_PRIVATE_DECLS typedef GPtrArray *(*AsTranslateDesktopTextFn) (const GKeyFile *de, const gchar *text, @@ -54,7 +53,6 @@ gboolean as_desktop_entry_parse_file (AsComponent *cpt, gpointer user_data, GError **error); -#pragma GCC visibility pop -G_END_DECLS +AS_END_PRIVATE_DECLS #endif /* __AS_DESKTOP_ENTRY_H */ diff --git a/src/as-macros-private.h b/src/as-macros-private.h index 3f00aee9..9c4d8ba3 100644 --- a/src/as-macros-private.h +++ b/src/as-macros-private.h @@ -26,6 +26,20 @@ #include "as-macros.h" G_BEGIN_DECLS + +#ifdef _MSC_VER + +#define AS_BEGIN_PRIVATE_DECLS G_BEGIN_DECLS +#define AS_END_PRIVATE_DECLS G_END_DECLS + +#ifdef AS_STATIC +# define AS_INTERNAL_VISIBLE +#else +# define AS_INTERNAL_VISIBLE __declspec(dllexport) +#endif + +#else + #pragma GCC visibility push(hidden) #define AS_BEGIN_PRIVATE_DECLS \ @@ -36,6 +50,8 @@ G_BEGIN_DECLS #define AS_INTERNAL_VISIBLE __attribute__((visibility("default"))) +#endif + /** * as_str_equal0: * Returns TRUE if strings are equal, ignoring NULL strings. @@ -107,7 +123,10 @@ G_BEGIN_DECLS } \ G_STMT_END +#ifndef _MSC_VER #pragma GCC visibility pop +#endif + G_END_DECLS #endif /* __AS_MACROS_PRIVATE_H */