On Thu, Mar 14, 2024 at 11:30:21PM -0700, Tyler Retzlaff wrote:
> MSVC is the only compiler that can produce usable shared libraries for
> DPDK on Windows because of the use of exported TLS variables.
> 
> Disable building of shared libraries with LLVM and MinGW so that
> remaining __declspec macros needed for the functional libraries built by
> MSVC can be used without triggering errors in LLVM and MinGW builds.
> 
> For Windows only install the default_library type to avoid confusion.
> Windows builds cannot build both shared and static in a single pass so
> install only the functional variant.
> 
> Signed-off-by: Tyler Retzlaff <roret...@linux.microsoft.com>

Comments inline below.

/Bruce

> ---
>  app/meson.build                       |  6 +++
>  config/meson.build                    | 27 ++++++++++++
>  drivers/meson.build                   | 72 +++++++++++++++----------------
>  drivers/net/octeontx/base/meson.build |  2 +-
>  examples/meson.build                  |  6 +++
>  lib/meson.build                       | 79 
> +++++++++++++++--------------------
>  6 files changed, 108 insertions(+), 84 deletions(-)
> 
> diff --git a/app/meson.build b/app/meson.build
> index 21b6da2..f4ed0f1 100644
> --- a/app/meson.build
> +++ b/app/meson.build
> @@ -46,6 +46,8 @@ default_cflags = machine_args + ['-DALLOW_EXPERIMENTAL_API']
>  default_ldflags = []
>  if get_option('default_library') == 'static' and not is_windows
>      default_ldflags += ['-Wl,--export-dynamic']
> +elif get_option('default_library') == 'shared' and is_ms_compiler
> +    default_ldflags += ['/experimental:tlsDllInterface']
>  endif
>  

Is this necessary, given that you add this flag as a project-wide argument
below in config/meson.build?

>  foreach app:apps
> @@ -104,6 +106,10 @@ foreach app:apps
>          link_libs = dpdk_static_libraries + dpdk_drivers
>      endif
>  
> +    if is_windows and is_shared_enabled
> +        cflags += '-DRTE_BUILD_SHARED_LIB'
> +    endif
> +
>      exec = executable('dpdk-' + name,
>              sources,
>              c_args: cflags,
> diff --git a/config/meson.build b/config/meson.build
> index 8c8b019..dd7971e 100644
> --- a/config/meson.build
> +++ b/config/meson.build
> @@ -53,6 +53,9 @@ if is_ms_compiler
>  
>      # enable statement expressions extension
>      add_project_arguments('/experimental:statementExpressions', language: 
> 'c')
> +
> +    # enable export of thread_local variables
> +    add_project_arguments('/experimental:tlsDllInterface', language: 'c')
>  endif
>  
>  # set the major version, which might be used by drivers and libraries
> @@ -516,4 +519,28 @@ if get_option('default_library') == 'both'
>   NOTE: DPDK always builds both shared and static libraries.  Please set
>   "default_library" to either "static" or "shared" to select default linkage
>   for apps and any examples.''')
> +elif get_option('default_library') == 'shared' and is_windows and not 
> is_ms_compiler
> +    error( '''
> + Unsupported value "shared" for "default_library" option.
> +
> + NOTE: DPDK Windows shared is only supported when building with MSVC. Please 
> set
> + "default_library" to either "static" or use MSVC.''')
> +endif
> +
> +if is_windows
> +    if is_ms_compiler and get_option('default_library') == 'shared'
> +        is_shared_enabled=true
> +    else
> +        is_shared_enabled=false
> +    endif
> +else
> +    is_shared_enabled=true
> +endif
> +

This can be shortened to:

is_shared_enabled=true
if is_windows and (not is_ms_compiler or not get_option(...) == 'shared')
        is_shared_enabled=false
endif


> +if is_windows
> +    install_static = get_option('default_library') == 'static'
> +    install_shared = get_option('default_library') == 'shared'
> +else
> +    install_static=true
> +    install_shared=true
>  endif

Might be better merged with the block above:

is_shared_enabled = true
install_static = true
install_shared = true
if is_windows
    install_static = get_option('default_library') == 'static'
    install_shared = get_option('default_library') == 'shared'

    if not is_ms_compiler or not install_shared
        is_shared_enabled = false
    endif
endif

> diff --git a/drivers/meson.build b/drivers/meson.build
> index 66931d4..c8b2d13 100644
> --- a/drivers/meson.build
> +++ b/drivers/meson.build
> @@ -80,7 +80,7 @@ foreach subpath:subdirs
>          subdir(class)
>          skip_class = false
>          foreach d:std_deps
> -            if not is_variable('shared_rte_' + d)
> +            if not is_variable('static_rte_' + d)
>                  skip_class = true
>                  reason = 'missing internal dependency, "@0@"'.format(d)
>                  if dpdk_libs_deprecated.contains(d)
> @@ -173,7 +173,7 @@ foreach subpath:subdirs
>                  if not build
>                      break
>                  endif
> -                if not is_variable('shared_rte_' + d)
> +                if not is_variable('static_rte_' + d)
>                      build = false
>                      reason = 'missing internal dependency, "@0@"'.format(d)
>                      if dpdk_libs_deprecated.contains(d)
> @@ -188,7 +188,9 @@ foreach subpath:subdirs
>                                  +'\tPlease enable missing dependency 
> "@0@"'.format(d))
>                      endif
>                  else
> -                    shared_deps += [get_variable('shared_rte_' + d)]
> +                    if is_shared_enabled
> +                        shared_deps += [get_variable('shared_rte_' + d)]
> +                    endif
>                      static_deps += [get_variable('static_rte_' + d)]
>                  endif
>              endforeach
> @@ -208,6 +210,9 @@ foreach subpath:subdirs
>          enabled_drivers += name
>          lib_name = '_'.join(['rte', class, name])
>          cflags += '-DRTE_LOG_DEFAULT_LOGTYPE=' + '.'.join([log_prefix, name])
> +        if is_windows and is_shared_enabled
> +            cflags += '-DRTE_BUILD_SHARED_LIB'
> +        endif

Rather than setting this in cflags in the drivers or lib meson.build files,
can we set this once as a project-level cflag and be done with it?

>          if annotate_locks and cc.get_id() == 'clang' and 
> cc.version().version_compare('>=3.5.0')
>              cflags += '-DRTE_ANNOTATE_LOCKS'
>              cflags += '-Wthread-safety'
> @@ -247,7 +252,7 @@ foreach subpath:subdirs
>                  include_directories: includes,
>                  dependencies: static_deps,
>                  c_args: cflags,
> -                install: true)
> +                install: install_static)
>  
>          # now build the shared driver
>          version_map = 
> '@0@/@1@/version.map'.format(meson.current_source_dir(), drv_path)
> @@ -271,55 +276,46 @@ foreach subpath:subdirs
>              endif
>          endif
>  
> -        if is_windows
> -            if is_ms_linker
> +        if is_shared_enabled
> +            if is_ms_compiler
>                  def_file = custom_target(lib_name + '_def',
>                          command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
>                          input: version_map,
>                          output: '@0@_exports.def'.format(lib_name))
>                  lk_deps += [def_file]
> -
> -                lk_args = ['-Wl,/def:' + def_file.full_path()]
> -                if meson.version().version_compare('<0.54.0')
> -                    lk_args += ['-Wl,/implib:drivers\\lib' + lib_name + 
> '.dll.a']
> -                endif
> +                lk_args = ['/experimental:tlsDllInterface', '/def:' + 
> def_file.full_path()]

Again, the /experimental flag shouldn't be necessary, since it's set as
project option in config/meson.build.

>              else
> -                mingw_map = custom_target(lib_name + '_mingw',
> -                        command: [map_to_win_cmd, '@INPUT@', '@OUTPUT@'],
> -                        input: version_map,
> -                        output: '@0@_mingw.map'.format(lib_name))
> -                lk_deps += [mingw_map]
> -
> -                lk_args = ['-Wl,--version-script=' + mingw_map.full_path()]
> +                lk_args = ['-Wl,--version-script=' + version_map]
>              endif
> -        else
> -            lk_args = ['-Wl,--version-script=' + version_map]
>          endif
>  
> -        shared_lib = shared_library(lib_name, sources,
> -                objects: objs,
> -                include_directories: includes,
> -                dependencies: shared_deps,
> -                c_args: cflags,
> -                link_args: lk_args,
> -                link_depends: lk_deps,
> -                version: abi_version,
> -                soversion: so_version,
> -                install: true,
> -                install_dir: driver_install_path)
> -
> -        # create a dependency object and add it to the global dictionary so
> -        # testpmd or other built-in apps can find it if necessary
> -        shared_dep = declare_dependency(link_with: shared_lib,
> -                include_directories: includes,
> -                dependencies: shared_deps)
> +        if is_shared_enabled
> +            shared_lib = shared_library(lib_name, sources,
> +                    objects: objs,
> +                    include_directories: includes,
> +                    dependencies: shared_deps,
> +                    c_args: cflags,
> +                    link_args: lk_args,
> +                    link_depends: lk_deps,
> +                    version: abi_version,
> +                    soversion: so_version,
> +                    install: install_shared,
> +                    install_dir: driver_install_path)
> +
> +            # create a dependency object and add it to the global dictionary 
> so
> +            # testpmd or other built-in apps can find it if necessary
> +            shared_dep = declare_dependency(link_with: shared_lib,
> +                    include_directories: includes,
> +                    dependencies: shared_deps)
> +
> +            set_variable('shared_@0@'.format(lib_name), shared_dep)

Just a thought - could you avoid some of the changes throughout this set,
if you add an else-leg to the "is_shared_enabled" check, where, if shared
is not enabled, you assign shared_dep to be an empty string, or an empty
object or something.

That would mean that you wouldn't need to move the set_variable line as
here, you also wouldn't need to put conditionals around all the

Just a thought - could you avoid some of the changes throughout this set,
if you add an else-leg to the "is_shared_enabled" check, where, if shared
is not enabled, you assign shared_dep to be an empty string, or an empty
object or something.

That would mean that you wouldn't need to move the set_variable line as
here, you also wouldn't need to put conditionals around all the
'shared_deps +=' lines when processing deps for a component, and lastly,
you may not even need to change all the searches for shared_rte_ to
static_rte, since the variables would exist for shared - they'd just be
empty!

> +        endif
>          static_dep = declare_dependency(
>                  include_directories: includes,
>                  dependencies: static_deps)
>  
>          dpdk_drivers += static_lib

<snip for brevity>

Reply via email to