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

MSVC/Windows support #214

Merged
merged 24 commits into from
Sep 15, 2020
Merged

MSVC/Windows support #214

merged 24 commits into from
Sep 15, 2020

Conversation

zebrys
Copy link
Contributor

@zebrys zebrys commented Sep 7, 2020

This PR enable Microsoft Visual Studio (>=2017) to build the project and use it on Windows.

@zebrys zebrys changed the title MSVC support MSVC/Windows support Sep 7, 2020
@evanmiller
Copy link
Contributor

Hi, thank you for the contribution. It appears the travis and appveyor builds are failing - examining the output, "exit code 139" indicates a segfault. So there is likely a memory or double-free error somewhere.

Look at the code, I think it's better to have a single logic path (where possible) for both Windows and non-Windows. So I'm OK using malloc() instead of variable-sized stack allocations everywhere in order to meet MSVC's requirements. I recommend removing the non-_MSC_VER paths that aren't necessary.

Going forward, we will need to ensure that future changes do not break the MSVC build. So I also recommend changing the Appveyor configuration to build with an MSVC toolset. I'm not overly versed in Appveyor, but I can try to provide assistance there if you need it.

Copy link
Contributor

@evanmiller evanmiller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking better, thank you for making the changes. I have suggested a few more. The only mandatory one is fixing READSTAT_VERSION.

Before approval, we will need to set up a build with MSVC on Appveyor (as mentioned previously).

#if !defined _MSC_VER
# include <sys/time.h>
#else
# define READSTAT_VERSION "0.2.0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version should not be hard-coded. Currently we maintain version information in configure.ac.

unlink(output_filename);
#else
_unlink(output_filename);
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For platform differences like this, I would prefer to move the logic somewhere else so that it can be reused. For a simple case like this probably adding to the top of the file

#if defined _MSC_VER
#define unlink _unlink
#endif

Would be fine.

struct tm ltm;
localtime_s(&ltm, &timestamp);
strftime(buffer, sizeof(buffer), "%d %b %Y %H:%M", &ltm);
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a more complicated platform difference like this, a separate function is probably warranted, e.g.

size_t readstat_strftime(char *s, size_t maxsize, const char *format, time_t timsetamp) {
#if !defined _MSC_VER
...
#else
...
#endif
}

That way we keep _MSC_VER out of the main application functions.

#include <unistd.h>
#if !defined(_MSC_VER)
# include <unistd.h>
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am guessing that most inclusions of unistd.h are no longer needed even on Unix, but I can clean them up later.

@@ -133,6 +134,9 @@ static readstat_error_t sas7bcat_parse_value_labels(const char *value_start, siz
}

cleanup:
if(label)
free(label);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not important, but for future reference: free will accept a NULL pointer, so it's not necessary to check it with if(label).

char header[hinfo->page_header_size];
memset(header, 0, sizeof(header));
char *header = malloc(hinfo->page_header_size);
memset(header, 0, hinfo->page_header_size);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

malloc/memset should use calloc(1, hinfo->page_header_size) instead

@evanmiller
Copy link
Contributor

CC @jonathon-love

@zebrys
Copy link
Contributor Author

zebrys commented Sep 8, 2020

Thank you for your feedback.
Regarding READSTAT_VERSION, I think the only way to do the same on MSVC would be to put it in the MSVC configuration file. Thus, in the end, there will be a READSTAT_VERSION define in the autotools and one in the MSVC file. Is it OK for you ?

@evanmiller
Copy link
Contributor

@zebrys Thanks for making the requested changes. Go ahead and add READSTAT_VERSION to the MSVC file and I'll see if it looks manageable from my end. (Current version is 1.1.4.)

@jonathon-love
Copy link
Contributor

ah! this looks great! i maintain a patch for MSVC support over here:

https://github.com/jonathon-love/ReadStat

but it looks like you've got it all covered anyway. let me see if i can simply use this as a drop-in replacement.

@jonathon-love
Copy link
Contributor

hi,

i'm having some failures, which i assume is to do with me using an earlier version of MSVC.

https://ci.appveyor.com/project/jonathon-love/jamovi-readstat

i'm using 14 (because we want python compatibility ... @ofajardo too). it looks like the earlier version doesn't support the restrict keyword. this is how i work around it:

jonathon-love@c77cbff#diff-34dc7b78affe90ca491a0d0492d933ee

how would people feel about me making this compatible with MSVC 14?

jonathon

@evanmiller
Copy link
Contributor

All versions are fine with me, as long as we get CI / Appveyor coverage in the end. The config file from the Boost::Math project may provide some guidance, as they cover both MSVC and GCC:

https://github.com/boostorg/math/blob/develop/appveyor.yml

@zebrys
Copy link
Contributor Author

zebrys commented Sep 9, 2020

The VS issue was due to a problem during a merge on our side. This last update is now tested with VS2017 and works well. It should also theoretically work with VS2019 and probably VS2014/VS2015 but the best would be to check. The restrict issue has been fixed as well (as a missing VLA use). @jonathon-love can you check the last update on VS2014?

@zebrys
Copy link
Contributor Author

zebrys commented Sep 9, 2020

@evanmiller We never used Appveyor so far. We will try to do that.

@evanmiller
Copy link
Contributor

@zebrys Okay, that would be great!

@zebrys
Copy link
Contributor Author

zebrys commented Sep 9, 2020

The iconv dependency is a problem for the CI on Windows: it does not seems to be already available on the image and is required for the MSVC project (we just need a directory with the header and the compiled lib files). What is the standard way to get dependencies like this in such CI ?

@evanmiller
Copy link
Contributor

Will this solve your problem?

https://github.com/fastlane/fastlane/pull/13191/files

We could also explore a native Win32 solution since almost all transcoding is routed through readstat_convert.

@jonathon-love
Copy link
Contributor

wrt iconv, you could consider my approach. i mount these projects as submodules:

https://github.com/kiyolee/libiconv-win-build
https://github.com/kiyolee/zlib-win-build

and here's my appveyor file:

https://github.com/jamovi/jamovi-readstat/blob/master/appveyor.yml

@jonathon-love
Copy link
Contributor

can you check the last update on VS2014?

it all builds for me!

@zebrys
Copy link
Contributor Author

zebrys commented Sep 10, 2020

Thank you for this link. I succeed to build a 32-bit version of ReadStat on Appveyor with some fix related to iconv. That being said, the package GNUWin32 is only for 32-bits machines and does not provide 64-bits libs (or static versions as well as debug ones). The unofficial repository https://github.com/pffang/libiconv-for-Windows.git does provides the required additional compiled versions for a proper x64 build. With this, the build succeed.

@evanmiller
Copy link
Contributor

@zebrys While you are working on Windows support, you may also want to take a look at issue #200

@zebrys
Copy link
Contributor Author

zebrys commented Sep 10, 2020

This last version should be complete. The CI on MSVC/Windows seems to work correctly. The project has been tested successfully on multiple machine with MSVC 15 and 17!

PS: @evanmiller Ok.

@jonathon-love
Copy link
Contributor

oh, sorry evan, we assumed #200 was by design ... we would have submitted a PR long ago if we'd realised you were interested.

i'm sure you'll figure it out @zebrys, but it's just a simple substitution of open() for _wsopen()

here's the implementation jamovi-readstat and pyreadstat use:

https://github.com/jamovi/jamovi-readstat/blob/master/jamovi/readstat.pyx#L26-L41

(in cython).

cheers

@zebrys
Copy link
Contributor Author

zebrys commented Sep 11, 2020

The CI seems to fail due to the MSVC configuration and not the branch as the same CI type work on the same branch content. Although I think MSVC 15 should be used regarding the documentation, the actual build version is 16.7 on our CI and 14.1 on your and it seems to prevent the build...

@zebrys
Copy link
Contributor Author

zebrys commented Sep 11, 2020

The problem seems to come from the MSVC SDK compatibility. Our CI was using MSVC 2019 by default with the specified OS (despite the Appveyor documentation). The 4 last commits try to enforce MSVC 2015 (even with MSVC 2017/2019 using an older/legacy compiler). Although we try our best to fix the compatibility issue, but we did not succeed so far. Using explicitly MSVC 2017 or 2019 works well in the CI. What about using MSVC 2017 by default for the CI (at least temporary)?

@zebrys
Copy link
Contributor Author

zebrys commented Sep 11, 2020

The last commits solve the SDK compatibility. However, this is far from being ideal as a very old SDK has to be used with the current available VS2015 (see the message before).
Thus, a choice should be made between merging this PR as is (with MSVC 2015 enforced and a old SDK) or using a newer version of MSVC 2017 without enforcing the compiler or the SDK.

@evanmiller evanmiller changed the base branch from master to dev September 11, 2020 11:44
@evanmiller
Copy link
Contributor

Generally I prefer newer tooling, and as development goes forward with the new CI configuration, I'd rather not spend my time chasing down errors with old SDKs.

Since the code base now builds with MSVC 14, I'm okay with using a newer compiler for CI and just waiting for user bug reports about MSVC 14 if it breaks in the future. Is that okay with you, @jonathon-love ?

@jonathon-love
Copy link
Contributor

yeah, that's fine. (MSVC 14 is MSVC 2017 i think).

cheers

jonathon

@evanmiller
Copy link
Contributor

@zebrys Go ahead and configure it whichever way is most convenient for your team. When you give the word, I'll merge it in.

@zebrys
Copy link
Contributor Author

zebrys commented Sep 15, 2020

Tank you. You can now merge this PR!

@evanmiller evanmiller merged commit d465d52 into WizardMac:dev Sep 15, 2020
@evanmiller
Copy link
Contributor

Thank you very much for all the work that went into this!

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.

5 participants