Skip to content

Instantly share code, notes, and snippets.

@MaxClerkwell
Last active January 23, 2022 22:06
Show Gist options
  • Save MaxClerkwell/095172ee27a6693378d435cc2249f8ff to your computer and use it in GitHub Desktop.
Save MaxClerkwell/095172ee27a6693378d435cc2249f8ff to your computer and use it in GitHub Desktop.
Compiling a Copperspice Example from WSL
Windows is one of the greatest operating systems ever built.
There is no doubt about that, but on the other hand it sometimes can be a real pain.
Especially when it comes to reproducable routines.
The Command-Promt features tools as running .bat files, but when it comes to ease of use, I personally prefer zsh.
zsh, bash or other text-based shells don't come with Windows naturally.
Anyway, it is possible to get these running.
One of the possibilities to achieve that is running the Windows-Subsystem-for-Linux, abbr. WSL.
WSL is a compatibility layer for Windows systems that makes it possible to run Linux executables.
With WSL Microsoft provides a way, that helps getting most of the good Linux stuff, while plugging some gaps
in Windows.
WSL can be installed as explained by the Microsoft Docs at https://docs.microsoft.com/en-us/windows/wsl/install.
To follow this tutorial, it is advised that WSL is installed correctly and have basic knowledge about the following concepts:
* Navigating the filesystem with a text-based shell
* Using a compiler from the command-line
* Shell environment variables
* Package-Management via `apt`
* Using `7zip` and `tar`
* Using `wget`
----------------------------------------------------------------
Step 1: Preparing the Filesystem
After revving up the engine of your WSL, your present working directory will be displayed as `~`.
The tilde is shorthand for the current users Home-Directory, sort of the UNIX analog to `My Files` in Windows.
Its full path is `/home/<Username>/` - you may also get this output by calling the `pwd` command, which stands for
`print working directory`.
Using the command `cd /` one jumps to the root-directory of the the UNIX-like filesystem that the WSL provides.
One of the great features of the WSL is, that it also allows access the Windows filesystem by navigating to `/mnt/c/`.
The WSL __mounts__ the Windows filesystem on drive `C:\` into that directory.
In order to install everything correctly some directories need to be set up.
The exact directory name here doesn't matter, but make sure that you will be using the same paths over this whole
tutorial in order for it to work.
Mine are set up as follows:
- Path of my gcc and g++ Installations via MinGW 7.3.0: `C:\MinGW\7.3.0\`
- CMake 3.16.8: `C:\CMake\3.16.8\`
- Ninja 1.10.2: `C:\Ninja\1.10.2\`
- CS-Libraries: `C:\Copperspice\1.7.2\`
- My CS-Projects: `C:\CS-Projects\`
You may also use whatever path suits your needs, just make sure, that you stick to your own naming-convention.
Keep in mind, that the longer your paths get, the compilers diagnostics-messages may get unconsumably long.
In the following examples we will often be switching between Windows and UNIX notation.
As a simple mnemonic: If the path contains a Backslash `\` you can assume it depicts a Windows-style path.
If the path contains a (Forward)slash `/` you can assume it is a UNIX-style path.
Since we will be writing some paths more often - and people like me hate to write that much - we will be using ENVs.
----------------------------------------------------------------
Insertion: Environment-Variables
Environment variables - abbr. ENVs - are dynamic string values a programm or process uses to store single pieces of
information.
Our shell systems - wheters it's bash; zsh or cmd - all provide the possibility to temporarily or permanently store
string-values in such ENVs.
If you are running a UNIX shell, such as the WSL, try running the command export in order to see the current state of
your ENVs.
In cmd the command set does the same.
To set a new ENV in UNIX, you may write export MYVARIABLE=Tutorial.
Just so you know, if you export a variable with the name of an existing one, the previous value will be overwritten.
To do the same in cmd, you may use set MYVARIABLE=Tutorial (make sure NOT to type any whitespace between the =-operator
and its left- or right-hand argument.
From that point on forward, this ENV will be set until the shell-session ends.
You can easily use them for example when you want to switch your current working directory.
Imagine you'd have set up an ENV called WORKDIR on your UNIXoid-system.
Executing echo $WORKDIR would print /mnt/c/myworkdir/.
But you may also switch to that directory by executing cd $WORKDIR.
Just so you know: If you want the content of an ENV variable you need to dereference the variables name by using the
$-operator.
It works just the same in cmd, except for the syntactical details of dereferencing it by using the %-operator before and
after the identifier: echo %WORKDIR%.
If you know, that some ENVs might be valuable for specific tasks, you may also put them into a file, that you can load
manually, whenever you'd like to perform a specific task.
At least that's the way I prefer to use my ENVs.
Simply use a text-editor - like vi or notepad - to create a file.
On Windows you'd call it something like setup_myvariables.bat and on UNIX something like setup_myvariables.env.
Just write the commands to set the variables.
Let's make a .env-file in our WSL in our home-directory.
`vim ~/copperspice.env`
Enter the following lines, save the file and quit.
`export WIN=/mnt/c/`
`export CMAKE=$WIN/CMake/3.16.8/`
`export GCC=$WIN/MinGW/7.3.0/`
`export NINJA=$WIN/Ninja/1.10.2/`
`export CSLIB=$WIN/Copperspice/1.7.2/`
`export WORKDIR=$WIN/CS-Projects/`
You can now run source ~/copperspice.env and this will load all ENV.
Isn't that awesome?!
In your cmd you'd use set in the script and not source, but call to load it.
----------------------------------------------------------------
So let's create the desired directories by using the command __Make Directory named <Directory-Name>__:
`mkdir $CMAKE $GCC $NINJA $CSLIB $WORKDIR`
Of course you could also write each of them individually.
----------------------------------------------------------------
Step 2: Installing the compiler
"This is my compiler. There are many like it..." - when arguing about C++ compilers, mind the gap.
Not often, but every once in a while speaking with people about compilers might end in a holy-war.
There are a couple of C++ standart-conformant compilers available and all of them may bring some advantages.
For this tutorial we will be using the GNU Compiler Collection - abbr. gcc.
----------------------------------------------------------------
If you'd like to use another, that's great, you may even shoot me an email and I may add a section to this very
tutorial.
Don't forget, that you might need a different version of the precompiled libraries in order to work with a different
compiler - search for ABI compatiblity if you'd like to know more about that.
----------------------------------------------------------------
Unfortunately there is no __original__ distribution of gcc for Windows, but there is a project called __Minimalistic GNU
for Windows__ or short: MinGW.
It contains a couple of GNU Utilities for Windows-Users.
If you'd like to learn more about MinGW, check out their website www.mingw-w64.org.
There you will find a download as well.
But since I don't like searching websites for links, I also put up a specific MinGW Version on my server that you can
download if you'd like.
Just execute:
`cd $GCC && wget www.auto-intern.de/x86_64-7.3.0-release-posix-seh-rt_v5-rev0.7z`
This will teleport you into the previously specified directory and download the archive that contains MinGW.
You can extract the archive using 7zip.
`7z x x86_64-7.3.0-release-posix-seh-rt_v5-rev0.7z`
For convenience I copied all files from the now extracted folder into the $GCC directory and deleted the archive as well
as the now empty mingw64 directory.
`mv ./mingw64/* . && rm -rf x86_64-7.3.0-release-posix-seh-rt_v5-rev0.7z mingw64/`
If everything went well, `tree -d -L 1` should print something like this:
```
tree -d -L 1
.
├── bin
├── etc
├── include
├── lib
├── libexec
├── licenses
├── opt
├── share
└── x86_64-w64-mingw32
9 directories
```
----------------------------------------------------------------
Step 3: Installing Ninja
Ninja is a Buildsystem that was designed to be as fast as possible.
Buildsystems basically operate our compiler when we are running them.
So instead of typing long and weird commands to call our compiler in a specific order, we will let our buildsystem do
that.
The installation-process is quite similar to the installation of our compiler.
Download; extract; delete unused artifacts.
`cd $NINJA && wget www.auto-intern.de/ninja-win.zip && unzip ninja-win.zip && rm ninja-win.zip`
Done.
Ninja only needs one executable binary, so don't be suprised.
No we can write scripts for the Ninja-Build-System and call Ninja with that script in order to run our compiler.
----------------------------------------------------------------
Step 4: Installing CMake
Ninjafiles are human readable (at least Ninjas authors say so) but they are not easy to write.
This is why we need CMake.
CMake is a __Build System Configuration Management Tool__.
It comes with it's own turing-complete scripting language and its purpose is to generate a recipe for Ninja.
In order to do that, a CMake script includes all informations about the buildprocess of our program.
Make sure to read one of these and you might understand what it means.
Written in this script `CMakeLists.txt` that is located in the top-directory of projects that are build with CMake are
requirements so to say.
When CMake runs, it tries to figure out, whether you've got an appropriate compiler; if you have all necessary libraries
installed and so on and so forth.
The result of a successfully run CMake script is a build-configuration-file for a specified buildsystem.
In our case, this will be Ninja.
`cd $CMAKE && wget www.auto-intern.de/cmake-3.16.8-win64-x64.zip && \
unzip cmake-3.16.8-win64-x64.zip && mv cmake-3.16.8-win64-x64/* . && \
rm -f cmake-3.16.8-win64-x64.zip`
Oh, did you see what I did there?
A backslash in a our WSL.
No, that is not a path, it is just an __escape symbol__ that makes clear, that the next character isn't actually part of
the command, but a control-command to the shell.
In this case, the character after the \ was a return.
If it wouldn't be there, the command would've been executed, but this way, we can use multiple lines to write our
command.
If `tree -d -L 1` displays:
.
├── bin
├── doc
├── man
└── share
4 directories
You probably did everything correctly.
----------------------------------------------------------------
Step 5: Installing the CopperSpice-Libraries
`cd $CSLIB && wget www.auto-intern.de/setup-CopperSpice-1.7.2-x64-MinGW.exe`
As you can see, the CopperSpice libraries come in an executable.
But this is no problem, we can call them from the WSL anyway.
Just run them by typing:
`cmd.exe /c ./setup-CopperSpice-1.7.2-x64-MinGW.exe`
Wait what? - Yep, you are reading correctly.
From your WSL you may run a cmd and with that execute any Windows-application.
Make sure, to install the CopperSpice Libraries into the directory you specified before.
In my case it was `C:\Copperspice\1.7.2\`, remember?
After that, we probably have installed everything we need.
---------------------------------------------------------------
Step 6: Checking the installed Software
MinGW, CMake and Ninja were all installed as Windows-Packages.
It is possible to run them directly in WSL, but we'd rather run them in a subprocess of our WSL.
What we will do is:
1) Start a new cmd
2) Load all of our program paths into the cmd-ENVs
3) Run all three previously installed tools
What we need to know before we do that, is that UNIXoid shells as well as the cmd have one rather special ENV.
A variable named PATH.
This variable contains a list of paths, that a possible binary will be searched in when the shell is trying to run a
command.
So every time you run `cd` or anything like that, the shell actually looks into the $PATH or %PATH% variable (if you
don't know the difference read the insertion into step 1 again please) and tries to find an executable, whichs name
matches the input.
So lets go.
`cmd.exe`
We opened a cmd-shell in the same terminal-emulator as our WSL had previously.
Typing `exit` closes the subprocess and brings us back to our WSL.
`cmd.exe`
`set PATH=C:\Ninja\1.10.2\;%PATH%`
Remember we are in a Windows-World after we launched cmd.exe.
Therefore the path is written in Windows-notation.
The `;%PATH%` makes sure, to append the content of the %PATH% variable to the new value of %PATH%.
As written before, otherwise our %PATH% would only contain `C:\Ninja\1.10.0\`.
But since Windows has some default paths we'd like to keep them - at least for now.
Basically just for telling you that this is the usual way to not lose the previous content of an ENV.
`ninja --version`
The output should tell you the current Ninja Version you are on.
If so, Ninja is installed correctly.
Congratulations!
If not however, make sure to check that you installed Ninja into your previously defined directory.
Now `set PATH=C:\CMake\3.16.8\bin;%PATH` and run `cmake --version`.
Do you see the CMake version-info?
Great!
Same with g++.
`set PATH=C:\MinGW\7.3.0\bin\`
`g++ --version`
Allright, everything should be installed just fine.
---------------------------------------------------------------
Step 7: Compile and run a minimal example
`cd $WORKDIR && wget www.auto-intern.de/csmini.tar.gz && tar -xzf csmini.tar.gz && rm csmini.tar.gz`
This teleports you into your specified work-directory, downloads a minimal example from my webserver, extracts the
directory `example1` and deletes the tarball.
In order to build this example, we'll need to perform the following steps:
1) manually create a directory $WORKDIR/example1/build - this will serve as a place for CMake- and Ninja-Files
2) write a batch-script for the cmd that loads necessary paths and runs CMake and Ninja
`mkdir $WORKDIR/example1/build`
`vim $WORKDIR/example1/compile.bat`
Into the .bat file we will insert the following:
```
set WORKDIR=C:\CS-Projects\
set PATH=C:\Ninja\1.10.2\
set PATH=C:\CMake\3.16.8\bin\;%PATH%
set PATH=C:\MinGW\7.3.0\bin\;%PATH%
set CopperSpice_DIR=C:\Copperspice\1.7.2\cmake\
cd %WORKDIR%\example1\build\
cmake -G "Ninja" ^
-DCMAKE_BUILD_TYPE=Release^
-DCMAKE_INSTALL_PREFIX=..\deploy^
..
ninja install
exit
```
Okay, that's a lot.
First we are setting some ENVs for our session.
The CopperSpice_DIR is a variable that has to be set in order for the CMake-script to find the Copperspice Libraries.
Alternatively, you could delete that line and add it to the CMake call with:
`-DCMAKE_PREFIX_PATH=C:\Copperspice\1.7.2\cmake`
After that we are switching into the actual build directory.
From there we are calling cmake to generate a Ninja-configuration.
The two dots only mean, that the CMakeLists.txt file that we want CMake to run is in the current parent-directory.
`ninja install` then runs Ninja, which compiles and copies a couple of dynamically linked libraries and our final
executable into `%WORKDIR%\example1\deploy\`.
`exit` basically just ends the cmd-session.
We can now call our `.bat`-script with `cmd.exe /c $WORKDIR/example1/compile.bat`.
After everything finishes, we can run `$WORKDIR/example1/deploy/example_1.exe`.
There it is, your first CopperSpice Application without taking your hands of your keyboard.
Hope this might be useful someday for you ;)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment