W64devkit: A Portable C and C++ Development Kit for Windows
nullprogram.com
nullprogram.com
Completely agree, Autoconf needs to die. C++ builds don't need to be complex and fragile, even if many environments are supported. Just use a minimal CMake build and resist the urge to implement "clever" things in it.
For example, requirements for what has to be static and what dynamic are different on Linux (distros want unbundling) and macOS and Windows (you can link to handful of things that ship with the OS). macOS is especially annoying, because if you're not careful, you'll link dynamically to homebrew's non-permanent locations. `find_package` doesn't do the right thing without clever non-minimal things around it.
I'm not claiming that spaces should not be supported, but you can live without them.
You can rent a Linux box for $10 if you want to compile.
For example, it checks for memcpy by writing this program:
void memcpy(void);
int main() { (void)memcpy(); }
and checking if it compiles. Which is of course illegal and completely nonsensical C code that no one would ever write. It is not entirely unreasonable for a compiler to error out if it sees this code, and ince we were working on an aggressive pointer analysis for C, we did do so. Of course, since the compiler failed on this code, autoconf concluded that the system didn't have memcpy, and it helpfully provided its own implementation... which immediately crashes the build (after autoconf completes, of course) for multiple redefinitions of memcpy.To top it all off, it appears that there has never been any system that didn't have memcpy that was capable of running any version of autoconf, let alone any software written this millennium. This check literally has no benefit, and is "complex code [added] for no reason."
Warning might be useful.
https://www.gnu.org/software/libc/manual/html_node/Reserved-...
> I don't expect compiler to care about it, just put function call into object code and move on.
memcpy actually has a few semantics that cannot be legally expressed in C (specifically, strict aliasing doesn't kick in). All modern compilers actually define memcpy as a compiler-builtin, and they rely on memcpy's semantics for optimization purposes.
Or, put more bluntly, memcpy does not correspond to a function call in object code. C is not a thin wrapper around assembly code, and has not been so for quite some time.
The actual test comes later: the real memcpy with three arguments, if it exists, is mislinked into the test executable; if memcpy cannot be found linking fails.
The test program would hopefully crash badly in case it is actually run, but the test is not about memcpy behaviour or about what the parameters of memcpy are.
memcpy is so magical that some specific undefined behavior is allowed. It's more than a mere library function- it's closer to a compiler intrinsic.
If you want to use a non-standard function that coincidentally happens to be memcpy, you have to use special compiler flags to inform the compiler that C library semantics are not in effect (e.g., -ffreestanding). Of course, the entire point of these checks is to figure out if the C library has these functions in the first place, so using these options during the tests would defeat the purpose.
> The actual test comes later: the real memcpy with three arguments, if it exists, is mislinked into the test executable; if memcpy cannot be found linking fails.
The actual test both compiles and links the executable, although it (obviously) doesn't attempt to run it. Whether it fails in the compile or the link step is immaterial to the test, as it fails either way.
Are you sure? I hear this a lot, but I don't think it's really true. char pointers are allowed to alias pointers to any other type in standard C (C17 6.5p7,) so unless I'm missing something, a correct C implementation of memcpy could just cast both its arguments to char pointers and copy them char-by-char.
I agree with your premise that the call to memcpy() is undefined, but I think that's entirely because of the conflicting definition, and has nothing to do with any special semantics of memcpy, so it would equally apply to any other standard library function.
I'm not completely certain. I have definitely seen this assertion before made by people more well-versed in the C standard than I, but I don't recall the exact argument. I think it may be the case that the write-via-char causes the effective type of memory to change (so it can only be read by char from that point on), but again, I don't trust my judgement here.
memmove on the other hand can't be implemented in standard C, but it can be implemented on most platforms using only implementation-defined behaviour, because casting a pointer to an intptr_t is implementation-defined, but on most platforms with a flat memory model, it gives you the linear memory address.
The compiler has special knowledge of memcpy and may introduce calls to it even if you don't call it anywhere in your code - see this : https://gcc.godbolt.org/z/_w78qd
The code above does not do so.
Contrary to what other people have suggested, according to my understanding of the C language standard, the word `memcpy` is not magical in any way, and undefined behaviour only comes in because the code will call `memcpy` with incorrect signature.
I guess this is not an argument that the above code to check for memcpy() is illegal, per se, but it's at the very least totally superfluous.
But every C build system will have to deal with some mess. That may be having multiple configurations for multiple OSes (with weird exceptions for weird OSes or WASM), snowflake libraries that just had to have their own pkg-config replacement, fiddling with flags for various flavors and versions of MSVC and non-MSVC compilers, rpaths, sovers, etc.
To me it's inevitable that every project that starts with "just 5 lines of simple CMake/Makefile, look how simple it is!" will end up with the same mess everyone else ends up with.
Coupled with an awful language, I just can't say that CMake is better, or much better, than autotools. But at least it handles Visual Studio and ninja. So yeah, we use it for the support it provides which is second-to-none, but it's a mostly terrible user experience, frankly.
And now it's even better to couple it with Conan for dependency management
Maybe CMake is not the best but is good enough and the new standard in the C++ world.
You say that but frankly between CMake code and Ant or msbuild ...
After 10 years they are finally introducing AAR support for NDK projects, which is also built on top of cmake.
Maven, Ant, implemented in Java
Sbt, implemented in Scala
Leinigen, implemented in Clojure
MSBuild, implemented in a mix of C++ and C#
Gems, implemented in Ruby
eggs, implemented in Python
cargo, implemented in Rust
Why should it be any different for C and C++?
So vcpkg it is.
(I’m not sure why I decided to share this right now. Sorry.)
That said one thing I like about autotools is how it generates a build system that doesn't depend on more than shell script and make, and can be packaged that way. It's annoying that CMake produces a build system that requires CMake to be present to build.
You know, what I'd _really_ like, the more I think about it, is consistent a way to treat the "build system" simply as a separate project than the "source code". It's just silly how coupled the build system can become with the specific project it's been designed for. More thought is needed towards decoupling build systems from their corresponding target code. The last thing I want as a C/C++ developer is to have to maintain multiple build systems AND make sure everything is correct about what "package files" (pkg-config, CMake targets, etc) they install and where. Lately I find more and more time is spend maintaining build systems than the actual code, and it's very frustrating, and only gets worse as more and more "solutions" become available that require whole new build systems to be designed and integrated. I have several projects that have at least 2 build systems in their repositories, and we have to constantly test them both and make sure they do the same things.
Just a little example of how clever it is:
If you want to cross compile to a target, you can't actually run the test programs you compile. So it becomes important to make them fail at compile or link time, not at run-time. If the expression to be tested is constant at compile time, autoconf will compile a test program that uses the expression in the size of an array type. So, let's say you want to check if sizeof(int) is at least 4. Then autoconf would make a test program that declares something like
typedef char foo[1-(sizeof(int)<4)*2];
If sizeof(int) is less than 4, then the array size would become negative, and that produces a compile time error. No need to actually run the program.
That is the place where I learned about this pattern many years ago. I'm still grateful for autoconf for teaching me that. This is how you did compile time assertions in C before C introduced _Static_assert.
Also note that autoconf has lots of other awesome features if you would like to learn about them. For example it can be configured to have a system-wide cache for test results, which speeds up future runs.
But the most important part of autoconf to me is that it is dependably configurable. On my system, I have both /usr/lib and /usr/lib64 so I can develop for both 32-bit and 64-bit worlds at the same time. Run configure with --libdir=/usr/lib64 and you are good to go. There is no single way to reliably do this with cmake. Among the conventions, -DLIB_SUFFIX=64 has apparently crystallized out as de-facto standard, but you can't rely on it. For LLVM you have to set LLVM_LIBDIR_SUFFIX instead.
Or let's say you want cmake to also look for include files in /usr/X11R7/include and for library files in /usr/X11R7/lib64. Good luck with that!
I'm not trying to say that cmake is bad. It is a good solution for what it is trying to achieve. But automake had higher goals and also achieved them.
But I think cmake vs autoconf is a false dichotomy. cmake is not the enemy here, and neither is autoconf.
I'm actually more disappointed in the hubris of all the people deciding they can make better build systems than automake or cmake and then roll their own, but consistently fail in situations that other people already encountered, thought about, and solved. We now have autoconf, cmake, jam, bjam (Boost), qmake (Qt), meson, waf, python and perl have their own stuff, everybody believes they need to ram a package manager down my throat as well.
To me, life time is the only resource that actually matters.
For every 5 minutes you think saved by not going autoconf, you made your "build from source" users waste 50 minutes per person to get your build scripts to work.
That's the biggest issue with autoconf... it tends to spend a lot of time basically asking the question "are you some weird Unix from 1997?" that no one actually cares about the answer to, because people don't take the time to curate their build systems.
At its core task of figuring out how to compile and install properly on a system, autotools can do a surprisingly bad job. One of its most frustrating habits is dropping flags from the linker invocation ("I don't know what -flto does, but I'll drop it from the linker even though the user explicitly told me to link with it because unknown flags tend to bad for the linker").
Yes, build systems are a complicated three-way tussle between the system, the developer, and the user, and we've done a very bad job as a community at finding good tools to mediate this tug-of-war.
About the LTO stuff: Maybe you did it wrong? I just tried to reproduce your problem by downloading GNU coreutils and running configure like this:
$ LDFLAGS=-flto CFLAGS="-Os -flto" ./configure
it definitely passes -flto to the compiler. I can't get to the linking stage because binutils is missing a plugin. Probably something wrong with my gcc setup. But autoconf is not removing -flto.
BTW: About non-standard environments: Go ask among your friends for people who do embedded systems development, and let them show you their environment. You'd be surprised.
In Python:
. ./venv/bin/activate pip install -r requirements.txt
In Java:
mvn build
In C++:
cat README.txt ..... now go apt install some packages. Except they might be named differently in your distro. Also some of them might be a different version but my README specifies no version, so it might fail to compile or error out later because of a header mismatch. Also the one available in your distro might not be compiled with the "X_POTATO=1" flag (which you will discover somewhere along the way is required), so its time to build that dependency from source and restart this whole dependency management hell one layer down have fun :)
There are different versions of compilers supporting different features floating around, but Makefiles or whatever don't specify that. Build dependency management is frequently something like `sudo apt install...`, tying the build process into packages installed in the OS itself rather than being sandboxed which creates all sorts of problems.
But when people use CMake? Ugh. I am in for a world of hurt, and probably so many custom assumptions about how to interface with the compiler and the operating system that not only is cross compilation probably off the table, but compiling it at all for one of my target platforms is likely not going to work :/. I pretty much always end up having to just throw away your build system entirely and start over from scratch just to even test the project for my target. OMG: a bunch of projects like using tools like Meson... the entire Meson 0.52.x line entirely broke the ability to cross compile to targets like Android on macOS as it incorrectly detected stuff about the target linker using the host compiler, and as it is largely a black box the only real way to deal with this is to avoid 0.52.x forever (I filed an issue about this right as 0.52 came out but they failed to fix this serious regression until 0.53).
And in languages other than C++? Ha ha ha omg they are all so horrible :(. I have been fighting for a month now trying to get a Rust library compiled to a static archive so I can link it into my project, and have nothing but a long list of bugs filed with upstream and workarounds for their broken build system to show for it, as in the end I finally decided it just wasn't worth dealing with anymore: bindgen passes the wrong target to clang for iOS (with no way to work around it as the person doing the compile, and often this happens in some deep dependency which makes overriding that really hard, as the build system believes it should be in charge of building dependencies), buildcc incorrectly mixes up HOST_CC and TARGET_CC when you are doing a cross compile to a target (such as CentOS 6) from a host (such as Ubuntu bionic) that shares a triple, the MinGW build not only tries to link against some entirely-wrong set of libraries that comes with Rust (they just last month shipped a broken fix for this that works in cases where you aren't passing a custom sysroot... but if you are doing MinGW seriously you have a custom sysroot so you can be compatible with MSYS2) but it also embeds into the archives parts of the target's standard libraries (which is just crazy: they apparently do this for MinGW, musl, and one other target), and I can't yet for the life of me get it to generate reproducible builds even for simpler packages (which is normally trivial with C++--yes, even with autoconf--I need to spend more time looking into this problem and filing issues about it, though).
What I don't get is the autoconf failed to update it to the modern situation and still keeping the behaviour that is totally irrelevant and considered harmful today.
https://andrewkelley.me/post/zig-cc-powerful-drop-in-replace...
The latest version of the VS2019 installer already packages clang 10.
If it is true, it would be amazing. So many of my APIs are unwieldy because of Microsoft's refusal to support this one feature.
Read the comments, it should be available in the next VS2019 update.
It tries to embrace it, but this is not necessary (and limited for non-trivial cases - and what isn't non-trivial for serious projects). CMake itself has good VS support for a long time. I always try to create VS solutions in parallel with ninja, nmake, jom, whatever completely from it without hand-holding of some integration.
Using such tools for me was only to do university work at home.
I dislike Visual Studio because to set things up you have to compare online screenshots to your settings and click around multiple tabs of configurations. In the end you (as beginner) don't know what libraries get included and if it will work on another computer. GNU tools are text-driven, so easier to replicate and to reason about.
We did not do bare bones C already in those days.
https://visualstudio.microsoft.com/downloads/#build-tools-fo...
But you are swimming upstream if you use the GNU tools for general Windows development. Windows is a complex, integrated system that has evolved over a long period of time and is very different from unix-like operating systems. The peculiarities that exist in MSVC and associated tools are not just the whimsies of Microsoft devdiv engineers and product managers, but often tie in to operating system functionality. If you try to develop Windows applications with mingw, you will eventually hit weird problems and errors that simply would not exist if you used Microsoft's toolchain.
If you want an alternative to Microsoft's compiler, then consider llvm, which is aiming for full ABI compatibility and even produces PDBs (though I'm skeptical that they're as complete as the ones generated by Microsoft's tools). But the compatibility is incomplete, so ymmv.
Microsoft seems to aim for deep integration of Linux in Windows per WSL. They even showcased that they are working on Wayland support for WSL, which would enable using Linux GUI applications directly on Windows.
We may soon get at a point where Linux applications feel as native on Windows as native Windows applications (which use quite a variety of toolkits anyway), as the integration deepens.
Now WSL still uses larger distribution images. But very little holds them from supporting thin Linux images with just the necessary dependencies that launches in Windows as any other application.
Using LLVM for ABI compat is a good tip though!
I don't know if it will ever come with a vanilla Windows Home edition install but somehow I doubt that.
I don't see what they have to lose. Windows is still used widely in business. But their lock-in has drastically reduced with the rise of iOS, Android, and web apps. Making Windows more attractive as a platform for developers to deploy applications, even if it is through the WSL subsystem will make Windows as a platform more competitive to these other ecosystems.
I am currently writing scientific software. But we have stopped building Windows versions, since these programs work great with WSL and it is far less effort than building these applications separately with Visual C++.
I'm currently in a weird spot with the software I distribute because the majority of the users "know enough to be dangerous" but aren't software engineers/IT professionals. We want them running code and using Linux like a pro, but there's a lot of training/documentation overhead just for our *nix builds and the friction to getting that up and running for WSL is daunting.
Luckily MS understands B2B native more than anyone else so I'm hopeful they'll have a solution eventually, but I'm not holding my breath until then.
I am not sure what the difference is, when WSL gets deeper and deeper integration with Windows. WSL2 does not use the personality mechanism anymore, but that's just to make it easier to fully support Linux. But once Linux becomes a personality of Windows in the informal sense, targeting Linux means targeting Windows at the same time.
But not as miserable as trying to use gnu tools for building C++ on windows. I have over a decade of exprience in that and it's always equally painfull.
There were good reasons to use GNU tools when MSVC did not have thorough enough support for C++11 and GCC did. Now MSVC is pretty good with the latest C++ standard.
Besides, Windows now comes with a real posix virtualized environment - Windows subsystem for Linux - which works great - use that, not msys2 or cygwin if you must have a gnu build system on a windows...
This has been an open issue for literally years now. But apparently nobody cares enough to fix it.
The whole reason I installed a cross compiler to Windows is so I could cross compile some search engine code I wrote that uses C++ threads. But if you have a copy of the header and run-time files from Visual Studio (there is a free edition of that now) you can actually use clang to cross compile for Windows from Linux just fine, including C++ threads.
That being said, I often use my own STL like mutex class which wraps SRWLock on Window and pthtead_mutex_t on Linux/macOS.
Also, as I heard it, winpthread combines the warts of windows threads with the warts of pthreads, doing neither justice.
I once created such a pocket-development setup, throwing in Fossil [0] for version control and project tracking, Notepad2 (notepad2-mod) [1,2] for editor. Actually, Git can also be set up for pocket, which then brings the whole MinGW for this.
I wanted to fit Geany [3] for more like IDE experience. It's also Scintilla based, but needed GTK, so I chose not to bother back then.
Android NDK can be set up this way too. It packs LLVM, so here it is another capable compiler to do the lab-work.
Just learn to program in portable C way, that is know the platform-specific ways to do certain things (threads, network) and #ifdef it properly. However this is a whole other story about how this sort of portability could be handled.
[1] https://xhmikosr.github.io/notepad2-mod/
[2] http://www.flos-freeware.ch/notepad2.html
[3] http://geany.org
I'm doing all local development on Linux and since msys2 is there by default getting the windows compile done is a bliss. Just need to bring msys2 to your path and all the rest (git, g++, linking, etc) is the same as in your Linux env. E.g. this is my prefix for the cross-build https://gist.github.com/dominicletz/5e50c49aa1bf30d2485f5bec...
C:\Program Files (x86)\Microsoft Visual Studio\2019\{edition}\VC\Tools\MSVC\{version}Edit: I feel like this whole discussion highlights the issues with the vs installer though. If it wouldn't create hundreds of folders all over the place it would be much easier to have a sane discussion about it.
https://visualstudio.microsoft.com/downloads/#build-tools-fo...
Edit: To nobodies surprise, the installer still doesn't work. The command line shortcuts it installs fail to properly set up the path. At least it looks like they are trying to print a sensible error message in the command prompt now though. Still not quite sure how they manage to fail this badly though.
https://www.kauffmann.nl/2019/03/04/how-to-install-docker-on...
https://blogs.vmware.com/workstation/2020/01/vmware-workstat...
This has to change first (the links giving some kind of hope).
I'm in 3D and image processing software, often with GPU interface requirements involved. I can reconcile some aspects in the virtual environments, but yet in the single case not all of them. On top of each other and according to experience, difficulties multiply.
It comes packaged as Docker Toolbox.
And M-Windows is a pristine example of orderly genius?
I support his right to his opinions. But here he is, running Docker and a pile of opinions to build himself a portable C&C++ env on M-Windows. (To use gcc, no less.)
Software in general is a mess. But that doesn't prevent great tools from being developed. There's a reason for the success of GNU and FOSS.
> For most users, the value is in the 78MiB .zip available in the “Releases” on GitHub.
immediately followed by:
> It’s “portable” in that there’s no installation. Just unzip it and start using it in place.
https://github.com/skeeto/w64devkit
It clearly says it's a dockerfile.