Importing C libraries on Windows

Hi everyone,

I'm trying to build a cross-platform library that depends on SDL2 (and related libraries). On macOS and Linux I can declare a systemLibrary in my Package.swift and use the following modulemap:

module CSDL [system] {
  umbrella header "header.h"
  link "SDL2"
  export *
}

and header.h:

#if __APPLE__
    #include <SDL.h>
#else
    #include <SDL2/SDL.h>
#endif

to interface with the underlying C library.

If I understand it correctly, on macOS and Linux, this relies on pkg-config to get the path to the headers and shared libraries. However, on Windows, I'm not sure what to do.

The SDL libraries are distributed as directories with header files and DLLs, that users can put anywhere on their system. So I'm left wondering:

  • Is there an equivalent modulemap I can use on Windows?
  • How can I tell SwiftPM where to find the headers and DLLs?
  • Should I include these headers and libraries in my Swift package, so I don't have to use absolute paths, and users aren't forced to put them in a specific location?
  • Is what I'm trying to do even possible with SwiftPM?

Thanks!

2 Likes

The modulemap mentioned in the thread should be fine.

You would need to tell swiftc where the headers and library content is via -Xswiftc -I... and -Xlinker -L.... Note that the DLLs do not matter, just have them in the path. You only care about the import librarires.

You may vend a copy of SDL if you prefer. I would recommend that you vend a copy for all platforms in that case for homogeneity.

Yes, this is possible with SPM, in fact, I just did the same for SPM itself in Basics: migrate TSCUtility.SQLite to Basics by compnerd · Pull Request #5764 · apple/swift-package-manager (github.com).

1 Like

You would need to tell swiftc where the headers and library content is via -Xswiftc -I... and -Xlinker -L... . Note that the DLLs do not matter, just have them in the path. You only care about the import librarires.

Thanks, that helped me made some progress! However, when doing this, I got a strange error:

C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\include\module.modulemap:61:14: error: header 'tmmintrinh' not found

      header "tmmintrinh"

and many others errors as a result. Upon inspecting this modulemap, I got the suspicion that this may be a typo, and indeed, when I change it to header "tmmintrin.h", everything builds and runs fine (so far).

Is this modulemap part of Visual Studio, or is it modified by Swift?

Thanks for reporting this issue. This is a bug in Swift (one that has been there for a while). It seems that it slipped through again in 5.7. I've put up platform: correct header for ssse3 by compnerd · Pull Request #61360 · apple/swift (github.com) for that. This was fixed on main a long time ago, so a more recent snapshot would also resolve the issue without having to manually fix the issue.

2 Likes

Another issue popped up: There seems to be a difference in how integer types are imported into Swift on Windows vs macOS and Linux :

For example, the following enum:

typedef enum
{
    SDL_WINDOW_FULLSCREEN = 0x00000001,         /**< fullscreen window */
    SDL_WINDOW_OPENGL = 0x00000002,             /**< window usable with OpenGL context */
    SDL_WINDOW_SHOWN = 0x00000004,              /**< window is visible */
    // ...
} SDL_WindowFlags;

imports as an enum with a raw value of type Int32 on Windows, but UInt32 on macOS and Linux. I've also seen issues where the reverse is true (UInt32 on Windows, but Int32 on macOS and Linux).

Another example is:

extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndexDPI(const char *file, int ptsize, long index, unsigned int hdpi, unsigned int vdpi);

That parameter of type long becomes an Int on macOS and Linux, but Int32 on Windows.

Is this an issue in SDL, or in Swift, or is it normal behavior that I need to work around?

This is not an issue. This is correct, desired behaviour. enum types are int on Windows, unsigned int on Linux and macOS. Importing C types will map accordingly. This is also why using values with the MSB set are a problem for portability with enum in C. I would be interested in hearing about the cases where it is the other way - that is Windows imports it as UInt32 where the enum is not an explicitly typed enum (e.g. using the clang extensions).

long is more interesting as it is always 32-bits on Windows and as such is mapped to Int32. This difference is visible at the C layer just as well - using a 64-bit value will truncate on Windows.

I'd recommend reading up on the Linux, macOS, and Windows ABIs for C and C++ to get a better idea of where all these cases can arise.

3 Likes

Thanks, that explains a lot! Fortunately, not too much workarounds were needed, and with those in place, everything I have so far builds and runs fine on Windows :slight_smile:

I've gone through the error messages again, and those that result from enums were consistent with what you explained (Int32 on Windows, UInt32 elsewhere). The others were unrelated to enums.