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

Provide a definition of ssize_t when compiling with MSVC [more robust alternative to #1375] #1377

Merged
merged 1 commit into from
Jan 18, 2022

Conversation

jmarshall
Copy link
Member

@jmarshall jmarshall commented Jan 11, 2022

PR #1375 proposes unconditionally typedeffing ssize_t on MSVC in htslib/hts_defs.h, but as discussed in #1025 (comment) this is fragile if user code or other header files #included also include their own workarounds for this ssize_t issue. (It would be reminiscent of the olden days when every C library had its own bool type!)

A robust alternative is to #define ssize_t within each affected HTSlib header only, undoing it at the end of the header:

Define ssize_t within the public headers that use it, enabling them to be compiled with MSVC, which does not provide this type definition in its <sys/types.h> or any other headers.

Having ssize_t as a macro may confuse other headers, so be sure to do this only after all other #includes and to undefine it again at the end of each header. User code or other libraries may also have workarounds for this issue, so checking its not already #defined and removing our ssize_t macro afterwards also avoids conflicting with other workarounds.

Under MSVC, including <stdint.h>/<string.h>/etc also causes <vcruntime.h> to be included, which defines size_t and intptr_t as unsigned/signed versions of the same type. So we define ssize_t as the latter, if it is not already defined (by some other workaround) and if _INTPTR_T_DEFINED has indeed been defined by <vcruntime.h> (to avoid confusing error messages).

(I assume that Cygwin and MinGW compilations always use GCC. If users might use Cygwin or MinGW headers with the MSVC compiler, the #if will need some Cygwin and MinGW guards too.)

Also define it (permanently) in <htslib/hts_os.h>, unless suppressed by defining HTS_NO_SSIZE_T before including the header.

At the time of #1025, this ssize_t problem was the only thing preventing public HTSlib headers from being easily included from user code compiled by MSVC. I have not checked to see if this is still the case in 2022.


This workaround is admittedly somewhat voluminous!

An alternative would be to define a hts_ssize_t typedef as ssize_t/SSIZE_T in <htslib/hts_defs.h> and use that instead of ssize_t in function signatures in other public HTSlib headers. IMHO this would be a very unfortunate approach, as it would mislead API users into thinking they had to use that type name in their own code instead of the familiar POSIX standard ssize_t type name.

@jmarshall
Copy link
Member Author

This addresses the ssize_t problem for the public header files.

For compiling the HTSlib source files themselves, the best approach to addressing this ssize_t problem (there are probably many other problems in compiling this source with MSVC as well!) would probably be to arrange for #define ssize_t intptr_t (perhaps with a suitable #include <vcruntime.h> or so) to be added to config.h.

This would usually be done in configure, but people wanting to compile HTSlib with MSVC are probably not in a position to be running a Unix shell script! An alternative would be a pre-written bespoke config-win.h, but really choosing the best way to proceed here (if at all) requires input from someone who actually uses MSVC.

(There is AC_TYPE_SSIZE_T, but it adds a definition defining it to int which is not compatible with the intptr_t used in this PR. So HTSlib would want its own m4/hts_type_ssize_t.m4.)

@jkbonfield
Copy link
Contributor

Thanks. It's a relatively small change, but I'll delay reviewing and merging for now until I've tested MSVC for real.

Basically I want to know if this is the only issue it finds. If the entire library can be linked against and public headers #included provided this one change is made, then I think it's justified. My recollections of having used an admittedly very old MSVC in the past is that it won't be that trivial. If it's one of many such problems, such as missing symbols, needing underscore added to dozens of POSIX function names, wrappers around functions which take different numbers of arguments (mkdir rings a bell), etc then it's probably not worth the hassle of supporting them all, which also means this change becomes redundant.

@BickfordA
Copy link
Contributor

BickfordA commented Jan 11, 2022

Happy to help here if I can, the ssize_t type was the only issue i couldn't resolve with POSIX header shims/libs on windows apart from one static array parameter issue. Though I am using CMake to drive the compilation which sidesteps some of the issues with having a custom shell script for the configure step.

Define `ssize_t` within the public headers that use it, enabling them
to be compiled with MSVC, which does not provide this type definition
in its <sys/types.h> or any other headers.

Having `ssize_t` as a macro may confuse other headers, so be sure to do
this only after all other #includes and to undefine it again at the end
of each header. User code or other libraries may also have workarounds
for this issue, so checking its not already #defined and removing our
`ssize_t` macro afterwards also avoids conflicting with other workarounds.

Under MSVC, including <stdint.h>/<string.h>/etc also causes <vcruntime.h>
to be included, which defines `size_t` and `intptr_t` as unsigned/signed
versions of the same type. So we define `ssize_t` as that, if it is not
already defined (by some other workaround) and if `_INTPTR_T_DEFINED` has
indeed been defined by <vcruntime.h> (to avoid confusing error messages).
We also check `_SSIZE_T_DEFINED`, which MSVC will likely define if it's
ever fixed to supply this typedef.

Also define it (permanently) in <htslib/hts_os.h>, unless suppressed
by defining `HTS_NO_SSIZE_T` before including the header.
@jmarshall
Copy link
Member Author

Now also checks _SSIZE_T_DEFINED, which MSVC will likely define if this is ever fixed.

@BickfordA: It would be useful if you would test building under MSVC using this PR instead of your ssize_t fix.

@BickfordA
Copy link
Contributor

BickfordA commented Jan 11, 2022

This works for me! I no longer need to add anything to the htslib/hts_def.h!

I do still need to add some definitions to my config.h file to cover some of the places not in that commit:

#include <BaseTsd.h>
typedef SSIZE_T ssize_t;

#define MAXSIZE_T   ((SIZE_T)~((SIZE_T)0))
#define MAXSSIZE_T  ((SSIZE_T)(MAXSIZE_T >> 1))
#define SSIZE_MAX MAXSIZE_T

If I don't add that to the config.h i get these errors:

1>C:\Development\aidan-dev\3rdparty\htslib\htslib\header.c(767,34): error C2065: 'SSIZE_MAX': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile_internal.h(69,5): error C2061: syntax error: identifier 'ssize_t' (compiling source file C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c)
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile_internal.h(74,14): error C2143: syntax error: missing ')' before '*' (compiling source file C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c)
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile_internal.h(74,14): error C2143: syntax error: missing '{' before '*' (compiling source file C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c)
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile_internal.h(74,20): error C2059: syntax error: ')' (compiling source file C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c)
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile_internal.h(89,1): error C2059: syntax error: '}' (compiling source file C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c)
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(150,35): error C2079: 'mem_backend' uses undefined struct 'hFILE_backend'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(169,16): error C2061: syntax error: identifier 'refill_buffer'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(169,16): error C2059: syntax error: ';'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(169,36): error C2059: syntax error: '<parameter-list>'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(231,9): error C2061: syntax error: identifier 'hgetdelim'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(231,9): error C2059: syntax error: ';'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(231,24): error C2059: syntax error: '<parameter-list>'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(290,9): error C2061: syntax error: identifier 'hpeek'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(290,9): error C2059: syntax error: ';'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(290,21): error C2059: syntax error: '<parameter-list>'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(308,9): error C2061: syntax error: identifier 'hread2'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(308,9): error C2059: syntax error: ';'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(308,22): error C2059: syntax error: '<parameter-list>'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(353,16): error C2061: syntax error: identifier 'flush_buffer'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(353,16): error C2059: syntax error: ';'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(353,35): error C2059: syntax error: '<parameter-list>'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(370,22): error C2037: left of 'flush' specifies undefined struct/union 'hFILE_backend'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(371,26): error C2037: left of 'flush' specifies undefined struct/union 'hFILE_backend'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(390,9): error C2061: syntax error: identifier 'hwrite2'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(390,9): error C2059: syntax error: ';'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(390,23): error C2059: syntax error: '<parameter-list>'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(469,24): error C2037: left of 'seek' specifies undefined struct/union 'hFILE_backend'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(485,22): error C2037: left of 'close' specifies undefined struct/union 'hFILE_backend'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(498,22): error C2037: left of 'close' specifies undefined struct/union 'hFILE_backend'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(964,13): error C2065: 'ssize_t': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(964,13): error C2146: syntax error: missing ';' before identifier 'rec'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(964,17): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1011,10): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1013,20): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1014,15): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1016,22): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1029,9): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1031,6): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1043,17): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1044,21): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1056,26): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1069,19): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1076,9): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1119,10): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1121,20): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1122,15): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1125,22): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1138,9): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1143,14): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1143,23): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1143,44): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1144,14): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1299,13): error C2065: 'ssize_t': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1299,13): error C2146: syntax error: missing ';' before identifier 'i'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1299,14): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1299,20): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1299,31): error C2065: 'in_idx': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1300,12): error C2065: 'in_idx': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1313,12): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1313,48): error C2065: 'in_idx': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1313,64): error C2065: 'in_idx': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1316,12): error C2065: 'in_idx': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1316,16): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1319,12): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1319,19): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1319,33): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1321,14): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1333,39): error C2065: 'in_idx': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1361,14): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1361,18): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1361,25): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1361,33): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1362,10): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1393,28): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1396,25): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1397,14): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1402,12): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1403,12): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1410,18): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1412,22): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1412,32): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1413,9): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1415,10): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1422,9): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1437,17): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1443,13): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1450,14): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1451,14): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1454,9): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1454,15): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1454,22): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1454,35): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1454,45): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1454,57): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1455,20): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1459,34): error C2065: 'i': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\htscodecs\htscodecs\fqzcomp_qual.c(1460,31): error C2065: 'rec': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\kstring.c(305,11): error C2065: 'ssize_t': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\kstring.c(305,11): error C2146: syntax error: missing ';' before identifier 'len'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\kstring.c(305,15): error C2065: 'len': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\kstring.c(306,11): error C2065: 'len': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\kstring.c(307,14): error C2065: 'len': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile_internal.h(69,5): error C2061: syntax error: identifier 'ssize_t' (compiling source file C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c)
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile_internal.h(74,14): error C2143: syntax error: missing ')' before '*' (compiling source file C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c)
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile_internal.h(74,14): error C2143: syntax error: missing '{' before '*' (compiling source file C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c)
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile_internal.h(74,20): error C2059: syntax error: ')' (compiling source file C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c)
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile_internal.h(89,1): error C2059: syntax error: '}' (compiling source file C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c)
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(73,16): error C2061: syntax error: identifier 'multipart_read'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(73,16): error C2059: syntax error: ';'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(73,37): error C2059: syntax error: '<parameter-list>'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(114,16): error C2061: syntax error: identifier 'multipart_write'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(114,16): error C2059: syntax error: ';'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(114,38): error C2059: syntax error: '<parameter-list>'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(138,35): error C2079: 'multipart_backend' uses undefined struct 'hFILE_backend'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(140,19): error C2065: 'multipart_read': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(140,36): error C2065: 'multipart_write': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(141,1): error C2078: too many initializers
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(139,1): error C2099: initializer is not a constant
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\multipart.c(140,54): error C2078: too many initializers
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\vcf.c(1267,13): error C2065: 'ssize_t': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\vcf.c(1267,13): error C2146: syntax error: missing ';' before identifier 'ret'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\vcf.c(1267,16): error C2065: 'ret': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\vcf.c(1269,14): error C2065: 'ret': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\vcf.c(1270,17): error C2065: 'ret': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(602,35): error C2079: 'fd_backend' uses undefined struct 'hFILE_backend'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(605,1): error C2078: too many initializers
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(604,14): error C2078: too many initializers
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(771,35): error C2079: 'mem_backend' uses undefined struct 'hFILE_backend'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(774,1): error C2078: too many initializers
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\hfile.c(773,11): error C2078: too many initializers
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\cram\cram_index.c(149,13): error C2065: 'ssize_t': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\cram\cram_index.c(149,13): error C2146: syntax error: missing ';' before identifier 'len'
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\cram\cram_index.c(149,16): error C2065: 'len': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\cram\cram_index.c(198,17): error C2065: 'len': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\cram\cram_index.c(199,28): error C2065: 'len': undeclared identifier
1>C:\Development\aidan-dev\3rdparty\htslib\htslib\cram\cram_index.c(203,13): error C2065: 'len': undeclared identifier

@jkbonfield
Copy link
Contributor

I think @jmarshall's changes are to the public headers, meaning when using a pre-built htslib from within MSVC. Your changes are for compiling htslib itself from MSVC. That's an entirely new level of pain.

I don't think it's workable without a lot more fiddling with the Makefile (for generating all that extra stuff in config.h) and/or your cmake config (although surely that's also well out of a limb as far as most MSVC users go - they'd want a project file), plus lots of extra work for import libraries and the like.

Given you're already willing to use a bunch of unix tools, headers and libraries in order to compile, what is your rationale for using the MSVC compiler specifically rather than using msys2/mingw?

@jkbonfield
Copy link
Contributor

Given you're already willing to use a bunch of unix tools, headers and libraries in order to compile, what is your rationale for using the MSVC compiler specifically rather than using msys2/mingw?

It's been a good 20 years since I had to use MSVC in anger, and I'm now remembering why I expunged all I learnt about it back then. Actually I'm starting to think using mingw libs from msvc is just plain not possible! I'm sure I've seen it done before, but I can't get it to work. I'd still like to know your thinking though too.

I've been trying to get MSVC to link against a mingw prebuilt library, but it's challenging. The steps I've tried to take are:

  1. Link using gcc (mingw) -Wl,--enable-auto-import -Wl,--out-implib=hts.dll.a -o hts.dll. I theory I'd like to go direct from the .a or .so to a .def and .lib and/or .dll, but that's even more problematic from what I can see.
  2. gendef.exe to turn the .dll into a .def
  3. llvm-dlltool to convert the .def to a .lib
  4. Use cl to link against hts.lib

All of that works and produces me a binary. However that binary then fails on first malloc (I think). Eg an htsfile equivalent built that way displays usage, but cannot parse a BAM file:

$ ./htsfile.exe test/colons.bam
      0 [main] htsfile (13536) child_copy: cygheap read copy failed, 0x180348408..0x180359210, done 0, windows pid 13536, Win32 error 6
    337 [main] htsfile (13536) C:\msys64\home\jkbon\htslib\htsfile.exe: *** fatal error - ccalloc would have returned NULL

The other issue is that it would require a ghastly number of ancillary dlls:

$ ldd hts.dll
        ntdll.dll => /c/WINDOWS/SYSTEM32/ntdll.dll (0x7ffefb140000)
        KERNEL32.DLL => /c/WINDOWS/System32/KERNEL32.DLL (0x7ffef9f20000)
        KERNELBASE.dll => /c/WINDOWS/System32/KERNELBASE.dll (0x7ffef8630000)
        msvcrt.dll => /c/WINDOWS/System32/msvcrt.dll (0x7ffef91f0000)
        msys-2.0.dll => /usr/bin/msys-2.0.dll (0x180040000)
        msys-curl-4.dll => /usr/bin/msys-curl-4.dll (0x83d2420000)
        msys-bz2-1.dll => /usr/bin/msys-bz2-1.dll (0x6e61220000)
        msys-lzma-5.dll => /usr/bin/msys-lzma-5.dll (0x49fa70000)
        msys-z.dll => /usr/bin/msys-z.dll (0x522fe0000)
        msys-gssapi-3.dll => /usr/bin/msys-gssapi-3.dll (0x42f53c0000)
        msys-crypto-1.1.dll => /usr/bin/msys-crypto-1.1.dll (0x478b980000)
        msys-brotlidec-1.dll => /usr/bin/msys-brotlidec-1.dll (0x438fc10000)
        msys-psl-5.dll => /usr/bin/msys-psl-5.dll (0x4a92e0000)
        msys-idn2-0.dll => /usr/bin/msys-idn2-0.dll (0x4322bb0000)
        msys-ssh2-1.dll => /usr/bin/msys-ssh2-1.dll (0x2a8c350000)
        msys-nghttp2-14.dll => /usr/bin/msys-nghttp2-14.dll (0x4f20660000)
        msys-zstd-1.dll => /usr/bin/msys-zstd-1.dll (0x48b870000)
        msys-asn1-8.dll => /usr/bin/msys-asn1-8.dll (0x6c901d0000)
        msys-ssl-1.1.dll => /usr/bin/msys-ssl-1.1.dll (0xd8f950000)
        msys-com_err-1.dll => /usr/bin/msys-com_err-1.dll (0x6219420000)
        msys-heimntlm-0.dll => /usr/bin/msys-heimntlm-0.dll (0x31fe970000)
        msys-hcrypto-4.dll => /usr/bin/msys-hcrypto-4.dll (0x50a4450000)
        msys-heimbase-1.dll => /usr/bin/msys-heimbase-1.dll (0x33889a0000)
        msys-krb5-26.dll => /usr/bin/msys-krb5-26.dll (0x20e6ec0000)
        msys-brotlicommon-1.dll => /usr/bin/msys-brotlicommon-1.dll (0x6ff5210000)
        msys-roken-18.dll => /usr/bin/msys-roken-18.dll (0x35fe2a0000)
        msys-iconv-2.dll => /usr/bin/msys-iconv-2.dll (0x5603f0000)
        msys-unistring-2.dll => /usr/bin/msys-unistring-2.dll (0x43b990000)
        msys-intl-8.dll => /usr/bin/msys-intl-8.dll (0x430b30000)
        msys-gcc_s-seh-1.dll => /usr/bin/msys-gcc_s-seh-1.dll (0xde8160000)
        msys-wind-0.dll => /usr/bin/msys-wind-0.dll (0x7e97010000)
        msys-wind-0.dll => /usr/bin/msys-wind-0.dll (0xd60000)
        msys-hx509-5.dll => /usr/bin/msys-hx509-5.dll (0x727f610000)
        msys-sqlite3-0.dll => /usr/bin/msys-sqlite3-0.dll (0x1d798a0000)
        msys-crypt-0.dll => /usr/bin/msys-crypt-0.dll (0x43dbf0000)
        advapi32.dll => /c/WINDOWS/System32/advapi32.dll (0x7ffefae90000)
        sechost.dll => /c/WINDOWS/System32/sechost.dll (0x7ffef9e50000)
        RPCRT4.dll => /c/WINDOWS/System32/RPCRT4.dll (0x7ffefad70000)
        CRYPTBASE.DLL => /c/WINDOWS/SYSTEM32/CRYPTBASE.DLL (0x7ffef7e30000)
        bcryptPrimitives.dll => /c/WINDOWS/System32/bcryptPrimitives.dll (0x7ffef8f60000

That would be a rediculous amount of cruft to have to ship with your dll. I don't know why so many, but my money would be on libcurl. (I can't see any other reason for thins like zstd and sqlite.) We could produce a cut down libary, but that then invites all sorts of bug reports about why it can't do X, Y and Z, so I reject that idea. The alternative is to make a static .lib instead, but static libs in Unix at least are normally just the .o files and the person doing the linking then has to add all those dependencies themselves.

So I'm concluding the MSVC just isn't sufficiently compatible with mingw to be viable. That sort of renders htslib as useless, unless you're also another mingw user (which realistically means you're a unix user and it's more likely you'll be using unix direct or via WSL within windows, rendering a native windows binary copy of the library somewhat moot).

I'd love to hear otherwise though!

So the alternative is to fully support MSVC directly. That's a bit of a pain, and I'm not sure any of us really have the energy to actively maintain such a thing ourselves, not being Microsoft developers. If there was a PR showing all the necessary changes that are needed to get the package to cleanly build under MSVC then we could review what it entails, but my gut instinct here having tried it is it's too much given it'd require shipping lots of third party things such as a pthreads and getopt implementations.

I would recommend instead WIndows users learn to love WSL instead.

@jkbonfield
Copy link
Contributor

jkbonfield commented Jan 13, 2022

I take that last statement back. It's been so long since I installed MINGW that I forgot the nuances. Thanks to @daviesrob for reminding me that both the MSYS environment matters and also the package names (pacman doesn't use the environment to work out the repository to install from). Yes, I'll document all this in INSTALL.

With $MSYSTEM of MINGW64 it does produce a library that msvc can link against. I hacked up a copy of test_view with a few bits tweaked to get it to build, and compiled that natively using MSVC and linking agains the MINGW64 flavour of hts-3.dll and it runs.

So this does go back to the main thrust of this PR. However it needed a lot more than just ssize_t to get it to work. I need to work out what's required yet by my test program (ie test_view) and what was in the library headers only. hts_verbose is one source of error though. I think it needs extra declspec stuff to work in MSVC. (I just commented it out for the sake of testing linkage.)

@jkbonfield
Copy link
Contributor

jkbonfield commented Jan 14, 2022

I can confirm that this PR works, but I can also confirm that -Dssize_t=intptr_t also works with MSVC:

for i in htslib/*.h
do
    echo $i
    echo -e "#include <$i>" > x.c
    cl.exe -c -Dssize_t=intptr_t x.c -I.
done

So while this PR simplifies things a bit, it's not the only viable method. We could instead simply document the lack of ssize_t and recommend apropriate compiler variables are set to compensate for lack of POSIX compliance. I'm OK with both, although I note this may accidentally lead to breakage in the future while we have to remember to add this boilerplate any time we use ssize_t in a new file.

Ie that's a policy decision I'll punt upwards. :)

@jmarshall
Copy link
Member Author

jmarshall commented Jan 14, 2022

-Dssize_t=intptr_t happens to work at present. As mentioned in the commit message, this PR took care to ensure that sufficient system includes occur first that size_t and intptr_t are defined in each header. If a usage of ssize_t were added to another HTSlib header that did not include many system headers other than <sys/types.h>, the proposed -D option would lead to an “unknown identifier intptr_t” error (unless care was taken to add an unnecessary <stdint.h> or so inclusion too).

Anyway, the salient policy difference is between MSVC users needing to do nothing special to include HTSlib headers, just as users on other platforms do, and MSVC users needing to do one small special thing first, whether that is a -D option or -include option or #include <htslib/hts_os.h> or otherwise ensure ssize_t is defined.

Despite having proposed this PR, I am in two minds as to whether ten lines of boilerplate in each header that uses ssize_t (and, as you say, needing to remember to add it each time another header gains an ssize_t usage — but fortunately the only thing that breaks if it is forgotten is MSVC usage) is really warranted.

The alternative would be to apply just the hts_os.h change, report an “add ssize_t to ucrt's sys/types.h“ issue to Microsoft, and document that MSVC users need to work around this Microsoft issue, either by -D, -include, #define, #include <htslib/hts_os.h>, or another method. Then in 3–5 years the problem might go away…

@jkbonfield
Copy link
Contributor

I did read your commit message, but I thought I'd experimentally try it without an explicit include just to see how often it was a problem in practice.

I'll also add, as alluded to elsewhere (not sure which issue now), most compilers have the option to do a pre-include forcing it before the compilation. Sure enough MSVC is no exception: https://docs.microsoft.com/en-us/cpp/build/reference/fi-name-forced-include-file?view=msvc-170

What this means is that basically the choice here is between documented appropriate CFLAGS that may be useful when using MSVC against our public headers, vs modifying the headers themselves. I can see both sides of this argument and it's unclear what's most appropriate.

@jkbonfield
Copy link
Contributor

We discussed it in our weekly meeting and the conclusion was this is probably the best of the various choices. Thanks.

@jkbonfield jkbonfield merged commit 4289991 into samtools:develop Jan 18, 2022
@jmarshall jmarshall deleted the ssize_t branch January 18, 2022 16:25
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.

3 participants