Author Topic: Setting Up Command Line Building With Mingw And MS VC  (Read 36779 times)

Frederick Harris

  • Newbie
  • *
  • Posts: 47
Setting Up Command Line Building With Mingw And MS VC
« on: November 19, 2021, 05:23:12 pm »
     It has come to my attention that in spite of all I have written on command line compiling with  C or C++ over the years, and in various forums,  that there are still folks who can't get it to work.  This astounds me.  I guess I shouldn't let it bother me but for some reason it does.  And I don't mean to come across as someone 'pushing' command line compiling on anyone.  I simply consider it a worthwhile skill (one among many) for a serious coder to consider or master - especially for new C or C++ coders who are having trouble dealing with some of the complexities of learning C++ with Visual Studio.

     I suppose what has prompted me to write this is that recently I acquired Bjarne Stroustrup's "Programming Principals And Practices Using C++", Second Edition, copyright 2014, and right at the beginning of the book, before even getting into any real code, to my surprise he mentions this for beginners just getting started....

Quote
If you are having trouble with one of the popular, but rather elaborate, IDEs (integrated development environments), we suggest you try working from the command line; it is surprisingly simple.  For example, here is the full set of commands needed to compile, link, and execute a simple program consisting of two source files, my_file1.cpp and my_file2.cpp, using the GNU C++ compiler on a Unix or Linux system:

c++ -o my_program my_file1.cpp my_file2.cpp
./my_program

Yes, that is really all it takes.

     So apparently I'm not alone in my feelings about this.  I'm also aware that on a personal level I tend to be somewhat idiosyncratic - maybe we all are to some extent, and I figured my own personal predisposition to build from the command line was rather unusual, and that very few programmers do as I do.  But further on at the beginning of Stroustrup's book on page 52 after presenting his "Hello, World!" first program, and in his discussion of the compilation and linkage step, he mentions this....

Quote
If you work from a command line window, as many professional programmers do, you will have to issue the compile and link commands yourself.  If instead you use an IDE (integrated development environments), as many professional programmers also do, a simple click on the correct button will do the job.

     So maybe I'm not so idiosyncratic as I thought; Stroustrup seems to be saying that there are a lot of coders in the world besides James Fuller and I who tend to prefer building from the command line.  And as I've said many times, and none other than the very famous Bjarne Stroustrup - creator of the C++ programming language, has seconded, it isn't at all hard to do.  So why can't many folks manage it in spite of the excruciating detail I've gone into in my various expositions of the topic?????

     I've agonized over this and wondered where have I failed in my explanations of the topic.  It occurred to me that just because I think something is easy and obvious doesn't really make it so for someone who doesn't know how to do it.  Please allow me a bit of a digression. 

     I'm a poor mechanic.  If I had my life to do over (and I don't) I'd have spent more time when growing up learning mechanical skills and working on cars and things like that.  But it seems like I always had my head in some book or other, while my friends were taking car motors apart, rebuilding them, etc.  In 2012 I bought myself a Toyota FJ Cruiser for off road use and for exploration of the mountains and deserts of the Western US, but I wasn't satisfied with the stock highway tires on it.  So I bought myself some large and heavy 'off road' tires.  Turns out the suspension of the truck, which was tuned for the smaller and lighter stock tires, just couldn't handle the bigger, heavier tires, and it was just about impossible to balance the tires.  At typical highway driving speeds the steering wheel would shake like it would if one 'threw' a wheel weight.  No amount of tire balancing seemed to help it.  I was just about at the point of selling the truck - which I loved, when I came across in an online forum a statement to the effect that when one moves to larger and heavier tires it is oftentimes necessary to upgrade the suspension to handle the modifications.  So I sunk another couple thousand dollars in having an off road suspension installed, and indeed that largely solved the problem.  Since I'm not a good mechanic I had a friend do it that has a car repair shop in my hometown. 

     Only issue was, new heavy duty upper control arms were installed which utilize polyurethane bushings which don't like conventional petroleum grease used in most vehicles.  And they need to be periodically greased.  And I never used a grease gun or greased a vehicle.  So for quite awhile I'd take the truck to the dealership for routine maintenance, and to my friend's shop so he could grease my upper control arms with the special 'synthetic grease' they wanted.  And I bought the special synthetic grease online.  It was all quite a hassle - not entirely unmanageable, but nonetheless a hassle.

     Then I moved a couple thousand miles West to Colorado and I didn't have my family friend to help me out on this.  Now this issue REALLY became a hassle.  I finally realized that I'm going to have to buy myself a grease gun and learn to do it myself.  So I buy the grease gun and dutifully follow the enclosed instructions on loading a grease gun cartridge.  A local shop owner shows me how to grease the zerk grease fittings, and somehow or other I manage it.  I actually managed it a few times, to my satisfaction - I guess I'm not too dumb!

     Finally though, I tried to grease them a couple months ago and I couldn't get one of the fittings to 'take' the grease.  After I'd pump grease in the fitting and remove the attachment to the grease fitting, it would all come pouring out, drip over everything, and make a mess.  I used up my whole cartridge of synthetic grease trying to grease this one stubborn grease fitting, and somehow or other had lost my original instructions that came with the grease gun on loading new grease cartridges.  And I'm so dumb on things like this I knew I didn't remember the somewhat involved steps of loading a fresh grease cartridge.  So what did I do?  I went out and bought another grease gun just so I'd have the instructions on loading cartridges!  That's how dumb I can be! 

     Turns out this time I bought a $40 grease gun instead of a cheap $25 one, and with the higher pressure it was capable of I managed to easily get that stubborn grease fitting to 'take' the grease.  So all's well that ends well!  But the reason I'm telling you all this story is that something that is ridiculously easy for one person doesn't necessarily mean it will be easy for someone else who doesn't know how to do something.  Like command line compiling. 

     While agonizing over why folks have a hard time with this the one issue that occurred to me was that in all my tutorials and writings on this topic I had always assumed my readers had a modicum of experience in working with a command line window, i.e., cmd.exe in Windows, and that they fully understood such issues as directory search PATHs, Change Directory Commands (ChDir or CD), environment variables, so on and so forth.  So in all my various postings, writings, etc., on this I always gave 'short shrift' to such issues, as I figured my readers were programmers, and fully understood such things.  I'd mention these issues in passing, but wouldn't dwell on them.  I think maybe that's where folks are having problems.  If so, perhaps I can help with that.

     So, what I'm going to do now is discuss Windows System commands one must understand in order to do command line building.  There really aren't many, they really aren't all that complicated, but if one doesn't know them and understand them, then one is pretty much 'dead in the water', ‘up the creek without a paddle’, 'sucking canal water', and maybe the reader can think of a few more phrases to describe an undesirable position to be in, in terms of command line building, same as me not being a good mechanic and not knowing how to do something so simple as greasing a grease fitting. 

     I'll start my exposition of this topic using my recent experience of installing the Embarcadero Dev-Cpp IDE/TDM-GCC compiler suite.....

https://www.embarcadero.com/free-tools/dev-cpp

     Dev-Cpp is the IDE component, TDM-GCC 9.2.0 is the open source GCC toolset, and Mingw the Windows specific implementation.  After that I'll discuss all this with reference to Microsoft's Visual Studio and Microsoft's Build Chain.

     When one downloads Embarcadero's Dev-Cpp development system, one is given the opportunity or choice to download only the IDE component, which, if one does only that, necessitates that the user will configure it through various complex visual interface elements to work with his or her's C/C++ compiler of choice.  I expect few do that, and for good reason - it is extremely difficult, if not impossible,  to get it all working with Dev-Cpp.  The other option, which I expect most users prefer, is to download a version which is automatically configured to work with some functioning C/C++ build system.  As mentioned above, that would be the TDM-GCC compiler suite.....

https://jmeubank.github.io/tdm-gcc/

     Now, the TDM-GCC compiler suite is a completely separate project from Embarcadero's Dev-Cpp project.  In the past I've downloaded and installed countless versions of TDM-GCC's compiler suite.  It doesn't include an IDE the assumption being that the user will obtain and configure an IDE for it him or herself, or simply do command line building, linking, etc.  That's always what I had done.  So here's where the plot thickens.  If one downloads and installs TDM-GCC, during the installation process the program asks the user whether or not he or she would like the PATH to the GCC based TDM installation added to the computer's PATH Environment Variables.  It's a simple 'Yes' or 'No' question.  I had always answered this question with a 'Yes' response because I knew I wanted to do only command line building, and it simplifies use of the command line tools greatly if the computer one is working on 'knows' how and where to find the various binaries that comprise the build chain.

     Some readers apparently may not know what a PATH is with reference to computers, or, for that matter, Environment Variables. Here is a link on Environment Variables....

https://www.computerhope.com/issues/ch000549.htm

     Now, when I had installed the Embarcadero Dev-Cpp package - which includes the TDM-GCC package, when I installed the thing it never asked me whether or not I wanted TDM-GCC added to my PATH Environment Variables.  There is nothing wrong with this, and it's entirely understandable, because the coders who built the Dev-Cpp package no doubt figured that the user would be using the Dev-Cpp IDE for building apps - not doing command line compiling.  But let's now take a detailed look at where this leaves us if we wish to do command line work in building apps and there is no PATH environment variable set. 

     On my systems where I've installed the Dev-Cpp development system I accepted the default installation path and the install program created this path...

C:\Program Files (x86)\Embarcadero\Dev-Cpp

     Under the above path it created a separate directory for the TDM-GCC-64 build system....

C:\Program Files (x86)\Embarcadero\Dev-Cpp\TDM-GCC-64

Obviously, there are various - actually very many, binary executable files in this or any build system which are needed at various times during the build process to create an executable or dll file.  These binaries, which actually constitute the 'build chain', are invariably located in a \bin subfolder under a compiler's main installation directory.  Continuing with my discussion of Dev-Cpp and TDM-GCC-64, it was located here on my systems....

C:\Program Files (x86)\Embarcadero\Dev-Cpp\TDM-GCC-64\bin

     Alright, armed with the above information now let's try to do some command line building with about the very simplest program imaginable - Dennis Ritchie's famous "Hello, World!" example...

Code: [Select]
// Hello.cpp
// g++ Hello.cpp -o "Hello.exe" -mconsole -m64 -s -Os
#include <stdio.h>

int main()
{
 printf("Hello, World!\n");
 getchar();

 return 0;
}

The 'magic' command line string that needs to be fed into a compiler build chain to build this program with GCC is as follows....

Code: [Select]
g++ Hello.cpp -o "Hello.exe" -mconsole -m64 -s -Os

     The first g++ term is shorthand for the compiler itself which is g++.exe.  The next term 'Hello.cpp' is the name of the source code file of the Hello, World! program.  As such, these first two terms are files which we are fervently hoping against all hope that Windows will be able to locate in some manner.  All the other remaining terms in the command line string are 'switches' which give the compiler information about how we want the compilation/linkage handled.  For example, -o "Hello.exe" tells the linker to create an executable file named Hello.exe. The -mconsole tells the build system to create a console mode program.  The -m64 switch tells it to build a 64 bit executable rather than a 32 bit one.  The –s switch tells the build system not to include debugging information in the executable.  The ‘s’ actually stands for ‘strip', i.e., strip debugging symbols from the binary.  This greatly reduces the size of the resulting executable.  Finally, the -Os switch tells it to optimize for small code size.

     Continuing, if I'm going to do command line compiling I need a command prompt window, and it just so happens I know the name of Windows command line processor, which is Cmd.exe, and I also just happen to know where it is located (at least one version, anyway), which is....

C:\Windows\System32\Cmd.exe

So I put a shortcut to Cmd.exe on my desktop and execute it, and low and behold I get a command prompt window opened to the current working directory, which, since I just executed Cmd.exe in
\Windows\System32, just happens to look like so ...

Code: [Select]
Microsoft Windows [Version 10.0.19042.1348]
(c) Microsoft Corporation. All rights reserved.

C:\Windows\System32>

     OK, so let's hold that and put it aside for a moment and deal with our "Hello, World!" code.  I presently have that in a directory like so on my computer....

Code: [Select]
C:\Code\VStudio\Hello

In that directory is this file as I've listed it above....

Code: [Select]
C:\Code\VStudio\Hello\Hello.cpp

So if we want to do command line building of Hello.cpp there are really two files involved which the operating system, i.e., Windows, must locate.  First, it must locate a compiler.  For GCC it just so happens to be g++.exe or c++.exe, which as stated above are in...

Code: [Select]
C:\Program Files (x86)\Embarcadero\Dev-Cpp\TDM-GCC-64\bin

And of course we have our Hello.cpp file in ...

Code: [Select]
C:\Code\VStudio\Hello\Hello.cpp

So now the big moment!  Let's try to build it!  We set focus to our command prompt window opened as above and either type or paste the command line string into the window so it now looks like so....

Code: [Select]
Microsoft Windows [Version 10.0.19042.1348]
(c) Microsoft Corporation. All rights reserved.

C:\Windows\System32>g++ Hello.cpp -o "Hello.exe" -mconsole -m64 -s -Os

Next, being of religious nature, I make the 'sign of the cross', say a little prayer, and hit the [ENTER] key and hope for the best!

     Uh, oh!  Just as I feared, it didn't work.  I must be dumb!  I knew it wouldn't work.  Nothin of mine ever works in this whole miserable C++ endeavor!  Is that what you are saying to yourself?  Well, let's delve into the details.  As you know with coding, the devil is always in the details!  Let's look at what Windows has provided to us in the way of the error message in our command line window....

Code: [Select]
C:\Windows\System32>g++ Hello.cpp -o "Hello.exe" -mconsole -m64 -s -Os
'g++' is not recognized as an internal or external command, operable program or batch file.

C:\Windows\System32>

It's saying "g++ is not recognized as an internal or external command, operable program or batch file."

That's really pretty plain English, and to elaborate, what the operating system did was start at the very beginning of the string and try to determine just what 'g++' is/was.  It would have first compared that string to commands it is familiar with such as the dir command to provide a directory listing, or the 'Change Directory' - ChDir or CD command to change from the current directory to another different one.  These are termed ‘internal system commands'.  It would not have found a match.  Next it would have searched the current directory, which is C:\Windows\System32, and all the additional directories listed in it's PATH Environment variables to try to locate g++.exe or maybe even g++.bat (a batch file).  It would have been unsuccessful on any of my systems to locate g++ because it is located neither in C:\Windows\System32, or in any of the other directories in my Environment Variables search Path.  On my current laptop here are my PATH variables, which I've copied out of my Environment Variables Dialog accessible from Control Panel....

Code: [Select]
C:\WINDOWS\system32;
C:\WINDOWS;
C:\WINDOWS\System32\Wbem;
C:\WINDOWS\System32\WindowsPowerShell\v1.0\;
C:\WINDOWS\System32\OpenSSH\;
C:\Program Files (x86)\Microsoft SQL Server\90\Tools\binn\;
C:\Users\Freddie\AppData\Local\Microsoft\WindowsApps;

If you examine the above list of paths you won't find 'C:\Program Files (x86)\Embarcadero\Dev-Cpp\TDM-GCC-64\bin;', which is where g++ is actually located, and the system didn't find it either in C:\Windows\System32.  I'm telling you all this to impress upon you that the system really tried hard to satisfy your command but finally had to give up. Just to put some hard numbers to this on my system
the \System32 folder is listed as comprising 6,062,063,616 bytes and 15,534 Files.  So at some point, after going through all this, the operating system just gave up and issued the above error message.  So what's the matter and how to fix it?  Well, it's because when Dev-Cpp was installed the PATH Environment Variable wasn't modified so as to include the PATH to the GCC build chain's \bin folder where g++ is located. 

     So there are actually at least two ways to fix this.  First, one could use my help link above to learn how to add the compiler's PATH to the system's Environment Table.  That would be a permanent fix.  Second, within one's command prompt window one could use the system's 'Set PATH=' command to temporarily add the desired path to the Environment Table.  We'll choose this later technique.  Here is what the correct command looks like....

Code: [Select]
Set PATH=C:\Program Files (x86)\Embarcadero\Dev-Cpp\TDM-GCC-64\bin;%PATH%;

So if I go ahead and execute that 'internal' system command into my command prompt window, then do like so to allow the system to list my currently modified path...

Code: [Select]
C:\Windows\System32>path [ENTER]

...I'll get something like so, modified only slightly by me to make it more readable....

Code: [Select]
PATH=
C:\Program Files (x86)\Embarcadero\Dev-Cpp\TDM-GCC-64\bin;
C:\WINDOWS\system32;
C:\WINDOWS;C:\WINDOWS\System32\Wbem;
C:\WINDOWS\System32\WindowsPowerShell\v1.0\;
C:\WINDOWS\System32\OpenSSH\;
C:\Program Files (x86)\Microsoft SQL Server\90\Tools\binn\;
C:\Users\Freddie\AppData\Local\Microsoft\WindowsApps;

As my patient reader can see by comparing the two listings above, the path to the TDM-GCC g++ compiler has been pre-pended to my system environment table.  If I now execute my Hello.cpp command line string, the system will hopefully, finally, be able to execute g++.exe and make my Hello.exe executable!  So let's give it a try!

Type it again at the Cmd.exe prompt and hit [ENTER]....

Code: [Select]
C:\Windows\System32>g++ Hello.cpp -o "Hello.exe" -mconsole -m64 -s -Os
g++: error: Hello.cpp: No such file or directory

C:\Windows\System32>


     Still not working!!!!  Now what's wrong!  Well, you may nor realize it, but we have made real, large, and genuine progress!  Our first error message we attended to above was generated by the Windows System itself.  It couldn't determine what or where g++ was.  This next error message, if you look carefully, wasn't generated by the Windows System.  It had actually found g++ and executed the GCC compiler!  Our PATH fix above worked perfectly! We should celebrate - not cry!  Our problem here though is that while g++ successfully executed, it was having the exact same problem Windows was having in trying to locate a specific file.  The file it (g++.exe) was looking for though was not g++ but rather Hello.cpp - our source code file.  The g++ compiler would have followed it's own search algorithm in attempting to find Hello.cpp, but it wouldn't have found it on my system.  It certainly wouldn't have found it in my C:\Windows\System32 folder - remember that's where we're presently at in terms of the current working directory, as I started Cmd.exe in the \System32 folder, and we never changed it to anywhere else.  So now how do we fix this?  Couldn't be much simpler!  Simply change the 'current working directory' to where our Hello.cpp file is located, and g++ will certainly find and read it.  But, you may ask, will the system be able to locate g++ if we do that?  Yes, that's no problem.  Since we added the location of g++ to our Environment Paths, the
system will be able to locate it no matter  what our current directory is changed to.  After all, g++ wasn't in my \System32 folder either.

     So how does one change a directory?  That's another system command one executes at the command prompt named ChDir or simply CD for 'Change Directory'.  Since my Hello.cpp file is in…

C:\Code\VStudio\Hello, we simply execute the following....

Code: [Select]
C:\Windows\System32>CD C:\Code\VStudio\Hello [ENTER]

Upon execution, I'm seeing this....

Code: [Select]
C:\Windows\System32>CD C:\Code\VStudio\Hello
C:\Code\VStudio\Hello>

Now we're in the directory where our Hello.cpp source code file is located.  To prove that simply execute another command prompt system command named dir as follows...

Code: [Select]

C:\Code\VStudio\Hello>dir
 Volume in drive C has no label.
 Volume Serial Number is 92DC-1CD1

 Directory of C:\Code\VStudio\Hello

11/15/2021  05:55 PM    <DIR>          .
11/15/2021  05:55 PM    <DIR>          ..
11/15/2021  05:57 PM                94 Hello.cpp
               1 File(s)             94 bytes
               2 Dir(s)  1,898,971,717,632 bytes free

C:\Code\VStudio\Hello>

As you can see from the output above, Hello.cpp is located in what is now the 'current directory'.  So our system will now find g++.exe because it is now temporarily in our system environment paths, and when g++ executes it will find Hello.cpp in the current directory.  So if I once more execute our command line string, followed by another dir command....

Code: [Select]
C:\Code\VStudio\Hello>g++ Hello.cpp -o "Hello.exe" -mconsole -m64 -s -Os

C:\Code\VStudio\Hello>dir
 Volume in drive C has no label.
 Volume Serial Number is 92DC-1CD1

 Directory of C:\Code\VStudio\Hello

11/15/2021  06:05 PM    <DIR>          .
11/15/2021  06:05 PM    <DIR>          ..
11/15/2021  05:57 PM                94 Hello.cpp
11/15/2021  06:05 PM            17,920 Hello.exe
               2 File(s)         18,014 bytes
               2 Dir(s)  1,898,975,178,752 bytes free

C:\Code\VStudio\Hello>

...you'll note we now have a 17,920 byte Hello.exe file.  Success!  If we execute Hello.exe we'll get our Hello, World! message.

     I've provided the above information using my experiences with Dev-Cpp and TDM-GCC-64 because of the somewhat difficult scenario it presents when one attempts to do command line building when the PATH environment variable isn't set in such a way that the system can find a compiler's build chain.

     Let me now present some information provided to me by expert PowerBASIC user, ObjReader Forum member, and creator of BcxAdp (Basic To C/C++ Translator, Application Development Platform), James Fuller...

https://sourceforge.net/projects/bcxadp/

     According to James, navigate to Cmd.exe in C:\Windows\System32 and right click on it to bring up the context sensitive menu where one can choose ...

Send To ....  Desktop (Create Shortcut)

     Then, when the shortcut is created, right click on the shortcut to bring up the context sensitive menu, and at the bottom select 'Properties'.  A tabbed dialog should open, with the 'Shortcut' tab selected, and the 'Target' edit control should be highlighted.  Using the paths I've described above to the TDM-GCC-64 build chain, and my Hello, World! program, James recommended this be pasted in the 'Target' edit control....

Code: [Select]
C:\Windows\System32\cmd.exe /K title Fred's Command Prompt && CD C:\Code\VStudio\Hello && path C:\Program Files (x86)\Embarcadero\Dev-Cpp\TDM-GCC-64\bin;%PATH%

     That will give you a nice shortcut that, when executed, will set up the command prompt window for you which will be able to execute the build chain from within your chosen project directory.  Obviously, you'll need to modify the above to where you've installed the TDM-GCC-64 build chain, and to wherever your project directory is located.

     I think the above information should help new users get command line compiling work.  It presents the basic concepts involved in command line compiling that are applicable with any build chain for any compiler.  I'd consider Dev-Cpp situation with TDM-GCC-64 perhaps a worse case scenario, as, working with Microsoft's Visual Studio Compiler Suite and build chain is quite a bit easier, as Microsoft provides shortcuts which when executed automatically configure the working environment so that everything just works when building from the command line, i.e., all the necessary paths are already set (except to your project directory).

     If you've installed Visual Studio (likely any version which includes the C/C++ build chain), you'll find these shortcuts on your Start Menu as sub-menu items under the main Visual Studio menu (not the shortcut to the IDE - choose the folder icon)...

x64 Native Tools Command Prompt for VS 2019
x64_x86 Cross Tools Command Prompt for VS 2019
x86 Native Tools Command Prompt for VS 2019
x86_x64 Cross Tools Command Prompt for VS 2019

Execute the top selection and a command prompt window will open to somewhere or other, on my system it opens to…

Code: [Select]
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community>

Change the directory to where a "Hello, World!" console program is located - on my system we're using the one in...

Code: [Select]
C:\Code\VStudio\Hello

So do like so if you have that directory structure too (or modify it to what you have been using if following along)...

Code: [Select]
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community>CD C:\Code\VStudio\Hello
C:\Code\VStudio\Hello>

Now type the following command line string to build Hello.cpp...

Code: [Select]
cl Hello.cpp

1)  C:\Code\VStudio\Hello>cl Hello.cpp
2)  Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30133 for x64
3)  Copyright (C) Microsoft Corporation.  All rights reserved.
4)
5)  Hello.cpp
6)  Microsoft (R) Incremental Linker Version 14.29.30133.0
7)  Copyright (C) Microsoft Corporation.  All rights reserved.
8)
9)  /out:Hello.exe
10) Hello.obj

C:\Code\VStudio\Hello>

     That successfully built Hello.exe.  How much simpler can it get???  I've numbered the lines I copied from my command line screen above.  On Line #1 I typed 'cl Hello.cpp' after the '>' prompt, then hit [ENTER].  Line #'s 2 and 3 are the outputs of the MSVC compiler, Version 19.29.  Line #4 is blank.  At Line #6 the linking phase begins with Version 14 of Microsoft's linker - link.exe.  At that point it will pull in code from the Microsoft C Runtime, and create the executable we want - Hello.exe.  Here is a 'dir' dump of my \Hello directory now with the new executable created...

Code: [Select]
C:\Code\VStudio\Hello>dir
 Volume in drive C has no label.
 Volume Serial Number is 92DC-1CD1

 Directory of C:\Code\VStudio\Hello

11/19/2021  07:48 AM    <DIR>          .
11/19/2021  07:48 AM    <DIR>          ..
11/15/2021  05:57 PM                94 Hello.cpp
11/19/2021  07:37 AM           127,488 Hello.exe
11/19/2021  07:37 AM             2,539 Hello.obj
               3 File(s)        130,121 bytes
               2 Dir(s)  1,896,401,100,800 bytes free

C:\Code\VStudio\Hello>

     As I said, how much simpler can it get?  You probably don't recall, but the Hello.exe file created by TDM was 17,920 bytes and here we have a 127,488 byte Hello.exe.  Hence justification for my TCLib.lib with which we can build it in 3 or 4 k!

     Now I'll finish up by elaborating somewhat on why things are/were so much easier with Microsoft's build system than with the TDM-GCC-64 build system I first started with.  With the above shortcut from the Windows 'Start Menu' with which we started our command line session...

x64 Native Tools Command Prompt for VS 2019

...if you right click on that and using the context sensitive pop-up menu you'll find a selection...

'Go To File Location...'

Execute that choice.  On my system this is where Windows Explorer opens up to....

Code: [Select]
C:\Program Data\Microsoft\Windows\Start Menu\Programs\Visual Studio 2019\Visual Studio Tools\VC

You'll see that shortcut file there so then right click on that shortcut file and another context sensitive menu pops up where you can choose 'Properties....'.  In the Properties sheet which pops up in the 'Target' edit control / text box I'm seeing this....

Code: [Select]
%comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"

What the %COMSPEC% term resolves to is the path to Cmd.exe which we used before, which is…

C:\Windows\System32

…on my system.  To verify that I'd simply type the following at the command prompt....

Code: [Select]
C:\Code\VStudio\Hello>echo %COMSPEC%
C:\WINDOWS\system32\cmd.exe

C:\Code\VStudio\Hello>

In terms of the '/k' switch, you can find information on that here....

https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb490880(v=technet.10)?redirectedfrom=MSDN#EBAA

...or search Microsoft docs for Cmd. The main thing about it however is a batch file is executed upon execution of that shortcut named vcvars64.bat.  If you follow the directory structure out to that file and open it in, for example, Notepad, you'll see it is only a line or two and it calls another batch file named vcvarsall.bat.  Now that's a big intimidating file.  Open it and take a look.  I'll tell you that I haven't a clue what any of that is about, no more than if it was written in Martian!  But it all executes for you and sets up your build environment for x64 compiling.  That's why it is so easy for you.  We didn't have to do the stuff we did to get the TDM compiler suite working.  Like I said, that was kind of a 'worst case' scenario.   I hope this information I've provided on Command Line Building helps you.

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1992
    • zapsolution
Re: Setting Up Command Line Building With Mingw And MS VC
« Reply #1 on: November 20, 2021, 10:38:41 am »
Fred,

PowerBASIC:
When compiling PB's code, i have never used the PB editor but UltraEdit32, and a batch command line, like this.

PBWIN905 /I.\;d:\Jose_WinAPI\WinAPI %1
Easy because of the limited set of options used by PB.
And I keep using Version 9.05, because i don't care of the extra DDT code, and the resulting code is much smaller than with PB 10 or PB 11 (private beta version)
Also I found that 9.05 was the most reliable version for a SDK coder. (There are still bugs in 10.04 and 11, never fixed since the passing of Zale).

Visual Studio 2019:
Is a totaly different animal to create a unique batch file, to compile from the command line

1 - C/C++ command line example
/permissive /ifcOutput "x64\Release\" /GS- /GL /W3 /Gy /Zc:wchar_t /Zi /Gm- /O1 /Fd"x64\Release\vc142.pdb" /Zc:inline /fp:precise /D "_UNICODE" /D "UNICODE" /D "_CRT_SECURE_NO_WARNINGS" /errorReport:prompt /GF /WX- /Zc:forScope /GR- /Gr /MT /FC /Fa"x64\Release\" /EHsc /nologo /Fo"x64\Release\" /Fp"x64\Release\FFcapture.pch" /diagnostics:column

2 - Linker option example
/OUT:"D:\VS2019\FFcapture\x64\Release\FFcapture.exe" /MANIFEST /LTCG /NXCOMPAT /PDB:"D:\VS2019\FFcapture\x64\Release\FFcapture.pdb" /DYNAMICBASE "Pathcch.lib" "Dwmapi.lib" "shlwapi.lib" "user32.lib" "kernel32.lib" "gdi32.lib" "shell32.lib" "ole32.lib" /MACHINE:X64 /OPT:REF /INCREMENTAL:NO /PGD:"D:\VS2019\FFcapture\x64\Release\FFcapture.pgd" /SUBSYSTEM:WINDOWS /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"x64\Release\FFcapture.exe.intermediate.manifest" /LTCGOUT:"x64\Release\FFcapture.iobj" /OPT:ICF /ERRORREPORT:PROMPT /ILK:"x64\Release\FFcapture.ilk" /NOLOGO /TLBID:1

For me to remember all these complex options that could change from one application to another, is something i just couldn't deal with.
This is the reason why i prefer to use the environment, and the facilities of the intellisense build-in editor, that is a real time saving for typo and bug detection.
« Last Edit: November 20, 2021, 11:42:52 am by Patrice Terrier »
Patrice
(Always working with the latest Windows version available...)

Frederick Harris

  • Newbie
  • *
  • Posts: 47
Re: Setting Up Command Line Building With Mingw And MS VC
« Reply #2 on: November 20, 2021, 02:38:50 pm »
I never heard of PB 11 (private beta version) Patrice.  Where did that come from?

I recall your mentioning UltraEdit before.  I guess I just never got around to checking that code editor out.  For C++ I've been using NotePad++ for quite awhile.  What I really, really like in code editors is a good, working project explorer, which correctly displays all my source code files and functions in a Tree View or whatever.   For PowerBASIC I loved the Lynx Project Explorer with Jose's SED editor.  Of course, Paul Nobles' Lynx became poorer and poorer with each successive PowerBASIC version.

The Visual Studio IDE is the only IDE I know of which has a fully functioning Project Explorer that works without bugs.  All the open source projects, to the best of my knowledge and experience, have bugs in their Project Explorers.  I'm assuming they must all be sharing the same defective code base.  Not long ago I downloaded and installed the latest Code::Blocks, and they took the Project Explorer out of it saying it crashes the app!  They call that progress!!!!!  The Notepad++ I use occasionally 'loses' functions.  They just disappear and won't show up in a project anymore.

Last winter I started writing my own code editor but I seem to have gotten sidetracked.  Don't know if I'll ever finish it.

In terms of PowerBASIC versions, I was surprised you mentioned version 9 as being your favorite version.  I had never thought of reverting to that version.  I will say though that I had one terrible time with Bob's last version of PB 10, i.e., 10.04.  I lost months with my ActiveX Grid Control trying to track down a problem - which I thought was in my code, but finally ended up being in 10.04.  I was only able to solve the problem by reverting to 10.03, which is the one I use.  I recall it was really miserable the way that problem developed for me.  I had been slowly and carefully coding my Grid Control as Bob was developing his version 10 code, i.e., 10.00, 10.01, 10.02, 10.03, etc.  Finally, after I had it pretty well finished I started getting crashes, and my assumption was I had missed some bug in my code, so I started going back through all my various versions of the grid to try to pin point where the bug showed up, in other words, which version worked perfectly with no crashes?  Very tedious!  By that time Bob had come out with his 10.04 version, and all my old versions were crashing using 10.04.  That's how I determined the bug wasn't mine, but in the 10.04 code of Bob's.  I seem to recall it manifested itself in Parse.  The problem disappeared when I reverted to 10.03.  So I never touched 10.04 after that bad experience.  It was the worst kind of bug - program crash at termination - ntdll.dll error.

I seem to recall there were quite a few additions to COM functionality with the version 10 series.  Not sure I could revert to V9.  Doesn't matter I guess.  I don't do any new coding in PowerBASIC anymore.  Breaks my heart, but I more or less gave up on it.  If only Bob would have put his last efforts into 64 bit rather than keeping on with the DDT stuff.  If he had done that his language would have lived on.  As it stands now that's doubtful.  I'm sure without doubt that the Drakes are good people and are trying, but in my opinion Bob was irreplacable.  If Bob had set up the compiler architecture for 64 bit, even without completely perfecting it all, I think others might have been able to perfect and extend it.  But my gut feeling is he never got that far.

Those command line strings the Visual Studio IDE creates are pretty scary!  I've never really looked at them in much detail; I just use my own, which are much simpler.   

What James Fuller does with his advanced batch file work amazes me.  I'm just not that advanced of a batch file creator to understand all the complex usages as found in vcvarsall.bat or in Jim's batch files.  I simply use the ones provided by Microsoft whose shortcuts are on the Start Menu under the Visual Studio installation.

Frederick Harris

  • Newbie
  • *
  • Posts: 47
Re: Setting Up Command Line Building With Mingw And MS VC
« Reply #3 on: November 20, 2021, 03:42:57 pm »
I promise this will be the last post I make on command line compiling (the following post).  Originally I put it on GitHub, and I understand at least one person couldn't follow it.  That's why I wrote the 1st post above.  This next one kind of picks off where I left off above.


Frederick Harris

  • Newbie
  • *
  • Posts: 47
More On Command Line Building With MS VC
« Reply #4 on: November 20, 2021, 03:45:06 pm »
Command Line Compiling With Visual Studio 2019 (also works with several versions back)

1) Do a usual left-click on Windows Start Menu and scroll down to Visual Studio 2019.  There should be
   two entries there (at least there are on my installations) for Visual Studio.  On mine, the top entry
   has a 'drop down' button which reveals several more options, and the bottom one is a single shortcut
   which starts the Visual Studio IDE (Integrated Development Environment);

2) Don't start the IDE.  Instead, click the top entry's drop down arrow to reveal the other shortcuts.
   Locate the one that says 'x64 Native Tools Command Prompt for VS 2019'.  Click that one to open a
   command prompt console window which also configures that environment for x64 builds by the x64 build
   chain, which involves cl.exe for compiling source files, i.e., *.cpp, *.h, and *.rc files, and link.exe
   for linking *.obj files created by the compiler.

Let's build Dennis Ritchie's (the creator of C and the Unix operating system) famous 'Hello, World!'
program from the command line so as to show how straightforward and ridiculously easy it is to build
programs from the command line.

First, open Microsoft's Notepad.exe.  We'll use the simplest text editor possible for this, and prove to you
that it's possible to code without heavyweight IDE's.  If you don't have a shortcut to Notepad on your desktop
(you should!!!), find it on the Windows Start Menu under the 'W's - Windows Accessories >> Notepad. 
Paste the following in Notepad....

Code: [Select]
// Hello.cpp
// cl Hello.cpp
#include <stdio.h>

int main()
{
 printf("Hello, World!\n");
 getchar();

 return 0;
}

...then save that file as text to some directory/folder on your box where you want to save source files, and
save it with an extension of *.cpp.  Might I recommend you create a separate folder for the file and name the
file Hello.cpp?  As an example of what I've done on my system as I write this, I've a directory off my root
C Drive named Code, i.e., C:\Code.  Under that I have a directory for my Microsoft Visual Studio code
named C:\Code\VStudio.  It is under that directory I made another project subdirectory named Hello which is
where I saved the above code as Hello.cpp, i.e.,...

C:\Code\VStudio\Hello\Hello.cpp

Now open a Visual Studio Command Prompt Window (or, maybe you have it open already if you followed
my steps 1 and 2 above) and use the CD (Change Directory) command to change the current or active
directory to where you've just saved your Hello.cpp file.  For example, when I opened a command prompt
window this is what I got...

**********************************************************************
** Visual Studio 2019 Developer Command Prompt v16.11.2
** Copyright (c) 2021 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community>

So you can see I'm in one of the Visual Studio installation directories.  We've got to move out of there to
where our Hello.cpp file is located.  The CD command will do that.  So to get us back to the root or C
directory type CD\ at the command prompt and hit [ENTER], i.e.,...

CD\ [ENTER]

Then you should see this....

**********************************************************************
** Visual Studio 2019 Developer Command Prompt v16.11.2
** Copyright (c) 2021 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community>CD\

C:\>

Next, we'll 'move out' to where our Hello.cpp file is located.  So do another CD command to get to the
directory where you put your Hello.cpp file.  For me it's like so....

C:\>CD C:\Code\VStudio\Hello

C:\Code\VStudio\Hello>

Let's do a cls to clear all that old gunk away...

C:\Code\VStudio\Hello>cls [ENTER]

...followed by a Dir command to see my Hello.cpp file as the only file there....

C:\Code\VStudio\Hello>Dir
 Volume in drive C has no label.
 Volume Serial Number is 92DC-1CD1

 Directory of C:\Code\VStudio\Hello

09/26/2021  11:34 AM    <DIR>          .
09/26/2021  11:34 AM    <DIR>          ..
09/26/2021  11:33 AM                96 Hello.cpp
               1 File(s)             96 bytes
               2 Dir(s)  1,895,221,362,688 bytes free

C:\Code\VStudio\Hello>

Now let's build Hello.cpp into Hello.exe.  At the command prompt simply type...

cl Hello.cpp [ENTER]

You don't type the [ENTER] part.  You just type 'cl Hello.cpp' (without the single quotes) then press the
[ENTER] key.  Here's what I see on my screen....

C:\Code\VStudio\Hello>cl Hello.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30133 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Hello.cpp
Microsoft (R) Incremental Linker Version 14.29.30133.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Hello.exe
Hello.obj

C:\Code\VStudio\Hello>

You should be able to see above the output from the compiler - which is cl.exe, and from the linker, which is
link.exe.  The compiler is version 19 and the linker is version 14.  There were no errors reported, and the
outputs of the compiler and linker are shown above as Hello.exe - which is the output of the linker, and
Hello.obj - which is the output file from the compiler.  If you clear your console window with the cls
command and do another dir command to see the new contents of the \Hello subdirectory you should see
something like so....

C:\Code\VStudio\Hello>dir
 Volume in drive C has no label.
 Volume Serial Number is 92DC-1CD1

 Directory of C:\Code\VStudio\Hello

09/26/2021  11:53 AM    <DIR>          .
09/26/2021  11:53 AM    <DIR>          ..
09/26/2021  11:33 AM                96 Hello.cpp
09/26/2021  11:53 AM           127,488 Hello.exe
09/26/2021  11:53 AM             2,539 Hello.obj
               3 File(s)        130,123 bytes
               2 Dir(s)  1,895,219,253,248 bytes free

C:\Code\VStudio\Hello>

As seen above we have a 96 byte Hello.cpp source file, a 127,488 byte executable, and a 2,539 byte
Hello.obj file.  We can run the Hello.exe file by simply typing it's name - with or without the extension, at
the command prompt....

C:\Code\VStudio\Hello>Hello
Hello, World!
 
Note that you need to hit the [ENTER] key to cause the program to end because it will be 'stuck' in the C
Runtime function call getchar() waiting patiently for a keystroke.  Note that if you are having any troubles at
this point, such as failing to get my Hello.cpp file to build without errors, then you've done something wrong
- perhaps not copying or typing my Hello.cpp file correctly, and you need to backup and follow my directions
EXACTLY!!!!  I'll assume from here on out that you are with me at this point.

Note that the size of the Hello.exe file of 127,488 bytes is atrocious.  In other code submissions and tutorials
I'll provide I'll show how we can cut that down to something more reasonable like 3 or 4 k. 

So let's review and get a deeper understanding of what has gone on so far.  When we opened the Visual Studio's
Command Prompt it was just like any command prompt window except that in the shortcut to it Microsoft caused a
batch file to run named vcvars64.bat which configured the environment for command line building using something
termed in C or C++ parlance the 'Build Chain'.  On my system it's located here (I think, maybe)....

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\bin\Hostx64\x64

It used to be very simple to find it, but the past few years it has gotten rather complicated.  But not to
worry!  It doesn't really matter where it is.  If you've followed my directions above, Hello.cpp will build
correctly as the system will locate everything correctly.  But it would be worth the reader's while to examine
some of the files in the \bin folder of the build chain to at least locate the ones I'm describing here,
specifically cl.exe and link.exe.

So let's get into this further.  At the top of Hello.cpp you'll see this line commented out....

// cl Hello.cpp

That line - minus the '//' comment characters, is what we fed into cl.exe - the MS VC++ compiler.  It is about
the simplest command line string possible that actually builds a binary file, i.e., executable.  In all my
programs, at the top of the main or startup module, I place these command line strings.  When I wish to build
the program I simply copy the command line string onto the clipboard, then paste it into the command
prompt window, then hit [ENTER].  There are other ways to do it for larger and more complex programs, and I'll
get to describing that in the fullness of time, but for now as we're just starting out, let's stick with this
technique.

Obviously, things can become more complicated, so let's look at some of the modifications we can make to the
above command line string.  First we'll look at things termed compiler and linker 'switches'.  At the command
prompt, clear your screen with cls, then do this....

C:\Code\VStudio\Hello>cl /?

In other words, just type 'cl /?' [ENTER]

Wow!  That had a big effect, didn't it?  That's how one gets help on all the 'switches' cl.exe recognizes.  Note
there is more info there than will fit on one console screen so you have to keep hitting [ENTER] to scroll
through all the data - maybe 6 or 8 screen-fulls.  Let's try some!

If you want, delete Hello.obj and Hello.exe from your \Hello directory, and try this for a new command line
string....

cl Hello.cpp /O1 /Os /FeHelloWorld.exe [ENTER]

In the above command line string the /O1, /Os, and /Fe terms tell the compiler to, respectively, optimize
maximally for code space, optimize for code space (somewhat redundant), and rename the output executable
file a different name than the source file.  In the case above we rename it 'HelloWorld.exe' instead of the
source code's Hello.exe.  If you execute the above and check out your \Hello folder you'll see we now have a
HelloWorld.exe.  On my system the optimizations had no effect.  Not much optimization
can be done to a simple program like that with only two function calls.

Let's try another instructive example before we move on to building GUI code.  Delete the Hello.obj and
HelloWorld.exe files from your \Hello directory and try this command line string....

cl Hello.cpp /c [ENTER]

Here is my command window after doing that and following it with a dir command to show the contents of
the \Hello folder...

C:\Code\VStudio\Hello>cl Hello.cpp /c
Microsoft (R) C/C++ Optimizing Compiler Version 19.25.28614 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Hello.cpp

C:\Code\VStudio\Hello>dir
 Volume in drive C is Windows
 Volume Serial Number is 3230-2F4C

 Directory of C:\Code\VStudio\Hello

09/26/2021  06:37 PM    <DIR>          .
09/26/2021  06:37 PM    <DIR>          ..
09/26/2021  11:33 AM                96 Hello.cpp
09/26/2021  06:37 PM             2,521 Hello.obj
               2 File(s)          2,617 bytes
               2 Dir(s)  848,256,962,560 bytes free

C:\Code\VStudio\Hello>

If you examine the above carefully you might note (if you are really observant) that no executable file was
created.  The only output file of the compiler was Hello.obj.  The /c switch told the compiler to only compile
the Hello.cpp source and output an object file only - ignoring the next or linking step.  A compiler only
knows about and examines one source code file at a time and it more or less converts it into binary or
machine code format, then outputs that file as seen above.  I qualified the former sentence with the term
'more or less' because an object file can't be run or executed directly, because it isn't quite 'done' yet; 'done'
in the sense of a cooking metaphor, like a steak that's too raw yet to eat, or a cake not done in the middle yet. 
For you see, virtually all programs pull in external code not found in any particular source code file.  Indeed,
that is the case in the above Hello.cpp file.  There are two calls to external routines which are not present in
the Hello.cpp source.  Those two calls are to a C Runtime function named printf() and another named
getchar().  The actual binary code for those two functions are contained in the C Runtime Library, and what
the compiler will do when converting the Hello.cpp code to binary is mark those function calls in such a way
that the linker in the next link step can pull that binary code out of the necessary library and incorporate it
into the final Hello.exe file. 

I failed to take note so far of the one include file in Hello.cpp.  That would be for #include <stdio.h>.  The
compiler was able to successfully build Hello.cpp into Hello.obj only because of the inclusion of that
include.  If that include weren't there, the compiler would issue an error and stop compiling.  So another way
of looking at the compilation process is to recognize that the compiler will compare each line in a program to
what it considers as correct C or C++ syntax, as the case may be, and to what it recognizes as a valid internal
language function call, such as sizeof() for example, or to the prototype of a correct external function call as
found in an include file.  I leave it as an exercise for the reader to attempt compilation of the above Hello.cpp
file after commenting out the #include <stdio>.  The compiler will 'error out', which is bad, but the good part
of it is that it will tell you in the error output exactly what it didn't like, and which line number in the
program was the subject of its ire.  This really is important, so maybe I ought to stop being lazy, do it myself,
and show you the output.  So I slash, slashed stdio.h and attempted to recompile and here is the output...

C:\Code\VStudio\Hello>cl Hello.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.25.28614 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Hello.cpp
Hello.cpp(5): error C3861: 'printf': identifier not found
Hello.cpp(6): error C3861: 'getchar': identifier not found

C:\Code\VStudio\Hello>

So you see, with stdio.h commented out, the compiler can not make any sense out of printf on line 5 and
getchar() on line 6.  These 'identifiers' are not symbols the compiler knows anything about.  It simply can't
continue and aborts with an error.  On the other hand, if printf() and getchar() functions were implemented in
Hello.cpp, the compiler would have been able to continue without any includes, and output binary opcodes
for everything, i.e., everything in main(), printf(), and getchar().  And the linker would have an easy job. 
Essentially, all it would need to do is output the executable almost exactly 'as is'.  So let's move on to the link
step.

If you have Hello.obj in your \Hello subfolder from our last invocation of cl.exe where we simply told it to
compile but not link Hello.cpp, then all you need to do to invoke the linker - Link.exe, is type....

...\Hello>Link Hello.obj

...at the command prompt, i.e., 'Link Hello.obj', and Link will create the executable from Hello.obj.  Here is
my console window showing that followed by a dir command to list the present contents of my
C:\Code\VStudio\Hello subdirectory...

C:\Code\VStudio\Hello>Link Hello.obj
Microsoft (R) Incremental Linker Version 14.25.28614.0
Copyright (C) Microsoft Corporation.  All rights reserved.

C:\Code\VStudio\Hello>dir
 Volume in drive C is Windows
 Volume Serial Number is 3230-2F4C

 Directory of C:\Code\VStudio\Hello

09/27/2021  01:35 AM    <DIR>          .
09/27/2021  01:35 AM    <DIR>          ..
09/26/2021  07:37 PM               127 Hello.cpp
09/27/2021  01:35 AM           126,464 Hello.exe
09/26/2021  06:37 PM             2,521 Hello.obj
               3 File(s)        129,112 bytes
               2 Dir(s)  848,167,505,920 bytes free

C:\Code\VStudio\Hello>

So, so far we've outlined and discussed two aspects or steps involved in producing an executable binary using
the C/C++ build system, which, as an astute reader can now see, is somewhat different from many other programming
languages, for example, the Basic family of languages.  I've broken it down into two steps, but it could actually
be conceptualized just as well into three steps, the first of which would then be the preprocessor stage.  Or the
preprocessor stage could be conceptualized as a sub-stage of my Step 1 above - the compilation stage.  What the
preprocessor does is actually read the includes into the main source file at the point of inclusion, and it
expands macros.

But let's move back to further elaboration on the link stage we've just observed.  I'm guessing it hasn't
occurred to my reader the question of how the linker knew where to find the code for the printf() and
getchar() functions?  These are library functions - the code for them is not contained in our Hello.cpp file,
and there were no command line switches after invoking Link.exe to tell the linker where to find that library
code.  The answer is that every vendor of a C or C++ compiler provides their own implementation of the C
or C++ Standard Library, and when they set up their build system or build chain the linker has a property
known as the 'Default Library' which is basically the default place it looks to bring in binary code when it
encounters a function call not found in the *.obj files with which it is working.  I don't want to fall off the
complexity cliff here and overwhelm my reader, but if in your studies of C and C++ so far you've been told
that main() and WinMain() are the 'entry point' functions for console and GUI (Graphical User Interface)
programs respectively, that isn't exactly the case.  The actual entry point functions are these....

wWinMainCRTStartup(void);
WinMainCRTStartup(void);
wmainCRTStartup();
mainCRTStartup();

...depending on whether it's a GUI or console mode program, and whether it's wide or single byte strings. 
And from within whichever of those functions is contained within the built binary, a call is made to main() or
WinMain() in the user's program.  But this only happens after the C or C++ Standard Library is loaded.  Note
the CRTStartup terms in all four of those functions.  So that's a brief but useful explanation to further cement
your understanding of the link stage of the build system.

Another important concept to realize, which has very real implications for you - the coder, is that the linker is
working only with binary *.obj files.  Strangely enough, it isn't reading in source *.cpp files.  Because it
doesn't know anything about the textural source files, if it encounters some problem in creating the executable
binary, it can't output an error message like the compiler can telling you the line number in the source where it
encountered the problem.  The most common example of this you'll encounter is a linker error termed 'unresolved
external'.  What that means - and using our Hello.cpp as an example, is that in attempting to create an executable
binary out of the various *.obj files it is looking at, it has encountered a function call which the compiler
passed on, i.e., it found a function prototype for a function in an include file, e.g., printf() or getchar() in
stdio.h, and it left it up to the linker to find the binary object code in some library so as to create the
executable.  But the linker has searched all the libraries the user provided in the command line string, and it
can't find the necessary code in any of them, including it's 'Default Library'.  Now, this won't happen in our
Hello.cpp example because printf() and getchar() are found in Microsoft's C Library, but I guarantee it will happen
to you my reader when you are building your own programs.  And when it happens to you for the first time I further
guarantee you will not be a happy camper.  Typically you'll have an abort of the linker with an error message
something to the effect that it encountered an 'unresolved external' and it will provide the name of the function
in pretty much mangled and nearly unrecognizable form for you to ponder over.  The name won't be exact because of
C++ name mangling which is necessary to support C++ function overloading, but you'll be able to get the picture
from the info it provides.  We'll see this and examine it all when we turn to creating GUI programs and building
them from the command line.  However, this is not an issue specific by any means to command line compilation - the
exact same issue exists when building code from within an IDE (Integrated Development Environment) like Visual
Studio, CodeBlocks, or Dev-C++.  In those environments though one must navigate through a lot of complex visual
interface elements, i.e., property sheets, dialogs, so on and so forth, to find the location of where the linker
settings are, so as to tell the linker the name of the library containing the missing 'unresolved externals' of
which it is complaining.

Oh!  One thing I failed to tell you - you can get a listing of linker command line switches, just like we did
with cl.exe, e.g., cl /?, by doing this at the command prompt...

link /?

So let's move on to building a Windows program with a graphical user interface, and we'll build it from the
command line. To start, allow me to describe my file setup, in the same way as I did with our Hello project
above.  My reader can do the same, or use his or her own file names, locations and directories.  First, I
created a directory under my C:\Code\VStudio folder named Forms, i.e.,

C:\Code\VStudio\Forms

...and next in my ...\Forms folder I created another directory named Form1, i.e.,...

C:\Code\VStudio\Forms\Form1

Next I changed my directory using the CD (ChrDir, i.e., Change Directory) command from the
C:\Code\VStudio\Hello directory where we were working with the Hello project, to the new ...\Form1
directory...

C:\Code\VStudio\Hello>CD\

C:\>CD C:\Code\VStudio\Forms\Form1

C:\Code\VStudio\Forms\Form1>

Below is my Form1.cpp file, which is about as stripped down and minimalistic of a Windows C++ SDK
program as it gets which still nonetheless registers a custom Window Class and utilizes a Window Procedure
to handle messages from the Windows operating system...

Code: [Select]
// Form1.cpp
// cl Form1.cpp user32.lib
#include <windows.h>

LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 if(msg==WM_DESTROY)
 {
    PostQuitMessage(0);
    return 0;
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 MSG messages;
 WNDCLASS wc;
 
 wc.lpszClassName = "Form1",   wc.lpfnWndProc   = fnWndProc;
 wc.hInstance     = hInstance, wc.style         = 0;
 wc.cbClsExtra    = 0,         wc.cbWndExtra    = 0;
 wc.hIcon         = NULL,      wc.hCursor       = NULL;
 wc.lpszMenuName  = NULL,      wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
 RegisterClass(&wc);
 CreateWindow("Form1","Form1",WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}

I will provide a tutorial on all this elsewhere for any of my readers who do not understand what is going on in
the above code, but for now my primary emphasis is on command line compilation and building, with an eye to
showing readers who only use an IDE how easy it is to do.  And it is indeed easy.  Just put the above code for
Form1.cpp in your directory and execute cl.exe as follows....

cl Form1.cpp user32.lib [ENTER]

Here is my console window after that....


C:\Code\VStudio\Hello>CD\

C:\>CD C:\Code\VStudio\Forms\Form1

C:\Code\VStudio\Forms\Form1>cl Form1.cpp user32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30133 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Form1.cpp
Microsoft (R) Incremental Linker Version 14.29.30133.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Form1.exe
Form1.obj
user32.lib

C:\Code\VStudio\Forms\Form1>

And that's it!  Couldn't be simpler.  If you examine the ..\Forms\Form1 directory you'll find Form1.obj
produced by the compiler cl.exe and Form1.exe produced by the linker Link.exe.  The Form1.exe file is
showing up as 92,160 bytes on my system.  You can double click on it from Windows Explorer to execute it,
or type Form1 at the console window and that will start it too.

Let's compare the command line compilation string for our Form1.cpp project with our earlier Hello project. 
For the Hello project we simply had....

cl Hello.cpp

...and for Form1.cpp we had....

cl Form1.cpp user32.lib

As you can see, there are two command line arguments to cl.exe there; 1) Form1.cpp for the compiler cl.exe, and
2) user32.lib for the linker, link.exe.  That could possibly be made clearer with this command line string,
which is a bit more verbose, but is entirely equivalent and produces the same result...

cl Form1.cpp /link user32.lib

The difference between our Hello.cpp file and project and our present GUI Form1 project is that the linker
needed to look elsewhere other than in the C or C++ Standard Libraries for 'external' function call resolution
in order to create the executable binary file - Form1.exe.  Specifically, the binary code for the external
function calls printf() and getchar() in Hello.cpp were located in the Default Library used by the build system,
and for that reason we did not have to list them in our Hello project command line compilation string.  In
Form1.cpp there are no C or C++ Standard Library function calls whatsoever.  However there ARE, if my counting is
correct, seven function calls to functions which are implemented in Microsoft's proprietary User32.lib, i.e., a
function library specific to Microsoft systems.  They are, starting from the top...

PostQuitMessage
DefWindowProc
RegisterClass
CreateWindow
GetMessage
TranslateMessage
DispatchMessage

That is why the user32.lib entry had to be added to our compilation string to create the Form1.exe file.  This
is important stuff for any C++ coder to understand, so I wish to make something of an interesting and perhaps
startling digression here to further cement these issues in the reader's mind.  Since as I stated above there are
no calls in Form1.cpp to C or C++ Standard Library functions, one might legitimately ask if the linker can be made
to not include those Default Libraries in the building of the executable binary?  The answer is 'Yes', and if the
reader were to do a 'Link /?' as I described somewhere above to dump all the linker switches, one might notice
this....

/NODEFAULTLIB[:library]

What that does is knock the C/C++ Standard Library as implemented by Microsoft out of the linker's search
path as it attempts to resolve 'external' function references.  Recall I stated that main() and WinMain()
weren't really the start up or entry point functions for a C or C++ program?  Well, what I want the reader to
do now is build this file named crt_win_a.cpp  into an object file, i.e., obj file using the command line string as
seen in the file below....

Code: [Select]
//========================================================================================
//                                      crt_win_a.cpp
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);

extern "C" void __cdecl WinMainCRTStartup(void)
{
 int iReturn = WinMain(GetModuleHandle(NULL),NULL,NULL,SW_SHOWDEFAULT);
 ExitProcess(iReturn);
}

Copy the above to a crt_win_a.cpp file and save it as text in the same directory with your Form1.cpp file, and
execute the command line string as seen above in the file at your command prompt, i.e.,...

cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN

...and you should obtain a crt_win_a.obj as a result, because, if you examine the command line string
carefully you'll find the '/c' argument we used above in our Hello project, which causes the compiler to only
produce the *.obj file without calling the linker.  It would be worthwhile for my reader to examine all those
arguments in cl's command line string to understand what they are doing.  Here's what I get when I compile
that file...

C:\Code\VStudio\Forms\Form1>cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30133 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

crt_win_a.cpp

C:\Code\VStudio\Forms\Form1>

Nothing too interesting there, is there?  But the important thing is it worked, for if you examine your
directory where you have Form1.cpp and crt_win_a.cpp you'll find your newly minted crt_win_a.obj file.

Next, rebuild Form1.cpp with this command line string....

cl Form1.cpp /O1 /Os /GS- /link crt_win_a.obj user32.lib

Here is the output in my console window from that operation....

C:\Code\VStudio\Forms\Form1>cl Form1.cpp /O1 /Os /GS- /link crt_win_a.obj user32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30133 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Form1.cpp
Microsoft (R) Incremental Linker Version 14.29.30133.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Form1.exe
crt_win_a.obj
user32.lib
Form1.obj

C:\Code\VStudio\Forms\Form1>

No errors, so it built with success.  Now for the shock!  Go to Windows Explorer and examine the Form1.exe
file just created.  Look at the size of the executable.  I'm seeing 3k instead of the 90k of the executable
created against a link with the C/C++ Standard Library.  Execute this file and you'll see it works exactly the
same as the Form1.exe file created first above using 'static' linkage to the LIBC.LIB or LIBCMT.LIB of the
C/C++ Standard Library.  And this is a statically linked executable requiring no run time dependencies to
work on any system to which it might be copied.  No necessity for distributing redistributable packages with
the executable or anything like that.

That crt_win_a.cpp file is part of my C Runtime Replacement for Microsoft's libraries, which I consider are
too bloated for me to work with.  It's my guess that if the reader followed my example above, that would
have been by far the smallest executable he or she ever produced.  But I thought this exercise might give the
interested reader some insight into the power available when creating C or C++ programs and doing
command line compilation.

Let's now turn to enhancing Form1.cpp by outputting to the window a 'Hello, World!' message.  This will
definitely enhance the reader's knowledge of the compilation/linkage process.  Let's keep the same directory,
but save this code as Form1a.cpp.  Note I added an 'a' character at the end of the 'Form1' string.....

Code: [Select]
// Form1a.cpp
// cl Form1a.cpp /O1 /Os /GS- /link crt_win_a.obj user32.lib
#include <windows.h>

LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
   case WM_PAINT:
     {
        PAINTSTRUCT ps;
        HDC hDC=BeginPaint(hwnd,&ps);
        TextOut(hDC,0,0,"Hello, World!",13);
        EndPaint(hwnd,&ps);
        return 0;
     }
   case WM_DESTROY:
     {
        PostQuitMessage(0);
        return 0;
     }
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 MSG messages;
 WNDCLASS wc;
 
 wc.lpszClassName = "Form1",   wc.lpfnWndProc   = fnWndProc;
 wc.hInstance     = hInstance, wc.style         = 0;
 wc.cbClsExtra    = 0,         wc.cbWndExtra    = 0;
 wc.hIcon         = NULL,      wc.hCursor       = NULL;
 wc.lpszMenuName  = NULL,      wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
 RegisterClass(&wc);
 CreateWindow("Form1","Form1",WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;


So you can see my compilation string above (I always put it there) which is....

cl Form1a.cpp /O1 /Os /GS- /link crt_win_a.obj user32.lib

So paste that into your console window to build the program.  Here is what I get....

C:\Code\VStudio\Forms\Form1>cl Form1a.cpp /O1 /Os /GS- /link crt_win_a.obj user32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30133 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Form1a.cpp
Microsoft (R) Incremental Linker Version 14.29.30133.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Form1a.exe
crt_win_a.obj
user32.lib
Form1a.obj
Form1a.obj : error LNK2019: unresolved external symbol __imp_TextOutA referenced in function "__int64
__cdecl
fnWndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)"
(?fnWndProc@@YA_JPEAUHWND__@@I_K_J@Z)
Form1a.exe : fatal error LNK1120: 1 unresolved externals

C:\Code\VStudio\Forms\Form1>

Uh Oh!  We've got a problem!  Didn't I tell you that you WOULD encounter this?  And that it wouldn't make
you happy?  That is one ugly error message and output screen, isn't it?  And like I said, there is no line
number in the error message telling you where in your source the error is located.  But if you understand the
compilation/linkage process, you understand that the linker can't tell you where in the source code the error
lies, because it isn't working with textural source but rather with binary object and library code.  What
happened above is the compiler 'read in' the contents of Windows.h header and found prototypes, i.e.,
function definitions, for all of the Windows Api functions which it found in the Form1a source above.  When
it generated object code in Form1a.obj it left 'markers', technically termed in compiler writer's parlance
'fixups', which it delegated to the linker to resolve.  Meaning, it was left up to the linker to find the binary
object or library code to pull into the executable it was tasked to create.  The compiler had done all it could. 
It found that the code in Form1a.cpp was syntactically correct in terms of C++ usage, and it found
syntactically correct function prototypes for all the external functions referenced in Form1a.cpp.  It created
all the object code it could, then passed on execution to the linker to find the missing object code for the
external functions.

So the linker is aware that in the CRT Start Up routine the Default Library was set to Kernel32.lib and LIBC
and LIBCMT were disallowed.  So the linker would have looked in kernel32.lib for binary code for
BeginPaint(), TextOut(), and EndPaint().  But it would not have found those functions there, as that essential
Windows library contains entirely non-GUI related functions, such as tasking, memory management,
threading, etc.  So the linker's other option would have been to look in the user32.lib provided in the
command line string for the above three functions.  It would have found BeginPaint() and EndPaint() there,
but not TextOut(). The reason it didn't find TextOut() there is because that functions IS NOT THERE!  It is
in another essential Windows Library named gdi32.lib!  You can easily prove that to yourself.  Simply bring
up TextOut in MSDN documentation....

https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-textouta

...and near the bottom of the page you'll see this....

Requirements
....
....
Library    Gdi32.lib

This pretty much suggests an easy fix to our problem, doesn't it?  Just add gdi32.lib at the end of our
compilation string, not?  So let's try again with this string....

cl Form1a.cpp /O1 /Os /GS- /link crt_win_a.obj user32.lib gdi32.lib

Here's my output again with this updated command line compilation string...
 
C:\Code\VStudio\Forms\Form1>cl Form1a.cpp /O1 /Os /GS- /link crt_win_a.obj user32.lib gdi32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30133 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Form1a.cpp
Microsoft (R) Incremental Linker Version 14.29.30133.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Form1a.exe
crt_win_a.obj
user32.lib
gdi32.lib
Form1a.obj

C:\Code\VStudio\Forms\Form1>

So we have success!
« Last Edit: November 20, 2021, 03:48:31 pm by Frederick Harris »

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1992
    • zapsolution
PowerBASIC 11
« Reply #5 on: November 20, 2021, 05:21:39 pm »
Quote
In terms of PowerBASIC versions, I was surprised you mentioned version 9 as being your favorite version.  I had never thought of reverting to that version.  I will say though that I had one terrible time with Bob's last version of PB 10, i.e., 10.04. 

Fred,

This was the ultimate version ever posted by Zale to the beta team members, on october 31, 2012.
and the latest build was #008



The memory errors started with the introduction of PowerArray.

« Last Edit: November 20, 2021, 05:38:37 pm by Patrice Terrier »
Patrice
(Always working with the latest Windows version available...)

Frederick Harris

  • Newbie
  • *
  • Posts: 47
Re: Setting Up Command Line Building With Mingw And MS VC
« Reply #6 on: November 20, 2021, 07:29:36 pm »
Hmmmm!  Very interesting Patrice.  I never knew about it.  And I thought I was on the beta team.  When PB 10 first came out I had even sent in some problems that needed to be fixed, and they were fixed pretty quick in the 10.01 release.  Maybe I said or did something that got me kicked off the team?  You know how Bob was - kind of thin skinned.  That file FIXWIN10 without an extension - what is that?  The v11 beta without exe extension???

Patrice Terrier

  • Administrator
  • *****
  • Posts: 1992
    • zapsolution
Re: Setting Up Command Line Building With Mingw And MS VC
« Reply #7 on: November 20, 2021, 09:21:59 pm »
Quote
That file FIXWIN10 without an extension - what is that?  The v11 beta without exe extension???
This is indeed a text file, about the 10.04 features, and the list of bugs that have been fixed at the end of the file.

Quote
You know how Bob was
Yes, and i had also several conflicts with him, especially about the time spent on DDT rather than working on 64-bit.
And this is the reason why i moved to C/C++ in 2010, after a short incursion in C# in 2005.
« Last Edit: November 21, 2021, 10:37:44 am by Patrice Terrier »
Patrice
(Always working with the latest Windows version available...)