Skip to content

libarchive: Fix static lib deps on Windows - #2840

Open
meator wants to merge 1 commit into
mesonbuild:masterfrom
meator:pr/fix-libarchive-static-lib-on-windows
Open

libarchive: Fix static lib deps on Windows#2840
meator wants to merge 1 commit into
mesonbuild:masterfrom
meator:pr/fix-libarchive-static-lib-on-windows

Conversation

@meator

@meator meator commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

With no intervention, libarchive library functions are declared with __declspec(dllimport) even when libarchive is built statically. This leads to linker errors.

I'm unsure how default_library=both is supposed to work here. There's no declare_dependency(static_only_compile_flags: '-DLIBARCHIVE_STATIC'). What's default_library=both even for anyway? Does setting it in a wrap even make sense?

@bonzini

bonzini commented Jul 19, 2026

Copy link
Copy Markdown

There's nothing like that in declare_dependency but you can provide separate overriding dependencies for static and shared (static keyword argument of meson.override_dependency).

@meator

meator commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

@bonzini Should I do that? Split up library() into shared_library() and static_library(), make it conditional based on get_variable('default_library'), provide two dependency objects and override them with static: true and static: false?

@bonzini

bonzini commented Jul 19, 2026

Copy link
Copy Markdown

You can use library(), and then use both_libs.get_shared_lib()/both_libs.get_static_lib().

Meson really should improve support for this, for example:

  • when dependency returns an internal dependency, automatically apply get_shared_lib/get_static_lib to both_libs objects (or dually do that when override_dependency stores the override, it's the same)
  • add a snippets.symbol_visibility_override_dependency(NAME, DEP, static_compilation: '-DLIBARCHIVE_STATIC_COMPILATION') method that corresponds to
meson.override_dependency(NAME, DEP, static: false)
meson.override_dependency(NAME, declare_dependency(c_args: '-DLIBARCHIVE_STATIC_COMPILATION', dependencies: DEP), static: true)

The name is a mouthful but still.

@meator

meator commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

You can use library(), and then use both_libs.get_shared_lib()/both_libs.get_static_lib().

According to documentation, library() returns a lib object, which does not have get_shared_lib() and get_static_lib() methods. both_libs has such methods, but library() doesn't return an object of this type. To get it, I'd have to call both_libraries(), which I cannot do when default_library is not both. I don't want to write build system definitions that will bend over backwards to support a usecase which is (I believe) pretty rare.

Having <lang>_shared_args and <lang>_static_args would help with some of the ugliness I anticipate, but that's Meson >=1.3.0. I'd hesitate to use such feature in code I maintain, let alone in libarchive, a core library.

To be honest, I don't really see a sensible way to plug both_libraries() into this. When default_library=both, static library must define LIBARCHIVE_STATIC and shared library must not do the same. The advantage of both_libraries(), the fact that both libraries can be compiled at once while reusing object files, cannot be applied here, so both_libraries() becomes a strange wrapper for shared_library() + static_library(), the two functions that have to be called already by default_library=shared and default_library=static1, directly or indirectly.

It doesn't help that extra logic has to be put both into compilation and into dependency exporting.

With all that in mind, here's the solution I'm envisioning that takes into account the things I wrote above in pseudocode:

base_c_args = [things needed both by static and shared lib]
static_expored_flags = ['-DLIBARCHIVE_STATIC', etc.]
static_only_args = [static_expored_flags, etc.]

common_kwargs = {
  'include_directories': libinc,
  'name_prefix': '',
  'version': '.'.join(['13', ver_parts[1], ver_parts[2]]),
  'darwin_versions': [
    ver_darwin_compat,
    '.'.join([ver_darwin_compat, ver_parts[2]]),
  ],
  'gnu_symbol_visibility': 'hidden',
  'dependencies': deps,
  'install': true,
}

static_lib_needed = get_variable('both_libraries') != 'shared'
shared_lib_needed = get_variable('both_libraries') != 'static'
both_libs_needed = get_variable('both_libraries') == 'both'

shared_lib: lib
static_lib: lib

# if get_variable('both_libraries') == 'both', static_lib_needed == shared_lib_needed == both_libs_needed == true

if static_lib_needed
  static_lib = static_library(
    'libarchive',
    sources,
    c_args: [base_c_args, static_only_args],
    kwargs: common_kwargs,
  )
  if not both_libs_needed
    shared_lib = static_lib
  endif
endif
if shared_lib_needed
  shared_lib = shared_library(
    'libarchive',
    sources,
    c_args: base_c_args,
    kwargs: common_kwargs,
  )
  if not both_libs_needed
    static_lib = shared_lib
  endif
endif

meson.override_dependency(
  'libarchive',
  declare_dependency(
    link_with: static_lib,
    compile_args: static_expored_flags if static_lib is truly static, if it isn't, it
      shall be []
  ),
  static: true
)
meson.override_dependency(
  'libarchive',
  declare_dependency(
    link_with: shared_lib,
  ),
  static: false
)

Unless this will be reviewed further and unless I will receive positive feedback on implementing this change, I will not make such change a part of the PR. My PR doesn't make the situation worse, it didn't work before and after my changes it still doesn't work. Maybe that was the original wrap author's intention, to not care about default_library=both (but I don't know, programmers have a tendency to not document code that wasn't written).

I know that your comment is a direct reaction to my question, perhaps you did not intend me to implement such change. If not, the PR should be ready to merge (maybe except for the 60 failing tests, but they are again not caused by my PR).

Footnotes

  1. I'm considering all code paths I have to account for, I know it does not work like that.

@bonzini

bonzini commented Jul 26, 2026

Copy link
Copy Markdown

library can also return a both_libs object, if default_library=both. both_libs is a subclass of lib, so to speak. https://github.com/mesonbuild/meson/blob/bb531f03cd7be40558cf26bdc8f2d311343a1b53/mesonbuild/interpreter/interpreter.py#L3557

that's Meson >=1.3.0

I agree that if you need to account for older versions your options are much more limited.

static_expored_flags if static_lib is truly static, if it isn't, it

"Is truly static" is just static_lib_needed, right?

With no intervention, libarchive library functions are declared with
__declspec(dllimport) even when libarchive is built statically. This
leads to linker errors.
@meator
meator force-pushed the pr/fix-libarchive-static-lib-on-windows branch from ee424a2 to eb673b7 Compare August 12, 2026 07:43
@meator

meator commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

library can also return a both_libs object, if default_library=both. both_libs is a subclass of lib, so to speak. https://github.com/mesonbuild/meson/blob/bb531f03cd7be40558cf26bdc8f2d311343a1b53/mesonbuild/interpreter/interpreter.py#L3557

I did not know that. But the issue remains, I can't use <lang>_shared_args or <lang>_static_args, so I still have to use the drafted solution.

"Is truly static" is just static_lib_needed, right?

Correct.

Should I export a static: true dependency when it might not be static? The same applies for static: false.

Should I implement the change I reluctantly proposed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants